diff --git a/configure/RULES.Db b/configure/RULES.Db index 479782b0e..f61d61c68 100644 --- a/configure/RULES.Db +++ b/configure/RULES.Db @@ -397,12 +397,12 @@ IOC_INST_TOP := $(firstword $(IOCS_APPL_TOP) \ %_registerRecordDeviceDriver.cpp: $(COMMON_DIR)/%.dbd @$(RM) $@ $*.tmp - $(REGISTERRECORDDEVICEDRIVER) $< $(basename $@) $(IOC_INST_TOP) > $*.tmp + $(REGISTERRECORDDEVICEDRIVER) $(DBDFLAGS) $< $(basename $@) $(IOC_INST_TOP) > $*.tmp $(MV) $*.tmp $@ %_registerRecordDeviceDriver.cpp: %.dbd @$(RM) $@ $*.tmp - $(REGISTERRECORDDEVICEDRIVER) $< $(basename $@) $(IOC_INST_TOP) > $*.tmp + $(REGISTERRECORDDEVICEDRIVER) $(DBDFLAGS) $< $(basename $@) $(IOC_INST_TOP) > $*.tmp $(MV) $*.tmp $@ .PRECIOUS: %_registerRecordDeviceDriver.cpp diff --git a/src/Makefile b/src/Makefile index 7d052650d..3307b8786 100644 --- a/src/Makefile +++ b/src/Makefile @@ -72,6 +72,10 @@ ioc/dbtemplate/test_DEPEND_DIRS = ioc DIRS += std std_DEPEND_DIRS = ioc libCom/RTEMS +DIRS += std/filters/test +std/filters/test_DEPEND_DIRS = std + + include $(TOP)/configure/RULES_DIRS diff --git a/src/ioc/as/asDbLib.c b/src/ioc/as/asDbLib.c index e087c2228..a12d1902c 100644 --- a/src/ioc/as/asDbLib.c +++ b/src/ioc/as/asDbLib.c @@ -6,7 +6,7 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are 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. \*************************************************************************/ /* Author: Marty Kraimer Date: 02-11-94*/ @@ -27,7 +27,7 @@ #include "caeventmask.h" #include "callback.h" #include "dbStaticLib.h" -#include "dbAddr.h" +#include "dbChannel.h" #include "dbAccess.h" #include "db_field_log.h" #include "dbEvent.h" @@ -40,7 +40,7 @@ static char *pacf=NULL; static char *psubstitutions=NULL; static epicsThreadId asInitTheadId=0; static int firstTime = TRUE; - + static long asDbAddRecords(void) { DBENTRY dbentry; @@ -98,7 +98,7 @@ int epicsShareAPI asSetSubstitutions(const char *substitutions) } return(0); } - + static void asSpcAsCallback(struct dbCommon *precord) { asChangeGroup(&precord->asp, precord->asg); @@ -109,7 +109,7 @@ static void asInitCommonOnce(void *arg) int *firstTime = (int *)arg; *firstTime = FALSE; } - + static long asInitCommon(void) { long status; @@ -117,7 +117,7 @@ static long asInitCommon(void) int wasFirstTime = firstTime; static epicsThreadOnceId asInitCommonOnceFlag = EPICS_THREAD_ONCE_INIT; - + epicsThreadOnce(&asInitCommonOnceFlag,asInitCommonOnce,(void *)&firstTime); if(wasFirstTime) { if(!pacf) return(0); /*access security will NEVER be turned on*/ @@ -148,7 +148,7 @@ int epicsShareAPI asInit(void) { return(asInitCommon()); } - + static void wdCallback(void *arg) { ASDBCALLBACK *pcallback = (ASDBCALLBACK *)arg; @@ -195,23 +195,15 @@ int epicsShareAPI asInitAsyn(ASDBCALLBACK *pcallback) } return(0); } - -int epicsShareAPI asDbGetAsl(void *paddress) -{ - DBADDR *paddr = paddress; - dbFldDes *pflddes; - pflddes = paddr->pfldDes; - return((int)pflddes->as_level); +int epicsShareAPI asDbGetAsl(struct dbChannel *chan) +{ + return dbChannelFldDes(chan)->as_level; } -void * epicsShareAPI asDbGetMemberPvt(void *paddress) +void * epicsShareAPI asDbGetMemberPvt(struct dbChannel *chan) { - DBADDR *paddr = paddress; - dbCommon *precord; - - precord = paddr->precord; - return((void *)precord->asp); + return dbChannelRecord(chan)->asp; } static void astacCallback(ASCLIENTPVT clientPvt,asClientStatus status) @@ -259,7 +251,7 @@ int epicsShareAPI astac(const char *pname,const char *user,const char *location) } return(0); } - + static void myMemberCallback(ASMEMBERPVT memPvt,FILE *fp) { dbCommon *precord; diff --git a/src/ioc/as/asDbLib.h b/src/ioc/as/asDbLib.h index f209a9028..b40a8d9df 100644 --- a/src/ioc/as/asDbLib.h +++ b/src/ioc/as/asDbLib.h @@ -5,12 +5,12 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are 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. \*************************************************************************/ /* share/epicsH/dbAsLib.h */ /* $Revision-Id$ */ /* Author: Marty Kraimer Date: 02-23-94*/ - + #ifndef INCdbAsLibh #define INCdbAsLibh @@ -22,6 +22,8 @@ typedef struct { long status; } ASDBCALLBACK; +struct dbChannel; + #ifdef __cplusplus extern "C" { #endif @@ -30,8 +32,8 @@ epicsShareFunc int epicsShareAPI asSetFilename(const char *acf); epicsShareFunc int epicsShareAPI asSetSubstitutions(const char *substitutions); epicsShareFunc int epicsShareAPI asInit(void); epicsShareFunc int epicsShareAPI asInitAsyn(ASDBCALLBACK *pcallback); -epicsShareFunc int epicsShareAPI asDbGetAsl( void *paddr); -epicsShareFunc void * epicsShareAPI asDbGetMemberPvt( void *paddr); +epicsShareFunc int epicsShareAPI asDbGetAsl(struct dbChannel *chan); +epicsShareFunc void * epicsShareAPI asDbGetMemberPvt(struct dbChannel *chan); epicsShareFunc int epicsShareAPI asdbdump(void); epicsShareFunc int epicsShareAPI asdbdumpFP(FILE *fp); epicsShareFunc int epicsShareAPI aspuag(const char *uagname); diff --git a/src/ioc/db/Makefile b/src/ioc/db/Makefile index 8bfec9434..cd015040e 100644 --- a/src/ioc/db/Makefile +++ b/src/ioc/db/Makefile @@ -17,9 +17,12 @@ INC += dbAccessDefs.h INC += dbCa.h INC += dbAddr.h INC += dbBkpt.h +INC += dbChannel.h INC += dbConvert.h INC += dbConvertFast.h +INC += dbExtractArray.h INC += dbEvent.h +INC += dbLink.h INC += dbLock.h INC += dbNotify.h INC += dbScan.h @@ -30,6 +33,8 @@ INC += db_field_log.h INC += initHooks.h INC += recGbl.h INC += dbIocRegister.h +INC += chfPlugin.h +INC += dbState.h INC += db_access_routines.h INC += db_convert.h @@ -50,11 +55,15 @@ menuGlobal_DBD += menuSimm.dbd DBDINC += $(basename $(menuGlobal_DBD)) DBDINC += dbCommon + dbCore_SRCS += dbLock.c dbCore_SRCS += dbAccess.c dbCore_SRCS += dbBkpt.c +dbCore_SRCS += dbChannel.c dbCore_SRCS += dbConvert.c dbCore_SRCS += dbFastLinkConv.c +dbCore_SRCS += dbExtractArray.c +dbCore_SRCS += dbLink.c dbCore_SRCS += dbNotify.c dbCore_SRCS += dbScan.c dbCore_SRCS += dbEvent.c @@ -74,4 +83,6 @@ dbCore_SRCS += dbPutNotifyBlocker.cpp dbCore_SRCS += dbContextReadNotifyCache.cpp dbCore_SRCS += templateInstances.cpp dbCore_SRCS += dbIocRegister.c +dbCore_SRCS += chfPlugin.c +dbCore_SRCS += dbState.c diff --git a/src/ioc/db/chfPlugin.c b/src/ioc/db/chfPlugin.c new file mode 100644 index 000000000..4516950e0 --- /dev/null +++ b/src/ioc/db/chfPlugin.c @@ -0,0 +1,649 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +/* Based on the linkoptions utility by Michael Davidsaver (BNL) */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "chfPlugin.h" + +#ifndef HUGE_VALF +# define HUGE_VALF HUGE_VAL +#endif +#ifndef HUGE_VALL +# define HUGE_VALL (-(HUGE_VAL)) +#endif + +/* + * Data for a chfPlugin + */ +typedef struct chfPlugin { + const chfPluginArgDef *opts; + size_t nopts; + epicsUInt32 *required; + const chfPluginIf *pif; +} chfPlugin; + +/* + * Parser state data for a chfFilter (chfPlugin instance) + */ +typedef struct chfFilter { + const chfPlugin *plugin; + epicsUInt32 *found; + void *puser; + epicsInt16 nextParam; +} chfFilter; + +/* Data types we get from the parser */ +typedef enum chfPluginType { + chfPluginTypeBool, + chfPluginTypeInt, + chfPluginTypeDouble, + chfPluginTypeString +} chfPluginType; + +/* + * Convert the (long) integer value 'val' to the type named in 'opt->optType' + * and store the result at 'user + opt->offset'. + */ +static int store_integer_value(const chfPluginArgDef *opt, void *user, long val) +{ + epicsInt32 *ival; + int *eval; + const chfPluginEnumType *emap; + double *dval; + char *sval; + char buff[22]; /* 2^64 = 1.8e+19, so 20 digits plus sign max */ + +/* printf("Got an integer for %s (type %d): %ld\n", opt->name, opt->optType, val); */ + + if (!opt->convert && opt->optType != chfPluginArgInt32) { + return -1; + } + + switch (opt->optType) { + case chfPluginArgInt32: + ival = (epicsInt32 *) ((char *)user + opt->offset); + *ival = val; + break; + case chfPluginArgBoolean: + eval = (int*) ((char*)user + opt->offset); + *eval = !!val; + break; + case chfPluginArgDouble: + dval = (double*) ((char*)user + opt->offset); + *dval = (double) val; + break; + case chfPluginArgString: + sval = ((char*)user + opt->offset); + sprintf(buff, "%ld", val); + if (strlen(buff) > opt->size-1) { + return -1; + } + strncpy(sval, buff, opt->size-1); + sval[opt->size-1]='\0'; + break; + case chfPluginArgEnum: + eval = (int*) ((char*)user + opt->offset); + for (emap = opt->enums; emap && emap->name; emap++) { + if (val == emap->value) { + *eval = val; + break; + } + } + if (!emap || !emap->name) { + return -1; + } + break; + case chfPluginArgInvalid: + return -1; + } + return 0; +} + +/* + * Convert the (int) boolean value 'val' to the type named in 'opt->optType' + * and store the result at 'user + opt->offset'. + */ +static int store_boolean_value(const chfPluginArgDef *opt, void *user, int val) +{ + epicsInt32 *ival; + int *eval; + double *dval; + char *sval; + +/* printf("Got a boolean for %s (type %d): %d\n", opt->name, opt->optType, val); */ + + if (!opt->convert && opt->optType != chfPluginArgBoolean) { + return -1; + } + + switch (opt->optType) { + case chfPluginArgInt32: + ival = (epicsInt32 *) ((char*)user + opt->offset); + *ival = val; + break; + case chfPluginArgBoolean: + eval = (int*) ((char*)user + opt->offset); + *eval = val; + break; + case chfPluginArgDouble: + dval = (double*) ((char*)user + opt->offset); + *dval = val ? 1. : 0.; + break; + case chfPluginArgString: + sval = ((char*)user + opt->offset); + if ((val ? 4 : 5) > opt->size-1) { + return -1; + } + strncpy(sval, (val ? "true" : "false"), opt->size-1); + sval[opt->size-1]='\0'; + break; + case chfPluginArgEnum: + case chfPluginArgInvalid: + return -1; + } + return 0; +} + +/* + * Convert the double value 'val' to the type named in 'opt->optType' + * and store the result at 'user + opt->offset'. + */ +static int store_double_value(const chfPluginArgDef *opt, void *user, double val) +{ + epicsInt32 *ival; + int *eval; + double *dval; + char *sval; + int i; + +/* + printf("Got a double for %s (type %d, convert: %s): %g\n", + opt->name, opt->optType, opt->convert?"yes":"no", val); +*/ + if (!opt->convert && opt->optType != chfPluginArgDouble) { + return -1; + } + + switch (opt->optType) { + case chfPluginArgInt32: +#if (LONG_MAX > 0x7fffffff) + if (val < -0x80000000L || val > 0x7fffffff) { + return -1; + } +#endif + ival = (epicsInt32 *) ((char*)user + opt->offset); + *ival = val; + break; + case chfPluginArgBoolean: + eval = (int*) ((char*)user + opt->offset); + *eval = (val != 0.) ? 1 : 0; + break; + case chfPluginArgDouble: + dval = (double*) ((char*)user + opt->offset); + *dval = val; + break; + case chfPluginArgString: + sval = ((char*)user + opt->offset); + if (opt->size <= 8) { /* Play it safe: 3 exp + 2 sign + 'e' + '.' */ + return -1; + } + i = epicsSnprintf(sval, opt->size, "%.*g", (int) opt->size - 7, val); + if (i >= opt->size) { + return -1; + } + break; + case chfPluginArgEnum: + case chfPluginArgInvalid: + return -1; + } + return 0; +} + +/* + * Convert the (char*) string value 'val' to the type named in 'opt->optType' + * and store the result at 'user + opt->offset'. + */ +static int store_string_value(const chfPluginArgDef *opt, void *user, const char *val, size_t len) +{ + epicsInt32 *ival; + long lval; + int *eval; + const chfPluginEnumType *emap; + double *dval; + char *sval; + char *end; + int i; + +/* printf("Got a string for %s (type %d): %.*s\n", opt->name, opt->optType, len, val); */ + + if (!opt->convert && opt->optType != chfPluginArgString && opt->optType != chfPluginArgEnum) { + return -1; + } + + switch (opt->optType) { + case chfPluginArgInt32: + lval = strtol(val, &end, 0); + /* test for the myriad error conditions which strtol may use */ + if (lval == LONG_MAX || lval == LONG_MIN +#if (LONG_MAX > 0x7fffffff) + || lval < -0x80000000L || lval > 0x7fffffff +#endif + || end == val) { + return -1; + } + ival = (epicsInt32 *) ((char*)user + opt->offset); + *ival = lval; + break; + case chfPluginArgBoolean: + eval = (int*) ((char*)user + opt->offset); + if (epicsStrnCaseCmp(val, "true", len) == 0) { + *eval = 1; + } else if (epicsStrnCaseCmp(val, "false", len) == 0) { + *eval = 0; + } else { + i = strtol(val, &end, 0); + if (i > INT_MAX || i < INT_MIN || end == val) { + return -1; + } + *eval = !!i; + } + break; + case chfPluginArgDouble: + dval = (double*) ((char*)user + opt->offset); + *dval = strtod(val, &end); + /* Indicates errors in the same manner as strtol */ + if (*dval==HUGE_VALF||*dval==HUGE_VALL||end==val ) + { + return -1; + } + break; + case chfPluginArgString: + i = opt->size-1 < len ? opt->size-1 : len; + sval = ((char*)user + opt->offset); + strncpy(sval, val, i); + sval[i] = '\0'; + break; + case chfPluginArgEnum: + eval = (int*) ((char*)user + opt->offset); + for (emap = opt->enums; emap && emap->name; emap++) { + if (strncmp(emap->name, val, len) == 0) { + *eval = emap->value; + break; + } + } + if( !emap || !emap->name ) { + return -1; + } + break; + case chfPluginArgInvalid: + return -1; + } + return 0; +} + +static void freeInstanceData(chfFilter *f) +{ + free(f->found); /* FIXME: Use a free-list */ + free(f); /* FIXME: Use a free-list */ +} + +/* + * chFilterIf callbacks + */ + +/* First entry point when a new filter instance is created. + * All per-instance allocations happen here. + */ +static parse_result parse_start(chFilter *filter) +{ + chfPlugin *p = (chfPlugin*) filter->plug->puser; + chfFilter *f; + + /* Filter context */ + /* FIXME: Use a free-list */ + f = calloc(1, sizeof(chfFilter)); + if (!f) { + fprintf(stderr,"chfFilterCtx calloc failed\n"); + goto errfctx; + } + f->nextParam = -1; + + /* Bit array to find missing required keys */ + /* FIXME: Use a free-list */ + f->found = calloc( (p->nopts/32)+1, sizeof(epicsUInt32) ); + if (!f->found) { + fprintf(stderr,"chfConfigParseStart: bit array calloc failed\n"); + goto errbitarray; + } + + /* Call the plugin to allocate its structure, it returns NULL on error */ + if (p->pif->allocPvt) { + if ((f->puser = p->pif->allocPvt()) == NULL) + goto errplugin; + } + + filter->puser = (void*) f; + + return parse_continue; + + errplugin: + free(f->found); /* FIXME: Use a free-list */ + errbitarray: + free(f); /* FIXME: Use a free-list */ + errfctx: + return parse_stop; +} + +static void parse_abort(chFilter *filter) { + chfPlugin *p = (chfPlugin*) filter->plug->puser; + chfFilter *f = (chfFilter*) filter->puser; + + /* Call the plugin to tell it we're aborting */ + if (p->pif->parse_error) p->pif->parse_error(f->puser); + if (p->pif->freePvt) p->pif->freePvt(f->puser); + freeInstanceData(f); +} + +static parse_result parse_end(chFilter *filter) +{ + chfPlugin *p = (chfPlugin*) filter->plug->puser; + chfFilter *f = (chfFilter*) filter->puser; + int i; + + /* Check if all required arguments were supplied */ + for(i = 0; i < (p->nopts/32)+1; i++) { + if ((f->found[i] & p->required[i]) != p->required[i]) { + if (p->pif->parse_error) p->pif->parse_error(f->puser); + if (p->pif->freePvt) p->pif->freePvt(f->puser); + freeInstanceData(f); + return parse_stop; + } + } + + /* Call the plugin to tell it we're done */ + if (p->pif->parse_ok) { + if (p->pif->parse_ok(f->puser)) { + if (p->pif->freePvt) p->pif->freePvt(f->puser); + freeInstanceData(f); + return parse_stop; + } + } + + return parse_continue; +} + +static parse_result parse_boolean(chFilter *filter, int boolVal) +{ + const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; + chfFilter *f = (chfFilter*)filter->puser; + + if (f->nextParam < 0 || store_boolean_value(&opts[f->nextParam], f->puser, boolVal)) { + return parse_stop; + } else { + return parse_continue; + } +} + +static parse_result parse_integer(chFilter *filter, long integerVal) +{ + const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; + chfFilter *f = (chfFilter*)filter->puser; + + if (f->nextParam < 0 || store_integer_value(&opts[f->nextParam], f->puser, integerVal)) { + return parse_stop; + } else { + return parse_continue; + } +} + +static parse_result parse_double(chFilter *filter, double doubleVal) +{ + const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; + chfFilter *f = (chfFilter*)filter->puser; + + if (f->nextParam < 0 || store_double_value(&opts[f->nextParam], f->puser, doubleVal)) { + return parse_stop; + } else { + return parse_continue; + } +} + +static parse_result parse_string(chFilter *filter, const char *stringVal, size_t stringLen) +{ + const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; + chfFilter *f = (chfFilter*)filter->puser; + + if (f->nextParam < 0 || store_string_value(&opts[f->nextParam], f->puser, stringVal, stringLen)) { + return parse_stop; + } else { + return parse_continue; + } +} + +static parse_result parse_start_map(chFilter *filter) +{ + return parse_continue; +} + +static parse_result parse_map_key(chFilter *filter, const char *key, size_t stringLen) +{ + const chfPluginArgDef *cur; + const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; + chfFilter *f = (chfFilter*)filter->puser; + int i; + + f->nextParam = -1; + for(cur = opts, i = 0; cur && cur->name; cur++, i++) { + if (strncmp(key, cur->name, stringLen) == 0) { + f->nextParam = i; + break; + } + } + if (f->nextParam == -1) { + return parse_stop; + } + + f->found[i/32] |= 1<<(i%32); + return parse_continue; +} + +static parse_result parse_end_map(chFilter *filter) +{ + return parse_continue; +} + +static long channel_open(chFilter *filter) +{ + chfPlugin *p = (chfPlugin*) filter->plug->puser; + chfFilter *f = (chfFilter*) filter->puser; + + if (p->pif->channel_open) return p->pif->channel_open(filter->chan, f->puser); + else return 0; +} + +static void channel_register_pre(chFilter *filter, + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) +{ + chfPlugin *p = (chfPlugin*) filter->plug->puser; + chfFilter *f = (chfFilter*) filter->puser; + + if (p->pif->channelRegisterPre) + p->pif->channelRegisterPre(filter->chan, f->puser, cb_out, arg_out, probe); +} + +static void channel_register_post(chFilter *filter, + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) +{ + chfPlugin *p = (chfPlugin*) filter->plug->puser; + chfFilter *f = (chfFilter*) filter->puser; + + if (p->pif->channelRegisterPost) + p->pif->channelRegisterPost(filter->chan, f->puser, cb_out, arg_out, probe); +} + +static void channel_report(chFilter *filter, int level, const unsigned short indent) +{ + chfPlugin *p = (chfPlugin*) filter->plug->puser; + chfFilter *f = (chfFilter*) filter->puser; + + if (p->pif->channel_report) + p->pif->channel_report(filter->chan, f->puser, level, indent); +} + +static void channel_close(chFilter *filter) +{ + chfPlugin *p = (chfPlugin*) filter->plug->puser; + chfFilter *f = (chfFilter*) filter->puser; + + if (p->pif->channel_close) p->pif->channel_close(filter->chan, f->puser); + if (p->pif->freePvt) p->pif->freePvt(f->puser); + free(f->found); /* FIXME: Use a free-list */ + free(f); /* FIXME: Use a free-list */ +} + +/* + * chFilterIf for the wrapper + * we just support a simple one-level map, and no arrays + */ +static chFilterIf wrapper_fif = { + parse_start, + parse_abort, + parse_end, + + NULL, /* parse_null, */ + parse_boolean, + parse_integer, + parse_double, + parse_string, + + parse_start_map, + parse_map_key, + parse_end_map, + + NULL, /* parse_start_array, */ + NULL, /* parse_end_array, */ + + channel_open, + channel_register_pre, + channel_register_post, + channel_report, + channel_close +}; + +const char* chfPluginEnumString(const chfPluginEnumType *emap, int i, const char* def) +{ + for(; emap && emap->name; emap++) { + if ( i == emap->value ) { + return emap->name; + } + } + return def; +} + +int chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef* opts) +{ + chfPlugin *p; + size_t i; + const chfPluginArgDef *cur; + epicsUInt32 *reqd; + + /* Check and count options */ + for (i = 0, cur = opts; cur && cur->name; i++, cur++) { + switch(cur->optType) { + case chfPluginArgInt32: + if (cur->size < sizeof(epicsInt32)) { + errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for epicsInt32 (%lu)\n", + key, cur->size, cur->name, + (unsigned long) sizeof(epicsInt32)); + return -1; + } + break; + case chfPluginArgBoolean: + if (cur->size < 1) { + errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for boolean (%lu)\n", + key, cur->size, cur->name, + (unsigned long) sizeof(char)); + return -1; + } + break; + case chfPluginArgDouble: + if (cur->size < sizeof(double)) { + errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for double (%lu)\n", + key, cur->size, cur->name, + (unsigned long) sizeof(double)); + return -1; + } + break; + case chfPluginArgString: + if (cur->size < sizeof(char*)) { + /* Catch if someone has given us a char* instead of a char[] + * Also means that char buffers must be >=4. + */ + errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for string (>= %lu)\n", + key, cur->size, cur->name, + (unsigned long) sizeof(char*)); + return -1; + } + break; + case chfPluginArgEnum: + if (cur->size < sizeof(int)) { + errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for enum (%lu)\n", + key, cur->size, cur->name, + (unsigned long) sizeof(int)); + return -1; + } + break; + case chfPluginArgInvalid: + errlogPrintf("Plugin %s: storage type for %s is not defined\n", + key, cur->name); + return -1; + break; + } + } + + /* Bit array used to find missing required keys */ + reqd = dbCalloc((i/32)+1, sizeof(epicsUInt32)); + if (!reqd) { + fprintf(stderr,"Plugin %s: bit array calloc failed\n", key); + return -1; + } + + for (i = 0, cur = opts; cur && cur->name; i++, cur++) { + if (cur->required) reqd[i/32] |= 1 << (i%32); + } + + /* Plugin data */ + p = dbCalloc(1, sizeof(chfPlugin)); + p->pif = pif; + p->opts = opts; + p->nopts = i; + p->required = reqd; + + dbRegisterFilter(key, &wrapper_fif, p); + + return 0; +} diff --git a/src/ioc/db/chfPlugin.h b/src/ioc/db/chfPlugin.h new file mode 100644 index 000000000..7d9745d10 --- /dev/null +++ b/src/ioc/db/chfPlugin.h @@ -0,0 +1,280 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +/* Based on the linkoptions utility by Michael Davidsaver (BNL) */ + +#ifndef CHFPLUGIN_H +#define CHFPLUGIN_H + +#include +#include +#include +#include + +struct db_field_log; + +/** @file chfPlugin.h + * @brief Channel filter simplified plugins. + * + * Utility layer to allow an easier (reduced) interface for + * channel filter plugins. + * + * Parsing the configuration arguments of a channel filter plugin + * is done according to an argument description table provided by the plugin. + * The parser stores the results directly into a user supplied structure + * after appropriate type conversion. + * + * To specify the arguments, a chfPluginArgDef table must be defined + * for the user structure. This table has to be specified when the plugin registers. + * + * The plugin is responsible to register an init function using + * epicsExportRegistrar() and the accompanying registrar() directive in the dbd, + * and call chfPluginRegister() from within the init function. + * + * For example: + * + * typedef struct myStruct { + * ... other stuff + * epicsUInt32 ival; + * double dval; + * epicsUInt32 ival2; + * int enumval; + * char strval[20]; + * } myStruct; + * + * static const + * chfPluginEnumType colorEnum[] = { {"Red",1}, {"Green",2}, {"Blue",3}, {NULL,0} }; + * + * static const + * chfPluginDef myStructDef[] = { + * chfInt32 (myStruct, ival, "Integer" , 0, 0), + * chfInt32 (myStruct, ival2, "Second" , 1, 0), + * chfDouble(myStruct, dval, "Double" , 1, 0), + * chfString(myStruct, strval , "String" , 1, 0), + * chfEnum (myStruct, enumval, "Color" , 1, 0, colorEnum), + * chfPluginEnd + * }; + * + * Note: The 4th argument specifies the parameter to be required (1) or optional (0), + * the 5th whether converting to the required type is allowed (1), or + * type mismatches are an error (0). + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Channel filter simplified plugin interface. + * + * The routines in this structure must be implemented by each filter plugin. + */ +typedef struct chfPluginIf { + + /* Memory management */ + /** @brief Allocate private resources. + * + * Called before parsing starts. + * The plugin should allocate its per-instance structures, + * returning a pointer to them or NULL requesting an abort of the operation. + * + * allocPvt may be set to NULL, if no resource allocation is needed. + * + * @return Pointer to private structure, NULL if operation is to be aborted. + */ + void * (* allocPvt) (void); + + /** @brief Free private resources. + * + * Called as part of abort or shutdown. + * The plugin should release any resources allocated for this filter; + * no further calls through this interface will be made. + * + * freePvt may be set to NULL, if no resources need to be released. + * + * @param pvt Pointer to private structure. + */ + void (* freePvt) (void *pvt); + + /* Parameter parsing results */ + /** @brief A parsing error occurred. + * + * Called after parsing failed with an error. + * + * @param pvt Pointer to private structure. + */ + void (* parse_error) (void *pvt); + + /** @brief Configuration has been parsed successfully. + * + * Called after parsing has finished ok. + * The plugin may check the validity of the parsed data, + * returning -1 to request an abort of the operation. + * + * @param pvt Pointer to private structure. + * @return 0 for success, -1 if operation is to be aborted. + */ + int (* parse_ok) (void *pvt); + + /* Channel operations */ + /** @brief Open channel. + * + * Called as part of the channel connection setup. + * + * @param chan dbChannel for which the connection is being made. + * @param pvt Pointer to private structure. + * @return 0 for success, -1 if operation is to be aborted. + */ + long (* channel_open) (dbChannel *chan, void *pvt); + + /** @brief Register callbacks for pre-event-queue operation. + * + * Called as part of the channel connection setup. + * + * This function is called to establish the stack of plugins that an event + * is passed through between the database and the event queue. + * + * The plugin must set pe_out to point to its own post-event callback in order + * to be called when a data update is sent from the database towards the + * event queue. + * + * The plugin may find out the type of data it will receive by looking at 'probe'. + * If the plugin will change the data type and/or size, it must update 'probe' + * accordingly. + * + * @param chan dbChannel for which the connection is being made. + * @param pvt Pointer to private structure. + * @param cb_out Pointer to this plugin's post-event callback (NULL to bypass + * this plugin). + * @param arg_out Argument that must be supplied when calling + * this plugin's post-event callback. + */ + void (* channelRegisterPre) (dbChannel *chan, void *pvt, + chPostEventFunc **cb_out, void **arg_out, + db_field_log *probe); + + /** @brief Register callbacks for post-event-queue operation. + * + * Called as part of the channel connection setup. + * + * This function is called to establish the stack of plugins that an event + * is passed through between the event queue and the final user (CA server or + * database access). + * + * The plugin must set pe_out to point to its own post-event callback in order + * to be called when a data update is sent from the event queue towards the + * final user. + * + * The plugin may find out the type of data it will receive by looking at 'probe'. + * If the plugin will change the data type and/or size, it must update 'probe' + * accordingly. + * + * @param chan dbChannel for which the connection is being made. + * @param pvt Pointer to private structure. + * @param cb_out Pointer to this plugin's post-event callback (NULL to bypass + * this plugin). + * @param arg_out Argument that must be supplied when calling + * this plugin's post-event callback. + */ + void (* channelRegisterPost) (dbChannel *chan, void *pvt, + chPostEventFunc **cb_out, void **arg_out, + db_field_log *probe); + + /** @brief Channel report request. + * + * Called as part of show... routines. + * + * @param chan dbChannel for which the report is requested. + * @param pvt Pointer to private structure. + * @param level Interest level. + * @param indent Number of spaces to print before each output line. + */ + void (* channel_report) (dbChannel *chan, void *pvt, int level, const unsigned short indent); + + /* FIXME: More filter routines here ... */ + + /** @brief Channel close request. + * + * Called as part of connection shutdown. + * @param chan dbChannel for which the connection is being shut down. + * @param pvt Pointer to private structure. + */ + void (* channel_close) (dbChannel *chan, void *pvt); + +} chfPluginIf; + +typedef enum chfPluginArg { + chfPluginArgInvalid=0, + chfPluginArgBoolean, + chfPluginArgInt32, + chfPluginArgDouble, + chfPluginArgString, + chfPluginArgEnum +} chfPluginArg; + +typedef struct chfPluginEnumType { + const char *name; + const int value; +} chfPluginEnumType; + +typedef struct chfPluginArgDef { + const char * name; + chfPluginArg optType; + unsigned int required:1; + unsigned int convert:1; + epicsUInt32 offset; + epicsUInt32 size; + const chfPluginEnumType *enums; +} chfPluginArgDef; + +#define chfInt32(Struct, Member, Name, Req, Conv) \ +{Name, chfPluginArgInt32, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} + +#define chfBoolean(Struct, Member, Name, Req, Conv) \ +{Name, chfPluginArgBoolean, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} + +#define chfDouble(Struct, Member, Name, Req, Conv) \ +{Name, chfPluginArgDouble, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} + +#define chfString(Struct, Member, Name, Req, Conv) \ +{Name, chfPluginArgString, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} + +#define chfEnum(Struct, Member, Name, Req, Conv, Enums) \ +{Name, chfPluginArgEnum, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), Enums} + +#define chfPluginArgEnd {0} + +/* Extra output when parsing and converting */ +#define CHFPLUGINDEBUG 1 + +/** @brief Return the string associated with Enum index 'i'. + * + * @param Enums A null-terminated array of string/integer pairs. + * @param i An Enum index. + * @param def String to be returned when 'i' isn't a valid Enum index. + * @return The string associated with 'i'. + */ +epicsShareFunc const char* chfPluginEnumString(const chfPluginEnumType *Enums, int i, const char* def); + +/** @brief Register a plugin. + * + * @param key The plugin name key that clients will use. + * @param pif Pointer to the plugin's interface. + * @param opts Pointer to the configuration argument description table. + */ +epicsShareFunc int chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef* opts); + +#ifdef __cplusplus +} +#endif + +#endif // CHFPLUGIN_H diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index d1ea856ec..d40205f4a 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -1,21 +1,22 @@ /*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. * Copyright (c) 2002 The University of Chicago, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ -/* dbAccess.c */ -/* $Revision-Id$ */ + /* * Original Author: Bob Dalesio * Current Author: Marty Kraimer - * Date: 11-7-90 -*/ + * Andrew Johnson + * Ralph Lange + */ - #include #include #include @@ -46,6 +47,7 @@ #include "dbAddr.h" #include "callback.h" #include "dbScan.h" +#include "dbLink.h" #include "dbLock.h" #include "dbEvent.h" #include "dbConvert.h" @@ -89,17 +91,6 @@ static short mapDBFToDBR[DBF_NTYPES] = { /* The following is to handle SPC_AS */ static SPC_ASCALLBACK spcAsCallback = 0; - -static void inherit_severity(const struct pv_link *ppv_link, - dbCommon *pdest, epicsEnum16 stat, epicsEnum16 sevr) -{ - switch(ppv_link->pvlMask&pvlOptMsMode) { - case pvlOptNMS: break; - case pvlOptMSI: if (sevr < INVALID_ALARM) break; - case pvlOptMS: recGblSetSevr(pdest,LINK_ALARM,sevr); break; - case pvlOptMSS: recGblSetSevr(pdest,stat,sevr); break; - } -} void epicsShareAPI dbSpcAsRegisterCallback(SPC_ASCALLBACK func) { @@ -113,7 +104,7 @@ long epicsShareAPI dbPutSpecial(DBADDR *paddr,int pass) dbCommon *precord = paddr->precord; long status=0; long special=paddr->special; - + prset = dbGetRset(paddr); if(special<100) { /*global processing*/ if((special==SPC_NOMOD) && (pass==0)) { @@ -139,7 +130,7 @@ long epicsShareAPI dbPutSpecial(DBADDR *paddr,int pass) } return(0); } - + static void get_enum_strs(DBADDR *paddr, char **ppbuffer, struct rset *prset,long *options) { @@ -199,7 +190,7 @@ choice_common: *ppbuffer = ((char *)*ppbuffer) + dbr_enumStrs_size; return; } - + static void get_graphics(DBADDR *paddr, char **ppbuffer, struct rset *prset,long *options) { @@ -239,7 +230,7 @@ static void get_graphics(DBADDR *paddr, char **ppbuffer, } return; } - + static void get_control(DBADDR *paddr, char **ppbuffer, struct rset *prset,long *options) { @@ -279,7 +270,7 @@ static void get_control(DBADDR *paddr, char **ppbuffer, } return; } - + static void get_alarm(DBADDR *paddr, char **ppbuffer, struct rset *prset,long *options) { @@ -324,35 +315,44 @@ static void get_alarm(DBADDR *paddr, char **ppbuffer, } return; } - -static void getOptions(DBADDR *paddr,char **poriginal,long *options,void *pflin) + +/* + * This code relies on *poriginal being aligned and all increments done by the + * blocks only changing the buffer pointer in a way that does not break alignment. + */ +static void getOptions(DBADDR *paddr, char **poriginal, long *options, + void *pflin) { db_field_log *pfl= (db_field_log *)pflin; struct rset *prset; - short field_type=paddr->field_type; + short field_type; dbCommon *pcommon; char *pbuffer = *poriginal; + if (!pfl || pfl->type == dbfl_type_rec) + field_type = paddr->field_type; + else + field_type = pfl->field_type; prset=dbGetRset(paddr); /* Process options */ pcommon = paddr->precord; if( (*options) & DBR_STATUS ) { unsigned short *pushort = (unsigned short *)pbuffer; - if(pfl!=NULL) { - *pushort++ = pfl->stat; - *pushort++ = pfl->sevr; - } else { - *pushort++ = pcommon->stat; - *pushort++ = pcommon->sevr; - } + if (!pfl || pfl->type == dbfl_type_rec) { + *pushort++ = pcommon->stat; + *pushort++ = pcommon->sevr; + } else { + *pushort++ = pfl->stat; + *pushort++ = pfl->sevr; + } *pushort++ = pcommon->acks; *pushort++ = pcommon->ackt; pbuffer = (char *)pushort; } if( (*options) & DBR_UNITS ) { memset(pbuffer,'\0',dbr_units_size); - if( prset && prset->get_units ){ + if( prset && prset->get_units ){ (*prset->get_units)(paddr, pbuffer); pbuffer[DB_UNITS_SIZE-1] = '\0'; } else { @@ -363,7 +363,7 @@ static void getOptions(DBADDR *paddr,char **poriginal,long *options,void *pflin) if( (*options) & DBR_PRECISION ) { memset(pbuffer, '\0', dbr_precision_size); if((field_type==DBF_FLOAT || field_type==DBF_DOUBLE) - && prset && prset->get_precision ){ + && prset && prset->get_precision ){ (*prset->get_precision)(paddr,pbuffer); } else { *options ^= DBR_PRECISION; /*Turn off DBR_PRECISION*/ @@ -373,12 +373,12 @@ static void getOptions(DBADDR *paddr,char **poriginal,long *options,void *pflin) if( (*options) & DBR_TIME ) { epicsUInt32 *ptime = (epicsUInt32 *)pbuffer; - if(pfl!=NULL) { - *ptime++ = pfl->time.secPastEpoch; - *ptime++ = pfl->time.nsec; - } else { - *ptime++ = pcommon->time.secPastEpoch; - *ptime++ = pcommon->time.nsec; + if (!pfl || pfl->type == dbfl_type_rec) { + *ptime++ = pcommon->time.secPastEpoch; + *ptime++ = pcommon->time.nsec; + } else { + *ptime++ = pfl->time.secPastEpoch; + *ptime++ = pfl->time.nsec; } pbuffer = (char *)ptime; } @@ -392,7 +392,7 @@ static void getOptions(DBADDR *paddr,char **poriginal,long *options,void *pflin) get_alarm(paddr, &pbuffer, prset, options); *poriginal = pbuffer; } - + struct rset * epicsShareAPI dbGetRset(const struct dbAddr *paddr) { struct dbFldDes *pfldDes = paddr->pfldDes; @@ -419,10 +419,10 @@ long epicsShareAPI dbPutAttribute( int epicsShareAPI dbIsValueField(const struct dbFldDes *pdbFldDes) { - if(pdbFldDes->pdbRecordType->indvalFlddes == pdbFldDes->indRecordType) - return(TRUE); + if (pdbFldDes->pdbRecordType->indvalFlddes == pdbFldDes->indRecordType) + return TRUE; else - return(FALSE); + return FALSE; } int epicsShareAPI dbGetFieldIndex(const struct dbAddr *paddr) @@ -430,50 +430,6 @@ int epicsShareAPI dbGetFieldIndex(const struct dbAddr *paddr) return paddr->pfldDes->indRecordType; } -long epicsShareAPI dbGetNelements(const struct link *plink,long *nelements) -{ - switch(plink->type) { - case CONSTANT: - *nelements = 0; - return(0); - case DB_LINK: { - DBADDR *paddr = (DBADDR *)plink->value.pv_link.pvt; - *nelements = paddr->no_elements; - return(0); - } - case CA_LINK: - return(dbCaGetNelements(plink,nelements)); - default: - break; - } - return(S_db_badField); -} - -int epicsShareAPI dbIsLinkConnected(const struct link *plink) -{ - switch(plink->type) { - case DB_LINK: return(TRUE); - case CA_LINK: return(dbCaIsLinkConnected(plink)); - default: break; - } - return(FALSE); -} - -int epicsShareAPI dbGetLinkDBFtype(const struct link *plink) -{ - switch(plink->type) { - case DB_LINK: - { - DBADDR *paddr = (DBADDR *)plink->value.pv_link.pvt; - - return((int)paddr->field_type); - } - case CA_LINK: return(dbCaGetLinkDBFtype(plink)); - default: break; - } - return(-1); -} - /* * Process a record if its scan field is passive. * Will notify if processing is complete by callback. @@ -481,50 +437,15 @@ int epicsShareAPI dbGetLinkDBFtype(const struct link *plink) */ long epicsShareAPI dbScanPassive(dbCommon *pfrom, dbCommon *pto) { - long status; - /* if not passive just return success */ - if(pto->scan != 0) return(0); + if (pto->scan != 0) + return 0; - if(pfrom && pfrom->ppn) dbNotifyAdd(pfrom,pto); - status = dbProcess(pto); - return(status); + if (pfrom && pfrom->ppn) + dbNotifyAdd(pfrom,pto); + return dbProcess(pto); } -/*KLUDGE: Following needed so that dbPutLink to PROC field works correctly*/ -long epicsShareAPI dbScanLink(dbCommon *pfrom, dbCommon *pto) -{ - long status; - unsigned char pact; - - if(pfrom && pfrom->ppn) dbNotifyAdd(pfrom,pto); - pact = pfrom->pact; - pfrom->pact = TRUE; - status = dbProcess(pto); - pfrom->pact = pact; - return(status); -} - -void epicsShareAPI dbScanFwdLink(struct link *plink) -{ - dbCommon *precord; - struct pv_link *pvlink; - short fwdLinkValue; - - if(plink->type!=DB_LINK && plink->type!=CA_LINK) return; - pvlink = &plink->value.pv_link; - precord = pvlink->precord; - if(plink->type==DB_LINK) { - dbAddr *paddr = (dbAddr *)plink->value.pv_link.pvt; - dbScanPassive(precord,paddr->precord); - return; - } - if(!(pvlink->pvlMask & pvlOptFWD)) return; - fwdLinkValue = 1; - dbCaPutLink(plink,DBR_SHORT,&fwdLinkValue,1); - return; -} - /* * Process the record. * 1. Check for breakpoints. @@ -536,121 +457,127 @@ void epicsShareAPI dbScanFwdLink(struct link *plink) */ long epicsShareAPI dbProcess(dbCommon *precord) { - struct rset *prset = precord->rset; - dbRecordType *pdbRecordType = precord->rdes; - unsigned char tpro=precord->tpro; - long status = 0; - int *ptrace; - int set_trace=FALSE; - dbFldDes *pdbFldDes; - int callNotifyCompletion = FALSE; + struct rset *prset = precord->rset; + dbRecordType *pdbRecordType = precord->rdes; + unsigned char tpro = precord->tpro; + long status = 0; + int *ptrace; + int set_trace = FALSE; + dbFldDes *pdbFldDes; + int callNotifyCompletion = FALSE; - ptrace = dbLockSetAddrTrace(precord); + ptrace = dbLockSetAddrTrace(precord); + /* + * Note that it is likely that if any changes are made + * to dbProcess() corresponding changes will have to + * be made in the breakpoint handler. + */ + + /* see if there are any stopped records or breakpoints */ + if (lset_stack_count != 0) { /* - * Note that it is likely that if any changes are made - * to dbProcess() corresponding changes will have to - * be made in the breakpoint handler. + * Check to see if the record should be processed + * and activate breakpoint accordingly. If this + * function call returns non-zero, skip record + * support and fall out of dbProcess(). This is + * done so that a dbContTask() can be spawned to + * take over record processing for the lock set + * containing a breakpoint. */ - - /* see if there are any stopped records or breakpoints */ - if (lset_stack_count != 0) { - /* - * Check to see if the record should be processed - * and activate breakpoint accordingly. If this - * function call returns non-zero, skip record - * support and fall out of dbProcess(). This is - * done so that a dbContTask() can be spawned to - * take over record processing for the lock set - * containing a breakpoint. - */ - if (dbBkpt(precord)) - goto all_done; + if (dbBkpt(precord)) + goto all_done; + } + + /* check for trace processing*/ + if (tpro) { + if(*ptrace==0) { + *ptrace = 1; + set_trace = TRUE; } - - /* check for trace processing*/ - if (tpro) { - if(*ptrace==0) { - *ptrace = 1; - set_trace = TRUE; - } - } + } - /* If already active dont process */ - if (precord->pact) { - unsigned short monitor_mask; + /* If already active dont process */ + if (precord->pact) { + unsigned short monitor_mask; - if (*ptrace) printf("%s: Active %s\n", - epicsThreadGetNameSelf(), precord->name); - /* raise scan alarm after MAX_LOCK times */ - if (precord->stat==SCAN_ALARM) goto all_done; - if (precord->lcnt++ !=MAX_LOCK) goto all_done; - if (precord->sevr>=INVALID_ALARM) goto all_done; - recGblSetSevr(precord, SCAN_ALARM, INVALID_ALARM); - monitor_mask = recGblResetAlarms(precord); - monitor_mask |= DBE_VALUE|DBE_LOG; - pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes]; - db_post_events(precord, - (void *)(((char *)precord) + pdbFldDes->offset), - monitor_mask); - goto all_done; - } - else precord->lcnt = 0; + if (*ptrace) + printf("%s: Active %s\n", + epicsThreadGetNameSelf(), precord->name); + /* raise scan alarm after MAX_LOCK times */ + if (precord->stat==SCAN_ALARM) goto all_done; + if (precord->lcnt++ !=MAX_LOCK) goto all_done; + if (precord->sevr>=INVALID_ALARM) goto all_done; + recGblSetSevr(precord, SCAN_ALARM, INVALID_ALARM); + monitor_mask = recGblResetAlarms(precord); + monitor_mask |= DBE_VALUE|DBE_LOG; + pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes]; + db_post_events(precord, + (void *)(((char *)precord) + pdbFldDes->offset), + monitor_mask); + goto all_done; + } + else precord->lcnt = 0; - /* - * Check the record disable link. A record will not be - * processed if the value retrieved through this link - * is equal to constant set in the record's disv field. - */ - status = dbGetLink(&(precord->sdis),DBR_SHORT,&(precord->disa),0,0); + /* + * Check the record disable link. A record will not be + * processed if the value retrieved through this link + * is equal to constant set in the record's disv field. + */ + status = dbGetLink(&precord->sdis, DBR_SHORT, &precord->disa, 0, 0); - /* if disabled check disable alarm severity and return success */ - if (precord->disa == precord->disv) { - if(*ptrace) printf("%s: Disabled %s\n", - epicsThreadGetNameSelf(), precord->name); - /*take care of caching and notifyCompletion*/ - precord->rpro = FALSE; - precord->putf = FALSE; - callNotifyCompletion = TRUE; - /* raise disable alarm */ - if (precord->stat==DISABLE_ALARM) goto all_done; - precord->sevr = precord->diss; - precord->stat = DISABLE_ALARM; - precord->nsev = 0; - precord->nsta = 0; - db_post_events(precord, &precord->stat, DBE_VALUE); - db_post_events(precord, &precord->sevr, DBE_VALUE); - pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes]; - db_post_events(precord, - (void *)(((char *)precord) + pdbFldDes->offset), - DBE_VALUE|DBE_ALARM); - goto all_done; - } + /* if disabled check disable alarm severity and return success */ + if (precord->disa == precord->disv) { + if(*ptrace) + printf("%s: Disabled %s\n", + epicsThreadGetNameSelf(), precord->name); - /* locate record processing routine */ - /* put this in iocInit() !!! */ - if (!(prset=precord->rset) || !(prset->process)) { - callNotifyCompletion = TRUE; - precord->pact=1;/*set pact TRUE so error is issued only once*/ - recGblRecordError(S_db_noRSET, (void *)precord, "dbProcess"); - status = S_db_noRSET; - if (*ptrace) printf("%s: No RSET for %s\n", - epicsThreadGetNameSelf(), precord->name); - goto all_done; - } - if(*ptrace) printf("%s: Process %s\n", - epicsThreadGetNameSelf(), precord->name); - /* process record */ - status = (*prset->process)(precord); - /* Print record's fields if PRINT_MASK set in breakpoint field */ - if (lset_stack_count != 0) { - dbPrint(precord); - } + /*take care of caching and notifyCompletion*/ + precord->rpro = FALSE; + precord->putf = FALSE; + callNotifyCompletion = TRUE; + + /* raise disable alarm */ + if (precord->stat==DISABLE_ALARM) goto all_done; + precord->sevr = precord->diss; + precord->stat = DISABLE_ALARM; + precord->nsev = 0; + precord->nsta = 0; + db_post_events(precord, &precord->stat, DBE_VALUE); + db_post_events(precord, &precord->sevr, DBE_VALUE); + pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes]; + db_post_events(precord, + (void *)(((char *)precord) + pdbFldDes->offset), + DBE_VALUE|DBE_ALARM); + goto all_done; + } + + /* locate record processing routine */ + /* FIXME: put this in iocInit() !!! */ + if (!(prset=precord->rset) || !(prset->process)) { + callNotifyCompletion = TRUE; + precord->pact=1;/*set pact TRUE so error is issued only once*/ + recGblRecordError(S_db_noRSET, (void *)precord, "dbProcess"); + status = S_db_noRSET; + if (*ptrace) + printf("%s: No RSET for %s\n", + epicsThreadGetNameSelf(), precord->name); + goto all_done; + } + if(*ptrace) + printf("%s: Process %s\n", + epicsThreadGetNameSelf(), precord->name); + /* process record */ + status = (*prset->process)(precord); + /* Print record's fields if PRINT_MASK set in breakpoint field */ + if (lset_stack_count != 0) { + dbPrint(precord); + } all_done: - if (set_trace) *ptrace = 0; - if(callNotifyCompletion && precord->ppn) dbNotifyCompletion(precord); - return(status); + if (set_trace) *ptrace = 0; + if(callNotifyCompletion && precord->ppn) dbNotifyCompletion(precord); + return(status); } - + /* * Fill out a database structure (*paddr) for * a record given by the name "pname." @@ -726,59 +653,7 @@ finish: dbFinishEntry(&dbEntry); return status; } - -/* JOH 10-19-04 */ -static char * dbCopyInNameComponentOfPV ( - char * pBuf, unsigned bufLen, const char * pComponent ) -{ - unsigned compLen = strlen ( pComponent ); - if ( compLen < bufLen ) { - strcpy ( pBuf, pComponent ); - return pBuf + compLen; - } - else { - unsigned reducedSize = bufLen - 1u; - strncpy ( pBuf, pComponent, reducedSize ); - pBuf[reducedSize] = '\0'; - return pBuf + reducedSize; - } -} -/* - * Copies PV name into pBuf of length bufLen - * - * Returns the number of bytes written to pBuf - * not including null termination. - * JOH 10-19-04 - */ -unsigned dbNameOfPV ( - const dbAddr * paddr, char * pBuf, unsigned bufLen ) -{ - dbFldDes * pfldDes = paddr->pfldDes; - char * pBufTmp = pBuf; - if ( bufLen == 0u ) { - return 0u; - } - pBufTmp = dbCopyInNameComponentOfPV ( - pBufTmp, bufLen, paddr->precord->name ); - pBufTmp = dbCopyInNameComponentOfPV ( - pBufTmp, bufLen - ( pBufTmp - pBuf ), "." ); - pBufTmp = dbCopyInNameComponentOfPV ( - pBufTmp, bufLen - ( pBufTmp - pBuf ), pfldDes->name ); - return pBufTmp - pBuf; -} -/* - * Returns the number of bytes in the PV name - * not including null termination. - * JOH 10-19-04 - */ -unsigned dbNameSizeOfPV ( const dbAddr * paddr ) -{ - unsigned recNameLen = strlen ( paddr->precord->name ); - dbFldDes * pfldDes = paddr->pfldDes; - unsigned fieldNameLen = strlen ( pfldDes->name ); - return recNameLen + fieldNameLen + 1; -} - + long epicsShareAPI dbValueSize(short dbr_type) { /* sizes for value associated with each DBR request type */ @@ -826,222 +701,117 @@ int epicsShareAPI dbLoadRecords(const char* file, const char* subs) return dbReadDatabase(&pdbbase, file, 0, subs); } - -long epicsShareAPI dbGetLinkValue(struct link *plink, short dbrType, - void *pbuffer, long *poptions, long *pnRequest) + +static long getLinkValue(DBADDR *paddr, short dbrType, + char *pbuf, long *nRequest) { - long status = 0; + dbCommon *precord = paddr->precord; + dbFldDes *pfldDes = paddr->pfldDes; + int maxlen; + DBENTRY dbEntry; + long status; - if (plink->type == CONSTANT) { - if (poptions) *poptions = 0; - if (pnRequest) *pnRequest = 0; - } else if (plink->type == DB_LINK) { - struct pv_link *ppv_link = &(plink->value.pv_link); - DBADDR *paddr = ppv_link->pvt; - dbCommon *precord = plink->value.pv_link.precord; + switch (dbrType) { + case DBR_STRING: + maxlen = MAX_STRING_SIZE - 1; + if (nRequest && *nRequest > 1) *nRequest = 1; + break; - /* scan passive records with links that are process passive */ - if (ppv_link->pvlMask&pvlOptPP) { - dbCommon *pfrom = paddr->precord; - unsigned char pact; - - pact = precord->pact; - precord->pact = TRUE; - status = dbScanPassive(precord,pfrom); - precord->pact = pact; - if (status) return status; + case DBR_CHAR: + case DBR_UCHAR: + if (nRequest && *nRequest > 0) { + maxlen = *nRequest - 1; + break; } - if(precord!= paddr->precord) { - inherit_severity(ppv_link,precord,paddr->precord->stat,paddr->precord->sevr); - } - - if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) { - status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr); - } else { - unsigned short dbfType = paddr->field_type; - long no_elements = paddr->no_elements; - - if (dbrType < 0 || dbrType > DBR_ENUM || - dbfType > DBF_DEVICE) { - status = S_db_badDbrtype; - recGblRecordError(status, precord, "GetLinkValue Failed"); - recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); - return status; - } - /* attempt to make a fast link */ - if ((!poptions || *poptions == 0) && - no_elements == 1 && - (!pnRequest || *pnRequest == 1) && - paddr->special != SPC_ATTRIBUTE) { - ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType]; - status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr); - } else { - ppv_link->getCvt = 0; - status = dbGet(paddr, dbrType, pbuffer, poptions, pnRequest, NULL); - } - } - ppv_link->lastGetdbrType = dbrType; - if (status) { - recGblRecordError(status, precord, "dbGetLinkValue"); - recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); - } - } else if (plink->type == CA_LINK) { - struct dbCommon *precord = plink->value.pv_link.precord; - const struct pv_link *pcalink = &plink->value.pv_link; - epicsEnum16 sevr, stat; - - status=dbCaGetLink(plink,dbrType,pbuffer,&stat,&sevr,pnRequest); - if (status) { - recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); - } else { - inherit_severity(pcalink,precord,stat,sevr); - } - if (poptions) *poptions = 0; - } else { - cantProceed("dbGetLinkValue: Illegal link type"); + /* else fall through ... */ + default: + return S_db_badDbrtype; } + + dbInitEntry(pdbbase, &dbEntry); + status = dbFindRecord(&dbEntry, precord->name); + if (!status) status = dbFindField(&dbEntry, pfldDes->name); + if (!status) { + char *rtnString = dbGetString(&dbEntry); + + strncpy(pbuf, rtnString, --maxlen); + pbuf[maxlen] = 0; + } + dbFinishEntry(&dbEntry); return status; } - -long epicsShareAPI dbPutLinkValue(struct link *plink, short dbrType, - const void *pbuffer, long nRequest) + +static long getAttrValue(DBADDR *paddr, short dbrType, + char *pbuf, long *nRequest) { - long status = 0; + int maxlen; - if (plink->type == DB_LINK) { - struct dbCommon *psource = plink->value.pv_link.precord; - struct pv_link *ppv_link = &plink->value.pv_link; - DBADDR *paddr = (DBADDR *)ppv_link->pvt; - dbCommon *pdest = paddr->precord; + if (!paddr->pfield) return S_db_badField; - status = dbPut(paddr, dbrType, pbuffer, nRequest); - inherit_severity(ppv_link,pdest,psource->nsta,psource->nsev); - if (status) return status; + switch (dbrType) { + case DBR_STRING: + maxlen = MAX_STRING_SIZE - 1; + if (nRequest && *nRequest > 1) *nRequest = 1; + break; - if (paddr->pfield == (void *)&pdest->proc || - (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) { - /*if dbPutField caused asyn record to process */ - /* ask for reprocessing*/ - if (pdest->putf) { - pdest->rpro = TRUE; - } else { /* otherwise ask for the record to be processed*/ - status = dbScanLink(psource, pdest); - } + case DBR_CHAR: + case DBR_UCHAR: + if (nRequest && *nRequest > 0) { + maxlen = *nRequest - 1; + break; } - if (status) - recGblSetSevr(psource, LINK_ALARM, INVALID_ALARM); - } else if (plink->type == CA_LINK) { - struct dbCommon *psource = plink->value.pv_link.precord; - - status = dbCaPutLink(plink, dbrType, pbuffer, nRequest); - if (status < 0) - recGblSetSevr(psource, LINK_ALARM, INVALID_ALARM); - } else { - cantProceed("dbPutLinkValue: Illegal link type"); + /* else fall through ... */ + default: + return S_db_badDbrtype; } - return status; + + strncpy(pbuf, paddr->pfield, --maxlen); + pbuf[maxlen] = 0; + return 0; } - + long epicsShareAPI dbGetField(DBADDR *paddr,short dbrType, void *pbuffer, long *options, long *nRequest, void *pflin) { - short dbfType = paddr->field_type; dbCommon *precord = paddr->precord; long status = 0; dbScanLock(precord); - if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) { - DBENTRY dbEntry; - dbFldDes *pfldDes = paddr->pfldDes; - char *rtnString; - char *pbuf = (char *)pbuffer; - int maxlen; - - if (options && (*options)) - getOptions(paddr, &pbuf, options, pflin); - if (nRequest && *nRequest == 0) goto done; - - switch (dbrType) { - case DBR_STRING: - maxlen = MAX_STRING_SIZE - 1; - if (nRequest && *nRequest > 1) *nRequest = 1; - break; - - case DBR_CHAR: - case DBR_UCHAR: - if (nRequest && *nRequest > 0) { - maxlen = *nRequest - 1; - break; - } - /* else fall through ... */ - default: - status = S_db_badDbrtype; - goto done; - } - - dbInitEntry(pdbbase, &dbEntry); - status = dbFindRecord(&dbEntry, precord->name); - if (!status) status = dbFindField(&dbEntry, pfldDes->name); - if (!status) { - rtnString = dbGetString(&dbEntry); - strncpy(pbuf, rtnString, maxlen); - pbuf[maxlen] = 0; - } - dbFinishEntry(&dbEntry); - } else { - status = dbGet(paddr, dbrType, pbuffer, options, nRequest, pflin); - } -done: + status = dbGet(paddr, dbrType, pbuffer, options, nRequest, pflin); dbScanUnlock(precord); return status; } - + long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, void *pbuffer, long *options, long *nRequest, void *pflin) { + char *pbuf = pbuffer; db_field_log *pfl = (db_field_log *)pflin; - short field_type = paddr->field_type; - long no_elements = paddr->no_elements; + short field_type; + long no_elements; long offset; struct rset *prset; long status = 0; - if (options && *options) { - char *pbuf = pbuffer; - + if (options && *options) getOptions(paddr, &pbuf, options, pflin); - pbuffer = pbuf; - } - if (nRequest && *nRequest == 0) return 0; - - if (paddr->special == SPC_ATTRIBUTE) { - char *pbuf = pbuffer; - int maxlen; - - if (!paddr->pfield) return S_db_badField; - - switch (dbrType) { - case DBR_STRING: - maxlen = MAX_STRING_SIZE - 1; - if (nRequest && *nRequest > 1) *nRequest = 1; - break; - - case DBR_CHAR: - case DBR_UCHAR: - if (nRequest && *nRequest > 0) { - maxlen = *nRequest - 1; - break; - } - /* else fall through ... */ - default: - return S_db_badDbrtype; - } - - strncpy(pbuf, (char *)paddr->pfield, maxlen); - pbuf[maxlen] = 0; + if (nRequest && *nRequest == 0) return 0; + + if (!pfl || pfl->type == dbfl_type_rec) { + field_type = paddr->field_type; + no_elements = paddr->no_elements; + } else { + field_type = pfl->field_type; + no_elements = pfl->no_elements; } + if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK) + return getLinkValue(paddr, dbrType, pbuf, nRequest); + + if (paddr->special == SPC_ATTRIBUTE) + return getAttrValue(paddr, dbrType, pbuf, nRequest); + /* Check for valid request */ if (INVALID_DB_REQ(dbrType) || field_type > DBF_DEVICE) { char message[80]; @@ -1052,7 +822,8 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, } /* check for array */ - if (paddr->special == SPC_DBADDR && + if ((!pfl || pfl->type == dbfl_type_rec) && + paddr->special == SPC_DBADDR && no_elements > 1 && (prset = dbGetRset(paddr)) && prset->get_array_info) { @@ -1062,15 +833,20 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, if (offset == 0 && (!nRequest || no_elements == 1)) { if (nRequest) *nRequest = 1; - if (pfl != NULL) { - DBADDR localAddr = *paddr; /* Structure copy */ - - localAddr.pfield = (char *)&pfl->field; - status = dbFastGetConvertRoutine[field_type][dbrType] - (localAddr.pfield, pbuffer, &localAddr); - } else { + if (!pfl || pfl->type == dbfl_type_rec) { status = dbFastGetConvertRoutine[field_type][dbrType] (paddr->pfield, pbuffer, paddr); + } else { + DBADDR localAddr = *paddr; /* Structure copy */ + localAddr.field_type = pfl->field_type; + localAddr.field_size = pfl->field_size; + localAddr.no_elements = pfl->no_elements; + if (pfl->type == dbfl_type_val) + localAddr.pfield = (char *) &pfl->u.v.field; + else + localAddr.pfield = (char *) pfl->u.r.field; + status = dbFastGetConvertRoutine[field_type][dbrType] + (localAddr.pfield, pbuffer, &localAddr); } } else { long n; @@ -1094,18 +870,23 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, /* convert database field and place it in the buffer */ if (n <= 0) { ;/*do nothing*/ - } else if (pfl) { - DBADDR localAddr = *paddr; /* Structure copy */ - - localAddr.pfield = (char *)&pfl->field; - status = convert(&localAddr, pbuffer, n, no_elements, offset); - } else { + } else if (!pfl || pfl->type == dbfl_type_rec) { status = convert(paddr, pbuffer, n, no_elements, offset); + } else { + DBADDR localAddr = *paddr; /* Structure copy */ + localAddr.field_type = pfl->field_type; + localAddr.field_size = pfl->field_size; + localAddr.no_elements = pfl->no_elements; + if (pfl->type == dbfl_type_val) + localAddr.pfield = (char *) &pfl->u.v.field; + else + localAddr.pfield = (char *) pfl->u.r.field; + status = convert(&localAddr, pbuffer, n, no_elements, offset); } } return status; } - + devSup* epicsShareAPI dbDTYPtoDevSup(dbRecordType *prdes, int dtyp) { return (devSup *)ellNth(&prdes->devList, dtyp+1); } @@ -1125,10 +906,9 @@ static long dbPutFieldLink(DBADDR *paddr, dbCommon *precord = paddr->precord; dbFldDes *pfldDes = paddr->pfldDes; long special = paddr->special; - DBLINK *plink = (DBLINK *)paddr->pfield; + struct link *plink = (struct link *)paddr->pfield; const char *pstring = (const char *)pbuffer; DBENTRY dbEntry; - DBADDR dbaddr; struct dsxt *old_dsxt = NULL; struct dset *new_dset = NULL; struct dsxt *new_dsxt = NULL; @@ -1199,21 +979,8 @@ static long dbPutFieldLink(DBADDR *paddr, switch (plink->type) { /* Old link type */ case DB_LINK: - free(plink->value.pv_link.pvt); - plink->value.pv_link.pvt = 0; - plink->type = PV_LINK; - plink->value.pv_link.getCvt = 0; - plink->value.pv_link.pvlMask = 0; - plink->value.pv_link.lastGetdbrType = 0; - dbLockSetSplit(precord); - break; - case CA_LINK: - dbCaRemoveLink(plink); - plink->type = PV_LINK; - plink->value.pv_link.getCvt = 0; - plink->value.pv_link.pvlMask = 0; - plink->value.pv_link.lastGetdbrType = 0; + dbRemoveLink(plink); break; case CONSTANT: @@ -1260,35 +1027,7 @@ static long dbPutFieldLink(DBADDR *paddr, switch (plink->type) { /* New link type */ case PV_LINK: - if (plink == &precord->tsel) - recGblTSELwasModified(plink); - plink->value.pv_link.precord = precord; - - if (!(plink->value.pv_link.pvlMask & (pvlOptCA|pvlOptCP|pvlOptCPP)) && - (dbNameToAddr(plink->value.pv_link.pvname, &dbaddr) == 0)) { - /* It's a DB link */ - DBADDR *pdbAddr; - - plink->type = DB_LINK; - pdbAddr = dbMalloc(sizeof(struct dbAddr)); - *pdbAddr = dbaddr; /* NB: structure copy */; - plink->value.pv_link.pvt = pdbAddr; - dbLockSetRecordLock(pdbAddr->precord); - dbLockSetMerge(precord, pdbAddr->precord); - } else { /* Make it a CA link */ - char *pperiod; - - plink->type = CA_LINK; - if (pfldDes->field_type == DBF_INLINK) { - plink->value.pv_link.pvlMask |= pvlOptInpNative; - } - dbCaAddLink(plink); - if (pfldDes->field_type == DBF_FWDLINK) { - pperiod = strrchr(plink->value.pv_link.pvname, '.'); - if (pperiod && strstr(pperiod, "PROC")) - plink->value.pv_link.pvlMask |= pvlOptFWD; - } - } + dbAddLink(precord, plink, pfldDes->field_type); break; case CONSTANT: @@ -1316,7 +1055,7 @@ restoreScan: } postScanEvent: if (scan != precord->scan) - db_post_events(precord, &precord->scan, DBE_VALUE|DBE_LOG); + db_post_events(precord, &precord->scan, DBE_VALUE | DBE_LOG); unlock: dbLockSetGblUnlock(); finish: @@ -1337,8 +1076,7 @@ long epicsShareAPI dbPutField(DBADDR *paddr, short dbrType, return S_db_noMod; /*check for putField disabled*/ - if (precord->disp && - (void *)(&precord->disp) != paddr->pfield) + if (precord->disp && paddr->pfield != &precord->disp) return S_db_putDisabled; if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) @@ -1347,7 +1085,7 @@ long epicsShareAPI dbPutField(DBADDR *paddr, short dbrType, dbScanLock(precord); status = dbPut(paddr, dbrType, pbuffer, nRequest); if (status == 0) { - if (paddr->pfield == (void *)&precord->proc || + if (paddr->pfield == &precord->proc || (pfldDes->process_passive && precord->scan == 0 && dbrType < DBR_PUT_ACKT)) { @@ -1366,16 +1104,18 @@ long epicsShareAPI dbPutField(DBADDR *paddr, short dbrType, dbScanUnlock(precord); return status; } - -static long putAckt(DBADDR *paddr, const unsigned short *pbuffer, long nRequest, + +static long putAckt(DBADDR *paddr, const void *pbuffer, long nRequest, long no_elements, long offset) { dbCommon *precord = paddr->precord; + const unsigned short *ptrans = pbuffer; - if (*pbuffer == precord->ackt) return 0; - precord->ackt = *pbuffer; + if (*ptrans == precord->ackt) return 0; + precord->ackt = *ptrans; db_post_events(precord, &precord->ackt, DBE_VALUE | DBE_ALARM); - if (!precord->ackt && precord->acks > precord->sevr) { + if (!precord->ackt && + precord->acks > precord->sevr) { precord->acks = precord->sevr; db_post_events(precord, &precord->acks, DBE_VALUE | DBE_ALARM); } @@ -1383,19 +1123,20 @@ static long putAckt(DBADDR *paddr, const unsigned short *pbuffer, long nRequest, return 0; } -static long putAcks(DBADDR *paddr, const unsigned short *pbuffer, long nRequest, +static long putAcks(DBADDR *paddr, const void *pbuffer, long nRequest, long no_elements, long offset) { dbCommon *precord = paddr->precord; + const unsigned short *psev = pbuffer; - if (*pbuffer >= precord->acks) { + if (*psev >= precord->acks) { precord->acks = 0; - db_post_events(precord, NULL, DBE_ALARM); db_post_events(precord, &precord->acks, DBE_VALUE | DBE_ALARM); + db_post_events(precord, NULL, DBE_ALARM); } return 0; } - + long epicsShareAPI dbPut(DBADDR *paddr, short dbrType, const void *pbuffer, long nRequest) { @@ -1403,17 +1144,17 @@ long epicsShareAPI dbPut(DBADDR *paddr, short dbrType, short field_type = paddr->field_type; long no_elements = paddr->no_elements; long special = paddr->special; - long offset; long status = 0; dbFldDes *pfldDes; int isValueField; - if (special == SPC_ATTRIBUTE) return S_db_noMod; + if (special == SPC_ATTRIBUTE) + return S_db_noMod; if (dbrType == DBR_PUT_ACKT && field_type <= DBF_DEVICE) { - return putAckt(paddr, (unsigned short *)pbuffer, 1, 1, 0); + return putAckt(paddr, pbuffer, 1, 1, 0); } else if (dbrType == DBR_PUT_ACKS && field_type <= DBF_DEVICE) { - return putAcks(paddr, (unsigned short *)pbuffer, 1, 1, 0); + return putAcks(paddr, pbuffer, 1, 1, 0); } else if (INVALID_DB_REQ(dbrType) || field_type > DBF_DEVICE) { char message[80]; @@ -1432,6 +1173,7 @@ long epicsShareAPI dbPut(DBADDR *paddr, short dbrType, paddr->pfield, paddr); } else { struct rset *prset = dbGetRset(paddr); + long offset = 0; if (paddr->special == SPC_DBADDR && prset && prset->get_array_info) { @@ -1439,8 +1181,6 @@ long epicsShareAPI dbPut(DBADDR *paddr, short dbrType, status = prset->get_array_info(paddr, &dummy, &offset); } - else - offset = 0; if (no_elements < nRequest) nRequest = no_elements; status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer, nRequest, no_elements, offset); @@ -1471,140 +1211,4 @@ long epicsShareAPI dbPut(DBADDR *paddr, short dbrType, return status; } - -/* various utility routines */ -long epicsShareAPI dbGetControlLimits( - const struct link *plink,double *low, double *high) -{ - struct buffer { - DBRctrlDouble - double value; - } buffer; - DBADDR *paddr; - long options = DBR_CTRL_DOUBLE; - long number_elements = 0; - long status; - if(plink->type == CA_LINK) return(dbCaGetControlLimits(plink,low,high)); - if(plink->type !=DB_LINK) return(S_db_notFound); - paddr = (DBADDR *)plink->value.pv_link.pvt; - status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0); - if(status) return(status); - *low = buffer.lower_ctrl_limit; - *high = buffer.upper_ctrl_limit; - return(0); -} - -long epicsShareAPI dbGetGraphicLimits( - const struct link *plink,double *low, double *high) -{ - struct buffer { - DBRgrDouble - double value; - } buffer; - DBADDR *paddr; - long options = DBR_GR_DOUBLE; - long number_elements = 0; - long status; - - if(plink->type == CA_LINK) return(dbCaGetGraphicLimits(plink,low,high)); - if(plink->type !=DB_LINK) return(S_db_notFound); - paddr = (DBADDR *)plink->value.pv_link.pvt; - status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0); - if(status) return(status); - *low = buffer.lower_disp_limit; - *high = buffer.upper_disp_limit; - return(0); -} - -long epicsShareAPI dbGetAlarmLimits(const struct link *plink, - double *lolo, double *low, double *high, double *hihi) -{ - struct buffer { - DBRalDouble - double value; - } buffer; - DBADDR *paddr; - long options = DBR_AL_DOUBLE; - long number_elements = 0; - long status; - - if(plink->type == CA_LINK) - return(dbCaGetAlarmLimits(plink,lolo,low,high,hihi)); - if(plink->type !=DB_LINK) return(S_db_notFound); - paddr = (DBADDR *)plink->value.pv_link.pvt; - status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0); - if(status) return(status); - *lolo = buffer.lower_alarm_limit; - *low = buffer.lower_warning_limit; - *high = buffer.upper_warning_limit; - *hihi = buffer.upper_alarm_limit; - return(0); -} - -long epicsShareAPI dbGetPrecision(const struct link *plink,short *precision) -{ - struct buffer { - DBRprecision - double value; - } buffer; - DBADDR *paddr; - long options = DBR_PRECISION; - long number_elements = 0; - long status; - - if(plink->type == CA_LINK) return(dbCaGetPrecision(plink,precision)); - if(plink->type !=DB_LINK) return(S_db_notFound); - paddr = (DBADDR *)plink->value.pv_link.pvt; - status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0); - if(status) return(status); - *precision = buffer.precision.dp; - return(0); -} - -long epicsShareAPI dbGetUnits( - const struct link *plink,char *units,int unitsSize) -{ - struct buffer { - DBRunits - double value; - } buffer; - DBADDR *paddr; - long options = DBR_UNITS; - long number_elements = 0; - long status; - - if(plink->type == CA_LINK) return(dbCaGetUnits(plink,units,unitsSize)); - if(plink->type !=DB_LINK) return(S_db_notFound); - paddr = (DBADDR *)plink->value.pv_link.pvt; - status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0); - if(status) return(status); - strncpy(units,buffer.units,unitsSize); - return(0); -} - -long epicsShareAPI dbGetAlarm(const struct link *plink, - epicsEnum16 *status,epicsEnum16 *severity) -{ - DBADDR *paddr; - - if(plink->type == CA_LINK) return(dbCaGetAlarm(plink,status,severity)); - if(plink->type !=DB_LINK) return(S_db_notFound); - paddr = (DBADDR *)plink->value.pv_link.pvt; - if (status) *status = paddr->precord->stat; - if (severity) *severity = paddr->precord->sevr; - return(0); -} - -long epicsShareAPI dbGetTimeStamp(const struct link *plink,epicsTimeStamp *pstamp) -{ - DBADDR *paddr; - - if (plink->type == CA_LINK) - return dbCaGetTimeStamp(plink,pstamp); - if (plink->type != DB_LINK) - return S_db_notFound; - paddr = (DBADDR *)plink->value.pv_link.pvt; - *pstamp = paddr->precord->time; - return 0; -} diff --git a/src/ioc/db/dbAccess.h b/src/ioc/db/dbAccess.h index 909774323..96246b302 100644 --- a/src/ioc/db/dbAccess.h +++ b/src/ioc/db/dbAccess.h @@ -3,8 +3,7 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbAccess.h */ @@ -22,6 +21,7 @@ #include "dbAddr.h" #include "dbLock.h" #include "dbAccessDefs.h" +#include "dbLink.h" #include "dbCa.h" #include "dbCommon.h" #include "db_field_log.h" diff --git a/src/ioc/db/dbAccessDefs.h b/src/ioc/db/dbAccessDefs.h index 73d68c4ef..7e5c7a78d 100644 --- a/src/ioc/db/dbAccessDefs.h +++ b/src/ioc/db/dbAccessDefs.h @@ -3,9 +3,8 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbAccessDefs.h */ /* $Revision-Id$ */ @@ -20,6 +19,8 @@ #include "epicsTypes.h" #include "epicsTime.h" +#include "dbBase.h" +#include "dbAddr.h" #ifdef INCLdb_accessh_epicsExportSharedSymbols # define epicsExportSharedSymbols @@ -47,7 +48,7 @@ epicsShareExtern volatile int interruptAccept; #define DBR_CTRL_DOUBLE 0x00000100 #define DBR_AL_LONG 0x00000200 #define DBR_AL_DOUBLE 0x00000400 - + /********************************************************************** * The next page contains macros for defining requests. * As an example the following defines a buffer to accept an array @@ -69,7 +70,7 @@ epicsShareExtern volatile int interruptAccept; * options = DBR_STATUS|DBR_TIME; * number_elements = 10; * rtnval=dbGetField(paddr,DBR_FLOAT,&buffer,&options,&number_elements); - * + * * When dbGetField returns: * rtnval is error status (0 means success) * options has a bit set for each option that was accepted @@ -97,7 +98,7 @@ epicsShareExtern volatile int interruptAccept; * MYBUFFER *pbuf1; * MYBUFFER buf; *************************************************************************/ - + /* Macros for defining each option */ #define DBRstatus \ epicsUInt16 status; /* alarm status */\ @@ -143,7 +144,7 @@ epicsShareExtern volatile int interruptAccept; epicsFloat64 upper_warning_limit;\ epicsFloat64 lower_warning_limit;\ epicsFloat64 lower_alarm_limit; - + /* structures for each option type */ struct dbr_status {DBRstatus}; struct dbr_units {DBRunits}; @@ -198,25 +199,6 @@ struct dbr_alDouble {DBRalDouble}; #define S_db_cntSpwn (M_dbAccess|63) /*Cannot spawn dbContTask*/ #define S_db_cntCont (M_dbAccess|65) /*Cannot resume dbContTask*/ #define S_db_noMemory (M_dbAccess|66) /*unable to allocate data structure from pool*/ - -/* Global Database Access Routines*/ -#define dbGetLink(PLNK, DBRTYPE, PBUFFER, OPTIONS, NREQUEST) \ - ( ( ( (PLNK)->type == CONSTANT ) && \ - ( (NREQUEST) == 0) &&\ - ( (OPTIONS) == 0) ) \ - ? 0 \ - : dbGetLinkValue((PLNK),(DBRTYPE), \ - (void *)(PBUFFER), (OPTIONS), (NREQUEST) ) ) -#define dbPutLink(PLNK, DBRTYPE, PBUFFER, NREQUEST) \ - ( ( (PLNK)->type == CONSTANT) \ - ? 0 \ - : dbPutLinkValue( (PLNK), (DBRTYPE), (void *)(PBUFFER), (NREQUEST) ) ) -#define dbGetPdbAddrFromLink(PLNK) \ - ( ( (PLNK)->type != DB_LINK ) \ - ? 0 \ - : ( ( (struct dbAddr *)( (PLNK)->value.pv_link.pvt) ) ) ) -#define dbGetSevr(PLINK,PSEVERITY) \ - dbGetAlarm((PLINK),NULL,(PSEVERITY)) epicsShareFunc long epicsShareAPI dbPutSpecial(struct dbAddr *paddr,int pass); epicsShareFunc struct rset * epicsShareAPI dbGetRset(const struct dbAddr *paddr); @@ -224,51 +206,24 @@ epicsShareFunc long epicsShareAPI dbPutAttribute( const char *recordTypename,const char *name,const char*value); epicsShareFunc int epicsShareAPI dbIsValueField(const struct dbFldDes *pdbFldDes); epicsShareFunc int epicsShareAPI dbGetFieldIndex(const struct dbAddr *paddr); -epicsShareFunc long epicsShareAPI dbGetNelements( - const struct link *plink,long *nelements); -epicsShareFunc int epicsShareAPI dbIsLinkConnected(const struct link *plink); -epicsShareFunc int epicsShareAPI dbGetLinkDBFtype(const struct link *plink); -epicsShareFunc long epicsShareAPI dbScanLink( - struct dbCommon *pfrom, struct dbCommon *pto); epicsShareFunc long epicsShareAPI dbScanPassive( struct dbCommon *pfrom,struct dbCommon *pto); -epicsShareFunc void epicsShareAPI dbScanFwdLink(struct link *plink); epicsShareFunc long epicsShareAPI dbProcess(struct dbCommon *precord); epicsShareFunc long epicsShareAPI dbNameToAddr( const char *pname,struct dbAddr *); epicsShareFunc devSup* epicsShareAPI dbDTYPtoDevSup(dbRecordType *prdes, int dtyp); epicsShareFunc devSup* epicsShareAPI dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset); -epicsShareFunc long epicsShareAPI dbGetLinkValue( - struct link *,short dbrType,void *pbuffer,long *options,long *nRequest); epicsShareFunc long epicsShareAPI dbGetField( struct dbAddr *,short dbrType,void *pbuffer,long *options, long *nRequest,void *pfl); epicsShareFunc long epicsShareAPI dbGet( struct dbAddr *,short dbrType,void *pbuffer,long *options, long *nRequest,void *pfl); -epicsShareFunc long epicsShareAPI dbPutLinkValue( - struct link *,short dbrType,const void *pbuffer,long nRequest); epicsShareFunc long epicsShareAPI dbPutField( struct dbAddr *,short dbrType,const void *pbuffer,long nRequest); epicsShareFunc long epicsShareAPI dbPut( struct dbAddr *,short dbrType,const void *pbuffer,long nRequest); -/* various utility routines */ -epicsShareFunc long epicsShareAPI dbGetControlLimits( - const struct link *plink,double *low, double *high); -epicsShareFunc long epicsShareAPI dbGetGraphicLimits( - const struct link *plink,double *low, double *high); -epicsShareFunc long epicsShareAPI dbGetAlarmLimits( - const struct link *plink,double *lolo, double *low, double *high, double *hihi); -epicsShareFunc long epicsShareAPI dbGetPrecision( - const struct link *plink,short *precision); -epicsShareFunc long epicsShareAPI dbGetUnits( - const struct link *plink,char *units,int unitsSize); -epicsShareFunc long epicsShareAPI dbGetAlarm( - const struct link *plink, epicsEnum16 *status,epicsEnum16 *severity); -epicsShareFunc long epicsShareAPI dbGetTimeStamp( - const struct link *plink,epicsTimeStamp *pstamp); - typedef void(*SPC_ASCALLBACK)(struct dbCommon *); /*dbSpcAsRegisterCallback called by access security */ epicsShareFunc void epicsShareAPI dbSpcAsRegisterCallback(SPC_ASCALLBACK func); diff --git a/src/ioc/db/dbAddr.h b/src/ioc/db/dbAddr.h index d7fdd94e9..2a6e4c0e2 100644 --- a/src/ioc/db/dbAddr.h +++ b/src/ioc/db/dbAddr.h @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos 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. \*************************************************************************/ #ifndef dbAddrh @@ -27,7 +27,4 @@ typedef struct dbAddr { typedef dbAddr DBADDR; -unsigned dbNameOfPV (const dbAddr * paddr, char * pBuf, unsigned bufLen); -unsigned dbNameSizeOfPV (const dbAddr * paddr); - #endif /* dbAddrh */ diff --git a/src/ioc/db/dbBkpt.c b/src/ioc/db/dbBkpt.c index f865ba80c..681886676 100644 --- a/src/ioc/db/dbBkpt.c +++ b/src/ioc/db/dbBkpt.c @@ -65,6 +65,7 @@ #include "dbAddr.h" #include "dbAccessDefs.h" #include "dbScan.h" +#include "dbLink.h" #include "dbLock.h" #include "recGbl.h" #include "dbTest.h" diff --git a/src/ioc/db/dbCAC.h b/src/ioc/db/dbCAC.h index 2ab3aacc3..9f297b8cc 100644 --- a/src/ioc/db/dbCAC.h +++ b/src/ioc/db/dbCAC.h @@ -5,7 +5,7 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are 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. \*************************************************************************/ /* * $Revision-Id$ @@ -49,7 +49,7 @@ #include "db_access.h" #include "dbNotify.h" #include "dbEvent.h" -#include "dbAddr.h" +#include "dbChannel.h" #include "dbLock.h" #include "dbCommon.h" #include "db_convert.h" @@ -75,25 +75,25 @@ protected: virtual ~dbBaseIO() {} }; -extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbAddr *paddr, +extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbChannel *dbch, int eventsRemaining, struct db_field_log *pfl ); -class dbSubscriptionIO : - public tsDLNode < dbSubscriptionIO >, +class dbSubscriptionIO : + public tsDLNode < dbSubscriptionIO >, public dbBaseIO { public: - dbSubscriptionIO ( + dbSubscriptionIO ( epicsGuard < epicsMutex > &, epicsMutex &, - dbContext &, dbChannelIO &, struct dbAddr &, cacStateNotify &, + dbContext &, dbChannelIO &, struct dbChannel *, cacStateNotify &, unsigned type, unsigned long count, unsigned mask, dbEventCtx ); void destructor ( epicsGuard < epicsMutex > & ); void unsubscribe ( epicsGuard < epicsMutex > & ); void channelDeleteException ( epicsGuard < epicsMutex > & ); void show ( epicsGuard < epicsMutex > &, unsigned level ) const; void show ( unsigned level ) const; - void * operator new ( size_t size, + void * operator new ( size_t size, tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & ); - epicsPlacementDeleteOperator (( void *, + epicsPlacementDeleteOperator (( void *, tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & )) private: epicsMutex & mutex; @@ -104,8 +104,8 @@ private: unsigned type; unsigned id; dbSubscriptionIO * isSubscription (); - friend void dbSubscriptionEventCallback ( - void * pPrivate, struct dbAddr * paddr, + friend void dbSubscriptionEventCallback ( + void * pPrivate, struct dbChannel * dbch, int eventsRemaining, struct db_field_log * pfl ); dbSubscriptionIO ( const dbSubscriptionIO & ); dbSubscriptionIO & operator = ( const dbSubscriptionIO & ); @@ -148,8 +148,8 @@ private: class dbContextReadNotifyCache { public: dbContextReadNotifyCache ( epicsMutex & ); - void callReadNotify ( epicsGuard < epicsMutex > &, - struct dbAddr & addr, unsigned type, unsigned long count, + void callReadNotify ( epicsGuard < epicsMutex > &, + struct dbChannel * dbch, unsigned type, unsigned long count, cacReadNotify & notify ); void show ( epicsGuard < epicsMutex > &, unsigned level ) const; private: @@ -161,29 +161,29 @@ private: class dbContext : public cacContext { public: - dbContext ( epicsMutex & cbMutex, epicsMutex & mutex, + dbContext ( epicsMutex & cbMutex, epicsMutex & mutex, cacContextNotify & notify ); virtual ~dbContext (); void destroyChannel ( epicsGuard < epicsMutex > &, dbChannelIO & ); - void callReadNotify ( epicsGuard < epicsMutex > &, - struct dbAddr & addr, unsigned type, unsigned long count, + void callReadNotify ( epicsGuard < epicsMutex > &, + struct dbChannel * dbch, unsigned type, unsigned long count, cacReadNotify & notify ); - void callStateNotify ( struct dbAddr &addr, unsigned type, unsigned long count, + void callStateNotify ( struct dbChannel * dbch, unsigned type, unsigned long count, const struct db_field_log * pfl, cacStateNotify & notify ); - void subscribe ( + void subscribe ( epicsGuard < epicsMutex > &, - struct dbAddr & addr, dbChannelIO & chan, - unsigned type, unsigned long count, unsigned mask, + struct dbChannel * dbch, dbChannelIO & chan, + unsigned type, unsigned long count, unsigned mask, cacStateNotify & notify, cacChannel::ioid * pId ); - void initiatePutNotify ( - epicsGuard < epicsMutex > &, dbChannelIO &, struct dbAddr &, - unsigned type, unsigned long count, const void * pValue, - cacWriteNotify & notify, cacChannel::ioid * pId ); + void initiatePutNotify ( + epicsGuard < epicsMutex > &, dbChannelIO &, struct dbChannel *, + unsigned type, unsigned long count, const void * pValue, + cacWriteNotify & notify, cacChannel::ioid * pId ); void show ( unsigned level ) const; void showAllIO ( const dbChannelIO & chan, unsigned level ) const; - void destroyAllIO ( + void destroyAllIO ( epicsGuard < epicsMutex > &, dbChannelIO & chan ); - void ioCancel ( epicsGuard < epicsMutex > &, + void ioCancel ( epicsGuard < epicsMutex > &, dbChannelIO & chan, const cacChannel::ioid &id ); void ioShow ( epicsGuard < epicsMutex > &, const cacChannel::ioid & id, unsigned level ) const; @@ -201,11 +201,11 @@ private: epics_auto_ptr < cacContext > pNetContext; char * pStateNotifyCache; - cacChannel & createChannel ( + cacChannel & createChannel ( epicsGuard < epicsMutex > &, - const char * pChannelName, cacChannelNotify &, + const char * pChannelName, cacChannelNotify &, cacChannel::priLev ); - void flush ( + void flush ( epicsGuard < epicsMutex > & ); unsigned circuitCount ( epicsGuard < epicsMutex > & ) const; @@ -213,7 +213,7 @@ private: epicsGuard < epicsMutex > & ) const; unsigned beaconAnomaliesSinceProgramStart ( epicsGuard < epicsMutex > & ) const; - void show ( + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; dbContext ( const dbContext & ); @@ -225,17 +225,17 @@ inline dbContextPrivateListOfIO::dbContextPrivateListOfIO () : { } -inline dbContextPrivateListOfIO::~dbContextPrivateListOfIO () +inline dbContextPrivateListOfIO::~dbContextPrivateListOfIO () { assert ( ! this->pBlocker ); } -inline void dbContext::callReadNotify ( - epicsGuard < epicsMutex > & guard, struct dbAddr &addr, +inline void dbContext::callReadNotify ( + epicsGuard < epicsMutex > & guard, struct dbChannel * dbch, unsigned type, unsigned long count, cacReadNotify & notifyIn ) { guard.assertIdenticalMutex ( this-> mutex ); - this->readNotifyCache.callReadNotify ( guard, addr, type, count, notifyIn ); + this->readNotifyCache.callReadNotify ( guard, dbch, type, count, notifyIn ); } #endif // dbCACh diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 22870ba30..fb421af1a 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -411,6 +411,13 @@ int dbCaIsLinkConnected(const struct link *plink) return pca->isConnected; } +void dbCaScanFwdLink(struct link *plink) { + short fwdLinkValue = 1; + + if (plink->value.pv_link.pvlMask & pvlOptFWD) + dbCaPutLink(plink, DBR_SHORT, &fwdLinkValue, 1); +} + #define pcaGetCheck \ assert(plink); \ if (plink->type != CA_LINK) return -1; \ @@ -639,7 +646,7 @@ done: static void eventCallback(struct event_handler_args arg) { caLink *pca = (caLink *)arg.usr; - DBLINK *plink; + struct link *plink; size_t size; dbCommon *precord = 0; struct dbr_time_double *pdbr_time_double; diff --git a/src/ioc/db/dbCa.h b/src/ioc/db/dbCa.h index ee0fee1ca..bf6f2edd9 100644 --- a/src/ioc/db/dbCa.h +++ b/src/ioc/db/dbCa.h @@ -40,6 +40,7 @@ epicsShareFunc long dbCaPutLinkCallback(struct link *plink, #define dbCaPutLink(plink, dbrType, pbuffer, nRequest) \ dbCaPutLinkCallback((plink), (dbrType), (pbuffer), (nRequest), 0, 0) epicsShareFunc int dbCaIsLinkConnected(const struct link *plink); +epicsShareFunc void dbCaScanFwdLink(struct link *plink); /* The following are available after the link is connected*/ epicsShareFunc long dbCaGetNelements(const struct link *plink, diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c new file mode 100644 index 000000000..9305f7da4 --- /dev/null +++ b/src/ioc/db/dbChannel.c @@ -0,0 +1,865 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Andrew Johnson + * Ralph Lange + */ + +#include +#include + +#include "cantProceed.h" +#include "dbChannel.h" +#include "dbCommon.h" +#include "dbBase.h" +#include "dbEvent.h" +#include "link.h" +#include +#include "dbAccessDefs.h" +#include "dbLock.h" +#include "dbStaticLib.h" +#include "epicsAssert.h" +#include "epicsString.h" +#include "errlog.h" +#include "gpHash.h" +#include "recSup.h" +#include "special.h" +#include "yajl_parse.h" + +/* The following is defined in db_convert.h */ +extern unsigned short dbDBRnewToDBRold[DBR_ENUM+1]; + +typedef struct parseContext { + dbChannel *chan; + chFilter *filter; + int depth; +} parseContext; + +#define CALLIF(rtn) !rtn ? parse_stop : rtn + +static void *dbchStringFreeList; + +void dbChannelInit (void) +{ + if (!dbchStringFreeList) { + freeListInitPvt(&dbchStringFreeList, + sizeof(epicsOldString), 128); + } +} + +static void chf_value(parseContext *parser, parse_result *presult) +{ + chFilter *filter = parser->filter; + + if (*presult == parse_stop || parser->depth > 0) + return; + + parser->filter = NULL; + if (filter->plug->fif->parse_end(filter) == parse_continue) { + ellAdd(&parser->chan->filters, &filter->list_node); + } else { + free(filter); /* FIXME: Use free-list */ + *presult = parse_stop; + } +} + +static int chf_null(void * ctx) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + parse_result result; + + assert(filter); + result = CALLIF(filter->plug->fif->parse_null)(filter ); + chf_value(parser, &result); + return result; +} + +static int chf_boolean(void * ctx, int boolVal) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + parse_result result; + + assert(filter); + result = CALLIF(filter->plug->fif->parse_boolean)(filter , boolVal); + chf_value(parser, &result); + return result; +} + +static int chf_integer(void * ctx, long integerVal) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + parse_result result; + + assert(filter); + result = CALLIF(filter->plug->fif->parse_integer)(filter , integerVal); + chf_value(parser, &result); + return result; +} + +static int chf_double(void * ctx, double doubleVal) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + parse_result result; + + assert(filter); + result = CALLIF(filter->plug->fif->parse_double)(filter , doubleVal); + chf_value(parser, &result); + return result; +} + +static int chf_string(void * ctx, const unsigned char * stringVal, + unsigned int stringLen) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + parse_result result; + + assert(filter); + result = CALLIF(filter->plug->fif->parse_string)(filter , (const char *) stringVal, stringLen); + chf_value(parser, &result); + return result; +} + +static int chf_start_map(void * ctx) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + + if (!filter) { + assert(parser->depth == 0); + return parse_continue; /* Opening '{' */ + } + + ++parser->depth; + return CALLIF(filter->plug->fif->parse_start_map)(filter ); +} + +static int chf_map_key(void * ctx, const unsigned char * key, + unsigned int stringLen) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + const chFilterPlugin *plug; + parse_result result; + + if (filter) { + assert(parser->depth > 0); + return CALLIF(filter->plug->fif->parse_map_key)(filter , (const char *) key, stringLen); + } + + assert(parser->depth == 0); + plug = dbFindFilter((const char *) key, stringLen); + if (!plug) { + printf("dbChannelCreate: Channel filter '%.*s' not found\n", (int) stringLen, key); + return parse_stop; + } + + /* FIXME: Use a free-list */ + filter = (chFilter *) callocMustSucceed(1, sizeof(*filter), "Creating dbChannel filter"); + filter->chan = parser->chan; + filter->plug = plug; + filter->puser = NULL; + + result = plug->fif->parse_start(filter); + if (result == parse_continue) { + parser->filter = filter; + } else { + free(filter); /* FIXME: Use free-list */ + } + return result; +} + +static int chf_end_map(void * ctx) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + parse_result result; + + if (!filter) { + assert(parser->depth == 0); + return parse_continue; /* Final closing '}' */ + } + + assert(parser->depth > 0); + result = CALLIF(filter->plug->fif->parse_end_map)(filter ); + + --parser->depth; + chf_value(parser, &result); + return result; +} + +static int chf_start_array(void * ctx) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + + assert(filter); + ++parser->depth; + return CALLIF(filter->plug->fif->parse_start_array)(filter ); +} + +static int chf_end_array(void * ctx) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + parse_result result; + + assert(filter); + result = CALLIF(filter->plug->fif->parse_end_array)(filter ); + --parser->depth; + chf_value(parser, &result); + return result; +} + +static const yajl_callbacks chf_callbacks = + { chf_null, chf_boolean, chf_integer, chf_double, NULL, chf_string, + chf_start_map, chf_map_key, chf_end_map, chf_start_array, chf_end_array }; + +static const yajl_parser_config chf_config = + { 0, 1 }; /* allowComments = NO , checkUTF8 = YES */ + +static void * chf_malloc(void *ctx, unsigned int sz) +{ + return malloc(sz); /* FIXME: free-list */ +} + +static void * chf_realloc(void *ctx, void *ptr, unsigned int sz) +{ + return realloc(ptr, sz); /* FIXME: free-list */ +} + +static void chf_free(void *ctx, void *ptr) +{ + return free(ptr); /* FIXME: free-list */ +} + +static const yajl_alloc_funcs chf_alloc = + { chf_malloc, chf_realloc, chf_free }; + +static long chf_parse(dbChannel *chan, const char **pjson) +{ + parseContext parser = + { chan, NULL, 0 }; + yajl_handle yh = yajl_alloc(&chf_callbacks, &chf_config, &chf_alloc, &parser); + const char *json = *pjson; + size_t jlen = strlen(json); + yajl_status ys; + long status; + + if (!yh) + return S_db_noMemory; + + ys = yajl_parse(yh, (const unsigned char *) json, jlen); + if (ys == yajl_status_insufficient_data) + ys = yajl_parse_complete(yh); + + switch (ys) { + case yajl_status_ok: + status = 0; + *pjson += yajl_get_bytes_consumed(yh); + break; + + case yajl_status_error: { + unsigned char *err; + + err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen); + printf("dbChannelCreate: %s\n", err); + yajl_free_error(yh, err); + } /* fall through */ + default: + status = S_db_notFound; + } + + if (parser.filter) { + assert(status); + parser.filter->plug->fif->parse_abort(parser.filter); + free(parser.filter); /* FIXME: free-list */ + } + yajl_free(yh); + return status; +} + +static long pvNameLookup(DBENTRY *pdbe, const char **ppname) +{ + long status; + + dbInitEntry(pdbbase, pdbe); + + status = dbFindRecordPart(pdbe, ppname); + if (status) + return status; + + if (**ppname == '.') + ++*ppname; + + status = dbFindFieldPart(pdbe, ppname); + if (status == S_dbLib_fieldNotFound) + status = dbGetAttributePart(pdbe, ppname); + + return status; +} + +long dbChannelTest(const char *name) +{ + DBENTRY dbEntry; + long status; + + if (!name || !*name || !pdbbase) + return S_db_notFound; + + status = pvNameLookup(&dbEntry, &name); + + dbFinishEntry(&dbEntry); + return status; +} + +#define TRY(Func, Arg) \ +if (Func) { \ + result = Func Arg; \ + if (result != parse_continue) goto failure; \ +} + +static long parseArrayRange(dbChannel* chan, const char *pname, const char **ppnext) { + epicsInt32 start = 0; + epicsInt32 end = -1; + epicsInt32 incr = 1; + epicsInt32 l; + char *pnext; + short exist; + chFilter *filter; + const chFilterPlugin *plug; + parse_result result; + long status = 0; + + /* If no number is present, strtol() returns 0 and sets pnext=pname, + else pnext points to the first char after the number */ + pname++; + l = strtol(pname, &pnext, 0); + exist = pnext - pname; + if (exist) start = l; + pname = pnext; + if (*pname == ']' && exist) { + end = start; + goto insertplug; + } + if (*pname != ':') { + status = S_dbLib_fieldNotFound; + goto finish; + } + pname++; + l = strtol(pname, &pnext, 0); + exist = pnext - pname; + pname = pnext; + if (*pname == ']') { + if (exist) end = l; + goto insertplug; + } + if (exist) incr = l; + if (*pname != ':') { + status = S_dbLib_fieldNotFound; + goto finish; + } + pname++; + l = strtol(pname, &pnext, 0); + exist = pnext - pname; + if (exist) end = l; + pname = pnext; + if (*pname != ']') { + status = S_dbLib_fieldNotFound; + goto finish; + } + + insertplug: + pname++; + *ppnext = pname; + + plug = dbFindFilter("arr", 3); + if (!plug) { + status = S_dbLib_fieldNotFound; + goto finish; + } + + /* FIXME: Use a free-list */ + filter = (chFilter *) callocMustSucceed(1, sizeof(*filter), "Creating 'arr' dbChannel filter"); + filter->chan = chan; + filter->plug = plug; + filter->puser = NULL; + + TRY(filter->plug->fif->parse_start, (filter)); + TRY(filter->plug->fif->parse_start_map, (filter)); + if (start != 0) { + TRY(filter->plug->fif->parse_map_key, (filter, "s", 1)); + TRY(filter->plug->fif->parse_integer, (filter, start)); + } + if (incr != 1) { + TRY(filter->plug->fif->parse_map_key, (filter, "i", 1)); + TRY(filter->plug->fif->parse_integer, (filter, incr)); + } + if (end != -1) { + TRY(filter->plug->fif->parse_map_key, (filter, "e", 1)); + TRY(filter->plug->fif->parse_integer, (filter, end)); + } + TRY(filter->plug->fif->parse_end_map, (filter)); + TRY(filter->plug->fif->parse_end, (filter)); + + ellAdd(&chan->filters, &filter->list_node); + return 0; + + failure: + free(filter); /* FIXME: Use free-list */ + status = S_dbLib_fieldNotFound; + + finish: + return status; +} + +/* Stolen from dbAccess.c: */ +static short mapDBFToDBR[DBF_NTYPES] = + { + /* DBF_STRING => */DBR_STRING, + /* DBF_CHAR => */DBR_CHAR, + /* DBF_UCHAR => */DBR_UCHAR, + /* DBF_SHORT => */DBR_SHORT, + /* DBF_USHORT => */DBR_USHORT, + /* DBF_LONG => */DBR_LONG, + /* DBF_ULONG => */DBR_ULONG, + /* DBF_FLOAT => */DBR_FLOAT, + /* DBF_DOUBLE => */DBR_DOUBLE, + /* DBF_ENUM, => */DBR_ENUM, + /* DBF_MENU, => */DBR_ENUM, + /* DBF_DEVICE => */DBR_ENUM, + /* DBF_INLINK => */DBR_STRING, + /* DBF_OUTLINK => */DBR_STRING, + /* DBF_FWDLINK => */DBR_STRING, + /* DBF_NOACCESS => */DBR_NOACCESS }; + +dbChannel * dbChannelCreate(const char *name) +{ + const char *pname = name; + DBENTRY dbEntry; + dbChannel *chan = NULL; + dbAddr *paddr; + dbFldDes *pflddes; + long status; + short dbfType; + + if (!name || !*name || !pdbbase) + return NULL; + + status = pvNameLookup(&dbEntry, &pname); + if (status) + goto finish; + + /* FIXME: Use free-list */ + chan = (dbChannel *) callocMustSucceed(1, sizeof(*chan), "dbChannelCreate"); + chan->name = epicsStrDup(name); /* FIXME: free-list */ + ellInit(&chan->filters); + ellInit(&chan->pre_chain); + ellInit(&chan->post_chain); + + paddr = &chan->addr; + pflddes = dbEntry.pflddes; + dbfType = pflddes->field_type; + + paddr->precord = dbEntry.precnode->precord; + paddr->pfield = dbEntry.pfield; + 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]; + + /* Handle field modifiers */ + if (*pname) { + if (*pname == '$') { + /* Some field types can be accessed as char arrays */ + if (dbfType == DBF_STRING) { + paddr->no_elements = pflddes->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 */ + paddr->no_elements = PVNAME_STRINGSZ + 12; + paddr->field_size = 1; + paddr->dbr_field_type = DBR_CHAR; + } else { + status = S_dbLib_fieldNotFound; + goto finish; + } + pname++; + } + + if (*pname == '[') { + status = parseArrayRange(chan, pname, &pname); + if (status) goto finish; + } + + /* JSON may follow */ + if (*pname == '{') { + status = chf_parse(chan, &pname); + if (status) goto finish; + } + + /* Make sure there's nothing else */ + if (*pname) { + status = S_dbLib_fieldNotFound; + goto finish; + } + } + + if (paddr->special == SPC_DBADDR) { + struct rset *prset = dbGetRset(paddr); + + /* Let record type modify the dbAddr */ + if (prset && prset->cvt_dbaddr) { + status = prset->cvt_dbaddr(paddr); + if (status) goto finish; + } + } + +finish: + if (status && chan) { + dbChannelDelete(chan); + chan = NULL; + } + dbFinishEntry(&dbEntry); + return chan; +} + +db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn) { + chFilter *filter; + ELLNODE *node; + db_field_log *pLog = pLogIn; + + for (node = ellFirst(&chan->pre_chain); node && pLog; node = ellNext(node)) { + filter = CONTAINER(node, chFilter, pre_node); + pLog = filter->pre_func(filter->pre_arg, chan, pLog); + } + return pLog; +} + +db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn) { + chFilter *filter; + ELLNODE *node; + db_field_log *pLog = pLogIn; + + for (node = ellFirst(&chan->post_chain); node && pLog; node = ellNext(node)) { + filter = CONTAINER(node, chFilter, post_node); + pLog = filter->post_func(filter->post_arg, chan, pLog); + } + return pLog; +} + +long dbChannelOpen(dbChannel *chan) +{ + chFilter *filter; + chPostEventFunc *func; + void *arg; + long status; + ELLNODE *node; + db_field_log probe; + db_field_log p; + + for (node = ellFirst(&chan->filters); node; node = ellNext(node)) { + filter = CONTAINER(node, chFilter, list_node); + /* Call channel_open */ + status = 0; + if (filter->plug->fif->channel_open) + status = filter->plug->fif->channel_open(filter); + if (status) return status; + } + + /* Set up type probe */ + probe.field_type = dbChannelFieldType(chan); + probe.no_elements = dbChannelElements(chan); + probe.field_size = dbChannelFieldSize(chan); + p = probe; + + /* + * Build up the pre- and post-event-queue filter chains + * Separate loops because the probe must reach the filters in the right order. + */ + for (node = ellFirst(&chan->filters); node; node = ellNext(node)) { + filter = CONTAINER(node, chFilter, list_node); + func = NULL; + arg = NULL; + if (filter->plug->fif->channel_register_pre) { + filter->plug->fif->channel_register_pre(filter, &func, &arg, &p); + if (func) { + ellAdd(&chan->pre_chain, &filter->pre_node); + filter->pre_func = func; + filter->pre_arg = arg; + probe = p; + } + } + } + for (node = ellFirst(&chan->filters); node; node = ellNext(node)) { + filter = CONTAINER(node, chFilter, list_node); + func = NULL; + arg = NULL; + if (filter->plug->fif->channel_register_post) { + filter->plug->fif->channel_register_post(filter, &func, &arg, &p); + if (func) { + ellAdd(&chan->post_chain, &filter->post_node); + filter->post_func = func; + filter->post_arg = arg; + probe = p; + } + } + } + + /* Save probe results */ + chan->final_no_elements = probe.no_elements; + chan->final_field_size = probe.field_size; + chan->final_type = probe.field_type; + chan->dbr_final_type = dbDBRnewToDBRold[mapDBFToDBR[probe.field_type]]; + + return 0; +} + +/* FIXME: For performance we should make these one-liners into macros, + * or try to make them inline if all our compilers can do that. + */ +const char * dbChannelName(dbChannel *chan) +{ + return chan->name; +} + +struct dbCommon * dbChannelRecord(dbChannel *chan) +{ + return chan->addr.precord; +} + +struct dbFldDes * dbChannelFldDes(dbChannel *chan) +{ + return chan->addr.pfldDes; +} + +long dbChannelElements(dbChannel *chan) +{ + return chan->addr.no_elements; +} + +short dbChannelFieldType(dbChannel *chan) +{ + return chan->addr.field_type; +} + +short dbChannelExportType(dbChannel *chan) +{ + return chan->addr.dbr_field_type; +} + +short dbChannelFieldSize(dbChannel *chan) +{ + return chan->addr.field_size; +} + +long dbChannelFinalElements(dbChannel *chan) +{ + return chan->final_no_elements; +} + +short dbChannelFinalFieldType(dbChannel *chan) +{ + return chan->final_type; +} + +short dbChannelFinalExportType(dbChannel *chan) +{ + return chan->dbr_final_type; +} + +short dbChannelFinalFieldSize(dbChannel *chan) +{ + return chan->final_field_size; +} + +short dbChannelSpecial(dbChannel *chan) +{ + return chan->addr.special; +} + +void * dbChannelField(dbChannel *chan) +{ + /* Channel filters do not get to interpose here since there are many + * places where the field pointer is compared with the address of a + * specific record field, so they can't modify the pointer value. + */ + return chan->addr.pfield; +} + +/* Only use dbChannelGet() if the record is already locked. */ +long dbChannelGet(dbChannel *chan, short type, void *pbuffer, + long *options, long *nRequest, void *pfl) +{ + /* FIXME: Vector through chan->get() ? */ + return dbGet(&chan->addr, type, pbuffer, options, nRequest, pfl); +} + +long dbChannelGetField(dbChannel *chan, short dbrType, void *pbuffer, + long *options, long *nRequest, void *pfl) +{ + dbCommon *precord = chan->addr.precord; + long status = 0; + + dbScanLock(precord); + status = dbChannelGet(chan, dbrType, pbuffer, options, nRequest, pfl); + dbScanUnlock(precord); + return status; +} + +/* Only use dbChannelPut() if the record is already locked. + * This routine doesn't work on link fields, ignores DISP, and + * doesn't trigger record processing on PROC or pp(TRUE). + */ +long dbChannelPut(dbChannel *chan, short type, const void *pbuffer, + long nRequest) +{ + /* FIXME: Vector through chan->put() ? */ + return dbPut(&chan->addr, type, pbuffer, nRequest); +} + +long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer, + long nRequest) +{ + /* FIXME: Vector through chan->putField() ? */ + return dbPutField(&chan->addr, type, pbuffer, nRequest); +} + +void dbChannelShow(dbChannel *chan, int level, const unsigned short indent) +{ + long elems = chan->addr.no_elements; + long felems = chan->final_no_elements; + int count = ellCount(&chan->filters); + int pre = ellCount(&chan->pre_chain); + int post = ellCount(&chan->post_chain); + + printf("%*schannel name: %s\n", indent, "", chan->name); + /* FIXME: show field_type as text */ + printf("%*s field_type=%d (%dB), %ld element%s, %d filter%s", indent, "", + chan->addr.field_type, chan->addr.field_size, elems, elems == 1 ? "" : "s", + count, count == 1 ? "" : "s"); + if (count) + printf(" (%d pre eventq, %d post eventq)\n", pre, post); + else + printf("\n"); + if (level > 0) + dbChannelFilterShow(chan, level - 1, indent + 2); + if (count) { + /* FIXME: show field_type as text */ + printf("%*s final field_type=%d (%dB), %ld element%s\n", indent, "", + chan->final_type, chan->final_field_size, felems, felems == 1 ? "" : "s"); + } +} + +void dbChannelFilterShow(dbChannel *chan, int level, const unsigned short indent) +{ + chFilter *filter = (chFilter *) ellFirst(&chan->filters); + while (filter) { + filter->plug->fif->channel_report(filter, level, indent); + filter = (chFilter *) ellNext(&filter->list_node); + } +} + +void dbChannelDelete(dbChannel *chan) +{ + chFilter *filter; + + /* Close filters in reverse order */ + while ((filter = (chFilter *) ellPop(&chan->filters))) { + filter->plug->fif->channel_close(filter); + free(filter); + } + free((char *) chan->name); /* FIXME: Use free-list */ + free(chan); /* FIXME: Use free-list */ +} + +static void freeArray(db_field_log *pfl) { + if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) { + freeListFree(dbchStringFreeList, pfl->u.r.field); + } else { + free(pfl->u.r.field); + } +} + +void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan) +{ + void *p; + struct dbCommon *prec = dbChannelRecord(chan); + + if (!pfl->type == dbfl_type_rec) return; + + pfl->type = dbfl_type_ref; + pfl->stat = prec->stat; + pfl->sevr = prec->sevr; + pfl->time = prec->time; + pfl->field_type = chan->addr.field_type; + pfl->no_elements = chan->addr.no_elements; + pfl->field_size = chan->addr.field_size; + pfl->u.r.dtor = freeArray; + pfl->u.r.pvt = pvt; + if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) { + p = freeListCalloc(dbchStringFreeList); + } else { + p = calloc(pfl->no_elements, pfl->field_size); + } + if (p) dbGet(&chan->addr, mapDBFToDBR[pfl->field_type], p, NULL, &pfl->no_elements, NULL); + pfl->u.r.field = p; +} + +/* FIXME: Do these belong in a different file? */ + +void dbRegisterFilter(const char *name, const chFilterIf *fif, void *puser) +{ + GPHENTRY *pgph; + chFilterPlugin *pfilt; + + if (!pdbbase) { + printf("dbRegisterFilter: pdbbase not set!\n"); + return; + } + + pgph = gphFind(pdbbase->pgpHash, name, &pdbbase->filterList); + if (pgph) + return; + + pfilt = dbCalloc(1, sizeof(chFilterPlugin)); + pfilt->name = epicsStrDup(name); + pfilt->fif = fif; + pfilt->puser = puser; + + ellAdd(&pdbbase->filterList, &pfilt->node); + pgph = gphAdd(pdbbase->pgpHash, pfilt->name, &pdbbase->filterList); + if (!pgph) { + free((void *) pfilt->name); + free(pfilt); + printf("dbRegisterFilter: gphAdd failed\n"); + return; + } + pgph->userPvt = pfilt; +} + +const chFilterPlugin * dbFindFilter(const char *name, size_t len) +{ + GPHENTRY *pgph = gphFindParse(pdbbase->pgpHash, name, len, + &pdbbase->filterList); + + if (!pgph) + return NULL; + return (chFilterPlugin *) pgph->userPvt; +} diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h new file mode 100644 index 000000000..2e50185aa --- /dev/null +++ b/src/ioc/db/dbChannel.h @@ -0,0 +1,181 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Andrew Johnson + * Ralph Lange + */ + +#ifndef INC_dbChannel_H +#define INC_dbChannel_H + +#include "dbDefs.h" +#include "dbAddr.h" +#include "ellLib.h" +#include "epicsTypes.h" +#include "errMdef.h" +#include "shareLib.h" +#include "db_field_log.h" +#include "dbEvent.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * event subscription + */ +typedef struct evSubscrip { + ELLNODE node; + struct dbChannel *chan; + EVENTFUNC *user_sub; + void *user_arg; + struct event_que *ev_que; + db_field_log **pLastLog; + unsigned long npend; /* n times this event is on the queue */ + unsigned long nreplace; /* n times replacing event on the queue */ + unsigned char select; + char useValque; + char callBackInProgress; + char enabled; +} evSubscrip; + +typedef struct chFilter chFilter; + +/* A dbChannel points to a record field, and can have multiple filters */ +typedef struct dbChannel { + const char *name; + dbAddr addr; /* address structure for record/field */ + long final_no_elements; /* final number of elements (arrays) */ + short final_field_size; /* final size of element */ + short final_type; /* final type of database field */ + short dbr_final_type; /* final field type as seen by database request */ + ELLLIST filters; /* list of filters as created from JSON */ + ELLLIST pre_chain; /* list of filters to be called pre-event-queue */ + ELLLIST post_chain; /* list of filters to be called post-event-queue */ +} dbChannel; + +/* Prototype for the post event function that is called in filter stacks */ +typedef db_field_log* (chPostEventFunc)(void *pvt, dbChannel *chan, db_field_log *pLog); + +/* Return values from chFilterIf->parse_* routines: */ +typedef enum { + parse_stop, parse_continue +} parse_result; + +/* These routines must be implemented by each filter plug-in */ +typedef struct chFilterIf { + /* Parsing event handlers: */ + parse_result (* parse_start)(chFilter *filter); + /* If parse_start() returns parse_continue for a filter, one of + * parse_abort() or parse_end() will later be called for that same + * filter. + */ + void (* parse_abort)(chFilter *filter); + /* If parse_abort() is called it should release any memory allocated + * for this filter; no further parse_...() calls will be made; + */ + parse_result (* parse_end)(chFilter *filter); + /* If parse_end() returns parse_stop it should have released any + * memory allocated for this filter; no further parse_...() calls will + * be made in this case. + */ + + parse_result (* parse_null)(chFilter *filter); + parse_result (* parse_boolean)(chFilter *filter, int boolVal); + parse_result (* parse_integer)(chFilter *filter, long integerVal); + parse_result (* parse_double)(chFilter *filter, double doubleVal); + parse_result (* parse_string)(chFilter *filter, const char *stringVal, + size_t stringLen); /* NB: stringVal is not zero-terminated: */ + + parse_result (* parse_start_map)(chFilter *filter); + parse_result (* parse_map_key)(chFilter *filter, const char *key, + size_t stringLen); /* NB: key is not zero-terminated: */ + parse_result (* parse_end_map)(chFilter *filter); + + parse_result (* parse_start_array)(chFilter *filter); + parse_result (* parse_end_array)(chFilter *filter); + + /* Channel operations: */ + long (* channel_open)(chFilter *filter); + void (* channel_register_pre) (chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); + void (* channel_register_post)(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); + void (* channel_report)(chFilter *filter, int level, const unsigned short indent); + /* FIXME: More filter routines here ... */ + void (* channel_close)(chFilter *filter); +} chFilterIf; + +/* A chFilterPlugin holds data for a filter plugin */ +typedef struct chFilterPlugin { + ELLNODE node; + const char *name; + const chFilterIf *fif; + void *puser; +} chFilterPlugin; + +/* A chFilter holds data for a single filter instance */ +struct chFilter { + ELLNODE list_node; + ELLNODE pre_node; + ELLNODE post_node; + dbChannel *chan; + const chFilterPlugin *plug; + chPostEventFunc *pre_func; + void *pre_arg; + chPostEventFunc *post_func; + void *post_arg; + void *puser; +}; + +struct dbCommon; +struct dbFldDes; + +epicsShareFunc void dbChannelInit (void); +epicsShareFunc long dbChannelTest(const char *name); +epicsShareFunc dbChannel * dbChannelCreate(const char *name); +epicsShareFunc long dbChannelOpen(dbChannel *chan); +epicsShareFunc const char * dbChannelName(dbChannel *chan); +epicsShareFunc struct dbCommon * dbChannelRecord(dbChannel *chan); +epicsShareFunc struct dbFldDes * dbChannelFldDes(dbChannel *chan); +epicsShareFunc long dbChannelElements(dbChannel *chan); +epicsShareFunc short dbChannelFieldType(dbChannel *chan); +epicsShareFunc short dbChannelExportType(dbChannel *chan); +epicsShareFunc short dbChannelFieldSize(dbChannel *chan); +epicsShareFunc long dbChannelFinalElements(dbChannel *chan); +epicsShareFunc short dbChannelFinalFieldType(dbChannel *chan); +epicsShareFunc short dbChannelFinalExportType(dbChannel *chan); +epicsShareFunc short dbChannelFinalElementSize(dbChannel *chan); +epicsShareFunc short dbChannelSpecial(dbChannel *chan); +epicsShareFunc void * dbChannelField(dbChannel *chan); +epicsShareFunc long dbChannelGet(dbChannel *chan, short type, + void *pbuffer, long *options, long *nRequest, void *pfl); +epicsShareFunc long dbChannelGetField(dbChannel *chan, short type, + void *pbuffer, long *options, long *nRequest, void *pfl); +epicsShareFunc long dbChannelPut(dbChannel *chan, short type, + const void *pbuffer, long nRequest); +epicsShareFunc long dbChannelPutField(dbChannel *chan, short type, + const void *pbuffer, long nRequest); +epicsShareFunc void dbChannelShow(dbChannel *chan, int level, + const unsigned short indent); +epicsShareFunc void dbChannelFilterShow(dbChannel *chan, int level, + const unsigned short indent); +epicsShareFunc void dbChannelDelete(dbChannel *chan); + +epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif, void *puser); +epicsShareFunc db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn); +epicsShareFunc db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn); +epicsShareFunc const chFilterPlugin * dbFindFilter(const char *key, size_t len); +epicsShareFunc void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_dbChannel_H */ diff --git a/src/ioc/db/dbChannelIO.cpp b/src/ioc/db/dbChannelIO.cpp index c9d16f53e..bdf7e459f 100644 --- a/src/ioc/db/dbChannelIO.cpp +++ b/src/ioc/db/dbChannelIO.cpp @@ -4,20 +4,20 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos 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. \*************************************************************************/ -/* +/* * $Revision-Id$ * - * + * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 - * + * * Copyright, 1986, The Regents of the University of California. - * - * + * + * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 @@ -40,15 +40,12 @@ #include "dbChannelIO.h" #include "dbPutNotifyBlocker.h" -dbChannelIO::dbChannelIO ( - epicsMutex & mutexIn, cacChannelNotify & notify, - const dbAddr & addrIn, dbContext & serviceIO ) : - cacChannel ( notify ), mutex ( mutexIn ), serviceIO ( serviceIO ), - addr ( addrIn ) +dbChannelIO::dbChannelIO ( + epicsMutex & mutexIn, cacChannelNotify & notify, + dbChannel * dbchIn, dbContext & serviceIO ) : + cacChannel ( notify ), mutex ( mutexIn ), serviceIO ( serviceIO ), + dbch ( dbchIn ) { - unsigned bufLen = dbNameSizeOfPV ( & this->addr ) + 1; - this->pNameStr.reset ( new char [ bufLen ] ); - dbNameOfPV ( & this->addr, this->pNameStr.get (), bufLen ); } void dbChannelIO::initiateConnect ( epicsGuard < epicsMutex > & guard ) @@ -57,7 +54,7 @@ void dbChannelIO::initiateConnect ( epicsGuard < epicsMutex > & guard ) this->notify().connectNotify ( guard ); } -dbChannelIO::~dbChannelIO () +dbChannelIO::~dbChannelIO () { } @@ -65,48 +62,49 @@ void dbChannelIO::destructor ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); this->serviceIO.destroyAllIO ( guard, *this ); + dbChannelDelete ( this->dbch ); this->~dbChannelIO (); } -void dbChannelIO::destroy ( +void dbChannelIO::destroy ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); this->serviceIO.destroyChannel ( guard, *this ); - // dont access this pointer after above call because - // object nolonger exists + // don't access this pointer after above call because + // object no longer exists } -cacChannel::ioStatus dbChannelIO::read ( - epicsGuard < epicsMutex > & guard, unsigned type, - unsigned long count, cacReadNotify & notify, ioid * ) +cacChannel::ioStatus dbChannelIO::read ( + epicsGuard < epicsMutex > & guard, unsigned type, + unsigned long count, cacReadNotify & notify, ioid * ) { guard.assertIdenticalMutex ( this->mutex ); - this->serviceIO.callReadNotify ( guard, this->addr, + this->serviceIO.callReadNotify ( guard, this->dbch, type, count, notify ); return iosSynch; } -void dbChannelIO::write ( - epicsGuard < epicsMutex > & guard, unsigned type, +void dbChannelIO::write ( + epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, const void *pValue ) { epicsGuardRelease < epicsMutex > unguard ( guard ); if ( count > LONG_MAX ) { throw outOfBounds(); } - int status = db_put_field ( &this->addr, type, pValue, + int status = dbChannel_put ( this->dbch, type, pValue, static_cast (count) ); if ( status ) { - throw std::logic_error ( + throw std::logic_error ( "db_put_field() completed unsuccessfully" ); } } -cacChannel::ioStatus dbChannelIO::write ( - epicsGuard < epicsMutex > & guard, unsigned type, - unsigned long count, const void * pValue, - cacWriteNotify & notify, ioid * pId ) +cacChannel::ioStatus dbChannelIO::write ( + epicsGuard < epicsMutex > & guard, unsigned type, + unsigned long count, const void * pValue, + cacWriteNotify & notify, ioid * pId ) { guard.assertIdenticalMutex ( this->mutex ); @@ -114,24 +112,24 @@ cacChannel::ioStatus dbChannelIO::write ( throw outOfBounds(); } - this->serviceIO.initiatePutNotify ( - guard, *this, this->addr, + this->serviceIO.initiatePutNotify ( + guard, *this, this->dbch, type, count, pValue, notify, pId ); return iosAsynch; } -void dbChannelIO::subscribe ( - epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, - unsigned mask, cacStateNotify & notify, ioid * pId ) -{ +void dbChannelIO::subscribe ( + epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, + unsigned mask, cacStateNotify & notify, ioid * pId ) +{ guard.assertIdenticalMutex ( this->mutex ); - this->serviceIO.subscribe ( - guard, this->addr, *this, + this->serviceIO.subscribe ( + guard, this->dbch, *this, type, count, mask, notify, pId ); } -void dbChannelIO::ioCancel ( +void dbChannelIO::ioCancel ( epicsGuard < epicsMutex > & mutualExclusionGuard, const ioid & id ) { @@ -139,7 +137,7 @@ void dbChannelIO::ioCancel ( this->serviceIO.ioCancel ( mutualExclusionGuard, *this, id ); } -void dbChannelIO::ioShow ( +void dbChannelIO::ioShow ( epicsGuard < epicsMutex > & guard, const ioid & id, unsigned level ) const { @@ -147,31 +145,35 @@ void dbChannelIO::ioShow ( this->serviceIO.ioShow ( guard, id, level ); } -void dbChannelIO::show ( +void dbChannelIO::show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { guard.assertIdenticalMutex ( this->mutex ); - printf ("channel at %p attached to local database record %s\n", - static_cast ( this ), this->addr.precord->name ); + printf ("channel at %p attached to local database record %s\n", + static_cast ( this ), + dbChannelRecord ( this->dbch ) -> name ); if ( level > 0u ) { - printf ( "\ttype %s, element count %li, field at %p\n", - dbf_type_to_text ( this->addr.dbr_field_type ), this->addr.no_elements, - this->addr.pfield ); - } - if ( level > 1u ) { - this->serviceIO.show ( level - 2u ); - this->serviceIO.showAllIO ( *this, level - 2u ); + printf ( " type %s, element count %li, field at %p\n", + dbf_type_to_text ( dbChannelExportType ( this->dbch ) ), + dbChannelElements ( this->dbch ), + dbChannelField ( this->dbch ) ); + if ( level > 1u ) { + dbChannelFilterShow ( this->dbch, level - 2u, 8 ); + this->serviceIO.show ( level - 2u ); + this->serviceIO.showAllIO ( *this, level - 2u ); + } } } unsigned long dbChannelIO::nativeElementCount ( - epicsGuard < epicsMutex > & guard ) const + epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->mutex ); - if ( this->addr.no_elements >= 0u ) { - return static_cast < unsigned long > ( this->addr.no_elements ); + long elements = dbChannelElements ( this->dbch ); + if ( elements >= 0u ) { + return static_cast < unsigned long > ( elements ); } return 0u; } @@ -181,31 +183,37 @@ const char * dbChannelIO::pName ( epicsGuard < epicsMutex > & guard ) const throw () { guard.assertIdenticalMutex ( this->mutex ); - return this->pNameStr.get (); + return dbChannelName ( this->dbch ); } unsigned dbChannelIO::getName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLen ) const throw () { - return dbNameOfPV ( & this->addr, pBuf, bufLen ); + const char *name = dbChannelName ( this->dbch ); + size_t len = strlen ( name ); + strncpy ( pBuf, name, bufLen ); + if (len < bufLen) + return len; + pBuf[--bufLen] = '\0'; + return bufLen; } short dbChannelIO::nativeType ( - epicsGuard < epicsMutex > & guard ) const + epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->mutex ); - return this->addr.dbr_field_type; + return dbChannelExportType( this->dbch ); } -void * dbChannelIO::operator new ( size_t size, +void * dbChannelIO::operator new ( size_t size, tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE -void dbChannelIO::operator delete ( void *pCadaver, +void dbChannelIO::operator delete ( void *pCadaver, tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); diff --git a/src/ioc/db/dbChannelIO.h b/src/ioc/db/dbChannelIO.h index b0398408e..bdb92cc5b 100644 --- a/src/ioc/db/dbChannelIO.h +++ b/src/ioc/db/dbChannelIO.h @@ -5,7 +5,7 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are 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. \*************************************************************************/ /* @@ -44,65 +44,64 @@ class dbChannelIO : public cacChannel, public dbContextPrivateListOfIO { public: - dbChannelIO ( - epicsMutex &, cacChannelNotify &, - const dbAddr &, dbContext & ); - void destructor ( + dbChannelIO ( + epicsMutex &, cacChannelNotify &, + dbChannel *, dbContext & ); + void destructor ( epicsGuard < epicsMutex > & ); void destroy ( epicsGuard < epicsMutex > & mutualExclusionGuard ); - void callReadNotify ( - epicsGuard < epicsMutex > &, - unsigned type, unsigned long count, + void callReadNotify ( + epicsGuard < epicsMutex > &, + unsigned type, unsigned long count, cacReadNotify & notify ); - void callStateNotify ( - unsigned type, unsigned long count, + void callStateNotify ( + unsigned type, unsigned long count, const struct db_field_log * pfl, cacStateNotify & notify ); - void show ( + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; unsigned getName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLen ) const throw (); const char * pName ( epicsGuard < epicsMutex > & ) const throw (); - void * operator new ( size_t size, + void * operator new ( size_t size, tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & ); - epicsPlacementDeleteOperator (( void *, + epicsPlacementDeleteOperator (( void *, tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & )) protected: ~dbChannelIO (); private: epicsMutex & mutex; dbContext & serviceIO; - dbAddr addr; - epics_auto_ptr < char, eapt_array > pNameStr; + dbChannel * dbch; void initiateConnect ( epicsGuard < epicsMutex > & ); - unsigned requestMessageBytesPending ( + unsigned requestMessageBytesPending ( epicsGuard < epicsMutex > & ); - void flush ( + void flush ( epicsGuard < epicsMutex > & ); - ioStatus read ( + ioStatus read ( epicsGuard < epicsMutex > &, - unsigned type, unsigned long count, + unsigned type, unsigned long count, cacReadNotify &, ioid * ); - void write ( + void write ( epicsGuard < epicsMutex > &, - unsigned type, unsigned long count, + unsigned type, unsigned long count, const void * pvalue ); - ioStatus write ( + ioStatus write ( epicsGuard < epicsMutex > &, - unsigned type, unsigned long count, + unsigned type, unsigned long count, const void * pvalue, cacWriteNotify &, ioid * ); - void subscribe ( + void subscribe ( epicsGuard < epicsMutex > &, - unsigned type, unsigned long count, + unsigned type, unsigned long count, unsigned mask, cacStateNotify ¬ify, ioid * ); - void ioCancel ( + void ioCancel ( epicsGuard < epicsMutex > & mutualExclusionGuard, const ioid & ); - void ioShow ( + void ioShow ( epicsGuard < epicsMutex > &, const ioid &, unsigned level ) const; short nativeType ( @@ -114,18 +113,18 @@ private: void operator delete ( void * ); }; -inline void dbChannelIO::callReadNotify ( - epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, +inline void dbChannelIO::callReadNotify ( + epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, cacReadNotify & notify ) { guard.assertIdenticalMutex ( this->mutex ); - this->serviceIO.callReadNotify ( guard, this->addr, type, count, notify ); + this->serviceIO.callReadNotify ( guard, this->dbch, type, count, notify ); } -inline void dbChannelIO::callStateNotify ( unsigned type, unsigned long count, +inline void dbChannelIO::callStateNotify ( unsigned type, unsigned long count, const struct db_field_log *pfl, cacStateNotify ¬ify ) { - this->serviceIO.callStateNotify ( this->addr, type, count, pfl, notify ); + this->serviceIO.callStateNotify ( this->dbch, type, count, pfl, notify ); } diff --git a/src/ioc/db/dbContext.cpp b/src/ioc/db/dbContext.cpp index 4a14dc90b..8b482b13e 100644 --- a/src/ioc/db/dbContext.cpp +++ b/src/ioc/db/dbContext.cpp @@ -4,19 +4,19 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos 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. \*************************************************************************/ -/* +/* * $Revision-Id$ * - * + * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 - * + * * Copyright, 1986, The Regents of the University of California. - * - * + * + * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 @@ -33,6 +33,7 @@ #include "epicsEvent.h" #include "epicsThread.h" #include "errlog.h" +#include "dbChannel.h" #define epicsExportSharedSymbols #include "db_access_routines.h" @@ -43,20 +44,20 @@ class dbService : public cacService { public: ~dbService () {} - cacContext & contextCreate ( - epicsMutex & mutualExclusion, - epicsMutex & callbackControl, + cacContext & contextCreate ( + epicsMutex & mutualExclusion, + epicsMutex & callbackControl, cacContextNotify & ); }; static dbService dbs; -cacContext & dbService::contextCreate ( - epicsMutex & mutualExclusion, - epicsMutex & callbackControl, +cacContext & dbService::contextCreate ( + epicsMutex & mutualExclusion, + epicsMutex & callbackControl, cacContextNotify & notify ) { - return * new dbContext ( callbackControl, + return * new dbContext ( callbackControl, mutualExclusion, notify ); } @@ -67,9 +68,9 @@ extern "C" void dbServiceIOInit () dbBaseIO::dbBaseIO () {} -dbContext::dbContext ( epicsMutex & cbMutexIn, +dbContext::dbContext ( epicsMutex & cbMutexIn, epicsMutex & mutexIn, cacContextNotify & notifyIn ) : - readNotifyCache ( mutexIn ), ctx ( 0 ), + readNotifyCache ( mutexIn ), ctx ( 0 ), stateNotifyCacheSize ( 0 ), mutex ( mutexIn ), cbMutex ( cbMutexIn ), notify ( notifyIn ), pNetContext ( 0 ), pStateNotifyCache ( 0 ) { @@ -84,41 +85,41 @@ dbContext::~dbContext () } cacChannel & dbContext::createChannel ( - epicsGuard < epicsMutex > & guard, const char * pName, + epicsGuard < epicsMutex > & guard, const char * pName, cacChannelNotify & notifyIn, cacChannel::priLev priority ) { guard.assertIdenticalMutex ( this->mutex ); - struct dbAddr addr; - int status; - { - // dont know if the database might call a put callback - // while holding its lock ... - epicsGuardRelease < epicsMutex > unguard ( guard ); - status = db_name_to_addr ( pName, & addr ); - } - if ( status ) { + dbChannel *dbch = dbChannel_create ( pName ); + if ( ! dbch ) { if ( ! this->pNetContext.get() ) { this->pNetContext.reset ( - & this->notify.createNetworkContext ( + & this->notify.createNetworkContext ( this->mutex, this->cbMutex ) ); } return this->pNetContext->createChannel ( guard, pName, notifyIn, priority ); } - else if ( ca_preemtive_callback_is_enabled () ) { - return * new ( this->dbChannelIOFreeList ) - dbChannelIO ( this->mutex, notifyIn, addr, *this ); - } - else { - errlogPrintf ( + + if ( ! ca_preemtive_callback_is_enabled () ) { + dbChannelDelete ( dbch ); + errlogPrintf ( "dbContext: preemptive callback required for direct in\n" "memory interfacing of CA channels to the DB.\n" ); throw cacChannel::unsupportedByService (); } + + try { + return * new ( this->dbChannelIOFreeList ) + dbChannelIO ( this->mutex, notifyIn, dbch, *this ); + } + catch (...) { + dbChannelDelete ( dbch ); + throw; + } } -void dbContext::destroyChannel ( +void dbContext::destroyChannel ( epicsGuard < epicsMutex > & guard, dbChannelIO & chan ) { guard.assertIdenticalMutex ( this->mutex ); @@ -134,30 +135,30 @@ void dbContext::destroyChannel ( this->dbChannelIOFreeList.release ( & chan ); } -void dbContext::callStateNotify ( struct dbAddr & addr, - unsigned type, unsigned long count, - const struct db_field_log * pfl, +void dbContext::callStateNotify ( struct dbChannel * dbch, + unsigned type, unsigned long count, + const struct db_field_log * pfl, cacStateNotify & notifyIn ) { unsigned long size = dbr_size_n ( type, count ); if ( type > INT_MAX ) { epicsGuard < epicsMutex > guard ( this->mutex ); - notifyIn.exception ( guard, ECA_BADTYPE, - "type code out of range (high side)", + notifyIn.exception ( guard, ECA_BADTYPE, + "type code out of range (high side)", type, count ); return; } if ( count > INT_MAX ) { epicsGuard < epicsMutex > guard ( this->mutex ); - notifyIn.exception ( guard, ECA_BADCOUNT, + notifyIn.exception ( guard, ECA_BADCOUNT, "element count out of range (high side)", type, count); return; } - // no need to lock this because state notify is + // no need to lock this because state notify is // called from only one event queue consumer thread if ( this->stateNotifyCacheSize < size) { char * pTmp = new char [size]; @@ -166,14 +167,14 @@ void dbContext::callStateNotify ( struct dbAddr & addr, this->stateNotifyCacheSize = size; } void *pvfl = (void *) pfl; - int status = db_get_field ( &addr, static_cast ( type ), + int status = dbChannel_get ( dbch, static_cast ( type ), this->pStateNotifyCache, static_cast ( count ), pvfl ); if ( status ) { epicsGuard < epicsMutex > guard ( this->mutex ); - notifyIn.exception ( guard, ECA_GETFAIL, - "db_get_field() completed unsuccessfuly", type, count ); + notifyIn.exception ( guard, ECA_GETFAIL, + "dbChannel_get() completed unsuccessfully", type, count ); } - else { + else { epicsGuard < epicsMutex > guard ( this->mutex ); notifyIn.current ( guard, type, count, this->pStateNotifyCache ); } @@ -185,10 +186,10 @@ extern "C" void cacAttachClientCtx ( void * pPrivate ) assert ( status == ECA_NORMAL ); } -void dbContext::subscribe ( +void dbContext::subscribe ( epicsGuard < epicsMutex > & guard, - struct dbAddr & addr, dbChannelIO & chan, - unsigned type, unsigned long count, unsigned mask, + struct dbChannel * dbch, dbChannelIO & chan, + unsigned type, unsigned long count, unsigned mask, cacStateNotify & notifyIn, cacChannel::ioid * pId ) { guard.assertIdenticalMutex ( this->mutex ); @@ -214,12 +215,12 @@ void dbContext::subscribe ( unsigned selfPriority = epicsThreadGetPrioritySelf (); unsigned above; - epicsThreadBooleanStatus tbs = + epicsThreadBooleanStatus tbs = epicsThreadLowestPriorityLevelAbove ( selfPriority, &above ); if ( tbs != epicsThreadBooleanStatusSuccess ) { above = selfPriority; } - int status = db_start_events ( tmpctx, "CAC-event", + int status = db_start_events ( tmpctx, "CAC-event", cacAttachClientCtx, ca_current_context (), above ); if ( status ) { db_close_events ( tmpctx ); @@ -227,7 +228,7 @@ void dbContext::subscribe ( } } if ( this->ctx ) { - // another thread tried to simultaneously setup + // another thread tried to simultaneously setup // the event system db_close_events ( tmpctx ); } @@ -237,9 +238,9 @@ void dbContext::subscribe ( } dbSubscriptionIO & subscr = - * new ( this->dbSubscriptionIOFreeList ) - dbSubscriptionIO ( guard, this->mutex, *this, chan, - addr, notifyIn, type, count, mask, this->ctx ); + * new ( this->dbSubscriptionIOFreeList ) + dbSubscriptionIO ( guard, this->mutex, *this, chan, + dbch, notifyIn, type, count, mask, this->ctx ); chan.dbContextPrivateListOfIO::eventq.add ( subscr ); this->ioTable.idAssignAdd ( subscr ); @@ -248,27 +249,27 @@ void dbContext::subscribe ( } } -void dbContext::initiatePutNotify ( +void dbContext::initiatePutNotify ( epicsGuard < epicsMutex > & guard, - dbChannelIO & chan, struct dbAddr & addr, - unsigned type, unsigned long count, const void * pValue, + dbChannelIO & chan, struct dbChannel * dbch, + unsigned type, unsigned long count, const void * pValue, cacWriteNotify & notifyIn, cacChannel::ioid * pId ) { guard.assertIdenticalMutex ( this->mutex ); if ( ! chan.dbContextPrivateListOfIO::pBlocker ) { - chan.dbContextPrivateListOfIO::pBlocker = - new ( this->dbPutNotifyBlockerFreeList ) + chan.dbContextPrivateListOfIO::pBlocker = + new ( this->dbPutNotifyBlockerFreeList ) dbPutNotifyBlocker ( this->mutex ); this->ioTable.idAssignAdd ( *chan.dbContextPrivateListOfIO::pBlocker ); } - chan.dbContextPrivateListOfIO::pBlocker->initiatePutNotify ( - guard, notifyIn, addr, type, count, pValue ); + chan.dbContextPrivateListOfIO::pBlocker->initiatePutNotify ( + guard, notifyIn, dbch, type, count, pValue ); if ( pId ) { *pId = chan.dbContextPrivateListOfIO::pBlocker->getId (); } } -void dbContext::destroyAllIO ( +void dbContext::destroyAllIO ( epicsGuard < epicsMutex > & guard, dbChannelIO & chan ) { guard.assertIdenticalMutex ( this->mutex ); @@ -284,7 +285,7 @@ void dbContext::destroyAllIO ( } while ( ( pIO = tmp.get() ) ) { - // This prevents a db event callback from coming + // This prevents a db event callback from coming // through after the notify IO is deleted pIO->unsubscribe ( guard ); // If they call ioCancel() here it will be ignored @@ -301,8 +302,8 @@ void dbContext::destroyAllIO ( } } -void dbContext::ioCancel ( - epicsGuard < epicsMutex > & guard, dbChannelIO & chan, +void dbContext::ioCancel ( + epicsGuard < epicsMutex > & guard, dbChannelIO & chan, const cacChannel::ioid &id ) { guard.assertIdenticalMutex ( this->mutex ); @@ -325,8 +326,8 @@ void dbContext::ioCancel ( } } -void dbContext::ioShow ( - epicsGuard < epicsMutex > & guard, const cacChannel::ioid &id, +void dbContext::ioShow ( + epicsGuard < epicsMutex > & guard, const cacChannel::ioid &id, unsigned level ) const { guard.assertIdenticalMutex ( this->mutex ); @@ -339,7 +340,7 @@ void dbContext::ioShow ( void dbContext::showAllIO ( const dbChannelIO & chan, unsigned level ) const { epicsGuard < epicsMutex > guard ( this->mutex ); - tsDLIterConst < dbSubscriptionIO > pItem = + tsDLIterConst < dbSubscriptionIO > pItem = chan.dbContextPrivateListOfIO::eventq.firstIter (); while ( pItem.valid () ) { pItem->show ( guard, level ); @@ -356,14 +357,14 @@ void dbContext::show ( unsigned level ) const this->show ( guard, level ); } -void dbContext::show ( +void dbContext::show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { guard.assertIdenticalMutex ( this->mutex ); - printf ( "dbContext at %p\n", + printf ( "dbContext at %p\n", static_cast ( this ) ); if ( level > 0u ) { - printf ( "\tevent call back cache location %p, and its size %lu\n", + printf ( "\tevent call back cache location %p, and its size %lu\n", static_cast ( this->pStateNotifyCache ), this->stateNotifyCacheSize ); this->readNotifyCache.show ( guard, level - 1 ); } @@ -375,7 +376,7 @@ void dbContext::show ( } } -void dbContext::flush ( +void dbContext::flush ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); diff --git a/src/ioc/db/dbContextReadNotifyCache.cpp b/src/ioc/db/dbContextReadNotifyCache.cpp index 1d3b8ab1e..f298a63c8 100644 --- a/src/ioc/db/dbContextReadNotifyCache.cpp +++ b/src/ioc/db/dbContextReadNotifyCache.cpp @@ -5,7 +5,7 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are 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. \*************************************************************************/ /* @@ -30,11 +30,11 @@ dbContextReadNotifyCache::dbContextReadNotifyCache ( epicsMutex & mutexIn ) : class privateAutoDestroyPtr { public: - privateAutoDestroyPtr ( + privateAutoDestroyPtr ( dbContextReadNotifyCacheAllocator & allocator, unsigned long size ) : _allocator ( allocator ), _p ( allocator.alloc ( size ) ) {} ~privateAutoDestroyPtr () { _allocator.free ( _p ); } - char * get () const { return _p; } + char * get () const { return _p; } private: dbContextReadNotifyCacheAllocator & _allocator; char * _p; @@ -43,28 +43,28 @@ private: }; // extra effort taken here to not hold the lock when calling the callback -void dbContextReadNotifyCache::callReadNotify ( - epicsGuard < epicsMutex > & guard, struct dbAddr & addr, +void dbContextReadNotifyCache::callReadNotify ( + epicsGuard < epicsMutex > & guard, struct dbChannel * dbch, unsigned type, unsigned long count, cacReadNotify & notify ) { guard.assertIdenticalMutex ( _mutex ); if ( type > INT_MAX ) { - notify.exception ( guard, ECA_BADTYPE, - "type code out of range (high side)", + notify.exception ( guard, ECA_BADTYPE, + "type code out of range (high side)", type, count ); return; } - if ( addr.no_elements < 0 ) { - notify.exception ( guard, ECA_BADCOUNT, + if ( dbChannelElements(dbch) < 0 ) { + notify.exception ( guard, ECA_BADCOUNT, "database has negetive element count", type, count); return; } - if ( count > static_cast < unsigned > ( addr.no_elements ) ) { - notify.exception ( guard, ECA_BADCOUNT, + if ( count > static_cast < unsigned long > ( dbChannelElements(dbch) ) ) { + notify.exception ( guard, ECA_BADCOUNT, "element count out of range (high side)", type, count); return; @@ -75,21 +75,21 @@ void dbContextReadNotifyCache::callReadNotify ( int status; { epicsGuardRelease < epicsMutex > unguard ( guard ); - status = db_get_field ( &addr, static_cast ( type ), - ptr.get (), static_cast ( count ), 0 ); + status = dbChannel_get ( dbch, static_cast ( type ), + ptr.get (), static_cast ( count ), 0 ); } if ( status ) { - notify.exception ( guard, ECA_GETFAIL, + notify.exception ( guard, ECA_GETFAIL, "db_get_field() completed unsuccessfuly", type, count ); } - else { - notify.completion ( + else { + notify.completion ( guard, type, count, ptr.get () ); } } -void dbContextReadNotifyCache::show ( +void dbContextReadNotifyCache::show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { guard.assertIdenticalMutex ( _mutex ); @@ -155,8 +155,8 @@ void dbContextReadNotifyCacheAllocator::show ( unsigned level ) const pNext = _pReadNotifyCache->pNext; count++; } - printf ( "\tcount %lu and size %lu\n", - static_cast < unsigned long > ( count ), + printf ( "\tcount %lu and size %lu\n", + static_cast < unsigned long > ( count ), _readNotifyCacheSize ); } } diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index 76f4f1a2c..6fbb9a03a 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -1,19 +1,20 @@ /*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. * Copyright (c) 2002 The University of Chicago, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ -/* dbEvent.c */ -/* $Revision-Id$ */ -/* routines for scheduling events to lower priority tasks via the RT kernel */ + /* - * Author: Jeffrey O. Hill - * Date: 4-1-89 -*/ + * Author: Jeffrey O. Hill + * + * Ralph Lange + */ #include #include @@ -39,6 +40,7 @@ #include "db_field_log.h" #define epicsExportSharedSymbols #include "dbAddr.h" +#include "dbChannel.h" #include "dbLock.h" #include "dbAccessDefs.h" #include "dbEvent.h" @@ -48,24 +50,6 @@ #define EVENTQUESIZE (EVENTENTRIES * EVENTSPERQUE) #define EVENTQEMPTY ((struct evSubscrip *)NULL) -/* - * event subscruiption - */ -struct evSubscrip { - ELLNODE node; - struct dbAddr *paddr; - EVENTFUNC *user_sub; - void *user_arg; - struct event_que *ev_que; - db_field_log *pLastLog; - unsigned long npend; /* n times this event is on the queue */ - unsigned long nreplace; /* n times replacing event on the queue */ - unsigned char select; - char valque; - char callBackInProgress; - char enabled; -}; - /* * really a ring buffer */ @@ -73,27 +57,27 @@ struct event_que { /* lock writers to the ring buffer only */ /* readers must never slow up writers */ epicsMutexId writelock; - db_field_log valque[EVENTQUESIZE]; + db_field_log *valque[EVENTQUESIZE]; struct evSubscrip *evque[EVENTQUESIZE]; struct event_que *nextque; /* in case que quota exceeded */ struct event_user *evUser; /* event user parent struct */ unsigned short putix; unsigned short getix; unsigned short quota; /* the number of assigned entries*/ - unsigned short nDuplicates; /* N events duplicated on this q */ + unsigned short nDuplicates; /* N events duplicated on this q */ unsigned short nCanceled; /* the number of canceled entries */ }; struct event_user { struct event_que firstque; /* the first event que */ - + epicsMutexId lock; epicsEventId ppendsem; /* Wait while empty */ epicsEventId pflush_sem; /* wait for flush */ - + EXTRALABORFUNC *extralabor_sub;/* off load to event task */ void *extralabor_arg;/* parameter to above */ - + epicsThreadId taskid; /* event handler task id */ struct evSubscrip *pSuicideEvent; /* event that is deleteing itself */ unsigned queovr; /* event que overflow count */ @@ -114,34 +98,28 @@ struct event_user { #define RNGINC(OLD)\ ( (unsigned short) ( (OLD) >= (EVENTQUESIZE-1) ? 0 : (OLD)+1 ) ) -#define LOCKEVQUE(EV_QUE)\ -epicsMutexMustLock((EV_QUE)->writelock); - -#define UNLOCKEVQUE(EV_QUE)\ -epicsMutexUnlock((EV_QUE)->writelock); - -#define LOCKREC(RECPTR)\ -epicsMutexMustLock((RECPTR)->mlok); - -#define UNLOCKREC(RECPTR)\ -epicsMutexUnlock((RECPTR)->mlok); +#define LOCKEVQUE(EV_QUE) epicsMutexMustLock((EV_QUE)->writelock) +#define UNLOCKEVQUE(EV_QUE) epicsMutexUnlock((EV_QUE)->writelock) +#define LOCKREC(RECPTR) epicsMutexMustLock((RECPTR)->mlok) +#define UNLOCKREC(RECPTR) epicsMutexUnlock((RECPTR)->mlok) static void *dbevEventUserFreeList; static void *dbevEventQueueFreeList; -static void *dbevEventBlockFreeList; +static void *dbevEventSubscriptionFreeList; +static void *dbevFieldLogFreeList; static char *EVENT_PEND_NAME = "eventTask"; static struct evSubscrip canceledEvent; -static unsigned short ringSpace ( const struct event_que *pevq ) +static unsigned short ringSpace ( const struct event_que *pevq ) { if ( pevq->evque[pevq->putix] == EVENTQEMPTY ) { if ( pevq->getix > pevq->putix ) { return ( unsigned short ) ( pevq->getix - pevq->putix ); } else { - return ( unsigned short ) ( ( EVENTQUESIZE + pevq->getix ) - pevq->putix ); + return ( unsigned short ) ( ( EVENTQUESIZE + pevq->getix ) - pevq->putix ); } } return 0; @@ -163,7 +141,7 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) DBADDR addr; long status; struct evSubscrip *pevent; - dbFldDes *pdbFldDes; + dbFldDes *pdbFldDes; if ( ! pname ) return DB_EVENT_OK; status = dbNameToAddr ( pname, &addr ); @@ -172,7 +150,7 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) return DB_EVENT_ERROR; } - LOCKREC ( addr.precord ); + LOCKREC (addr.precord); pevent = (struct evSubscrip *) ellFirst ( &addr.precord->mlis ); @@ -186,16 +164,16 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) ellCount ( &addr.precord->mlis ) ); while ( pevent ) { - pdbFldDes = pevent->paddr->pfldDes; + pdbFldDes = dbChannelFldDes(pevent->chan); if ( level > 0 ) { printf ( "%4.4s", pdbFldDes->name ); printf ( " { " ); - if ( pevent->select & DBE_VALUE ) printf( "VALUE " ); - if ( pevent->select & DBE_LOG ) printf( "LOG " ); - if ( pevent->select & DBE_ALARM ) printf( "ALARM " ); - if ( pevent->select & DBE_PROPERTY ) printf( "PROPERTY " ); + if ( pevent->select & DBE_VALUE ) printf( "VALUE " ); + if ( pevent->select & DBE_LOG ) printf( "LOG " ); + if ( pevent->select & DBE_ALARM ) printf( "ALARM " ); + if ( pevent->select & DBE_PROPERTY ) printf( "PROPERTY " ); printf ( "}" ); if ( pevent->npend ) { @@ -210,15 +188,15 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) taskId = ( void * ) pevent->ev_que->evUser->taskid; UNLOCKEVQUE(pevent->ev_que); if ( nEntriesFree == 0u ) { - printf ( ", thread=%p, queue full", + printf ( ", thread=%p, queue full", (void *) taskId ); } else if ( nEntriesFree == EVENTQUESIZE ) { - printf ( ", thread=%p, queue empty", + printf ( ", thread=%p, queue empty", (void *) taskId ); } else { - printf ( ", thread=%p, unused entries=%u", + printf ( ", thread=%p, unused entries=%u", (void *) taskId, nEntriesFree ); } } @@ -229,7 +207,7 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) if ( pevent->nreplace ) { printf (", discarded by replacement=%ld", pevent->nreplace); } - if ( ! pevent->valque ) { + if ( ! pevent->useValque ) { printf (", queueing disabled" ); } LOCKEVQUE(pevent->ev_que); @@ -245,19 +223,19 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) } if ( level > 3 ) { - printf ( ", ev %p, ev que %p, ev user %p", - ( void * ) pevent, - ( void * ) pevent->ev_que, + printf ( ", ev %p, ev que %p, ev user %p", + ( void * ) pevent, + ( void * ) pevent->ev_que, ( void * ) pevent->ev_que->evUser ); } printf( "\n" ); } - pevent = (struct evSubscrip *) ellNext ( &pevent->node ); + pevent = (struct evSubscrip *) ellNext ( &pevent->node ); } - UNLOCKREC ( addr.precord ); + UNLOCKREC (addr.precord); return DB_EVENT_OK; } @@ -268,32 +246,36 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) * * Initialize the event facility for this task. Must be called at least once * by each task which uses the db event facility - * + * * returns: ptr to event user block or NULL if memory can't be allocated */ dbEventCtx epicsShareAPI db_init_events (void) { struct event_user * evUser; - + if (!dbevEventUserFreeList) { - freeListInitPvt(&dbevEventUserFreeList, + freeListInitPvt(&dbevEventUserFreeList, sizeof(struct event_user),8); } if (!dbevEventQueueFreeList) { - freeListInitPvt(&dbevEventQueueFreeList, + freeListInitPvt(&dbevEventQueueFreeList, sizeof(struct event_que),8); } - if (!dbevEventBlockFreeList) { - freeListInitPvt(&dbevEventBlockFreeList, + if (!dbevEventSubscriptionFreeList) { + freeListInitPvt(&dbevEventSubscriptionFreeList, sizeof(struct evSubscrip),256); } - - evUser = (struct event_user *) + if (!dbevFieldLogFreeList) { + freeListInitPvt(&dbevFieldLogFreeList, + sizeof(struct db_field_log),2048); + } + + evUser = (struct event_user *) freeListCalloc(dbevEventUserFreeList); if (!evUser) { return NULL; } - + evUser->firstque.evUser = evUser; evUser->firstque.writelock = epicsMutexCreate(); if (!evUser->firstque.writelock) { @@ -304,7 +286,7 @@ dbEventCtx epicsShareAPI db_init_events (void) if (!evUser->ppendsem) { epicsMutexDestroy (evUser->firstque.writelock); return NULL; - } + } evUser->pflush_sem = epicsEventCreate(epicsEventEmpty); if (!evUser->pflush_sem) { epicsMutexDestroy (evUser->firstque.writelock); @@ -327,7 +309,7 @@ dbEventCtx epicsShareAPI db_init_events (void) /* * DB_CLOSE_EVENTS() - * + * * evUser block and additional event queues * deallocated when the event thread terminates * itself @@ -341,7 +323,7 @@ void epicsShareAPI db_close_events (dbEventCtx ctx) * Exit not forced on event blocks for now - this is left to channel * access and any other tasks using this facility which can find them * more efficiently. - * + * * NOTE: not deleting events before calling this routine could be * hazardous to the system's health. */ @@ -375,7 +357,7 @@ static struct event_que * create_ev_que ( struct event_user * const evUser ) * DB_ADD_EVENT() */ dbEventSubscription epicsShareAPI db_add_event ( - dbEventCtx ctx, struct dbAddr *paddr, + dbEventCtx ctx, struct dbChannel *chan, EVENTFUNC *user_sub, void *user_arg, unsigned select) { struct event_user * const evUser = (struct event_user *) ctx; @@ -389,7 +371,7 @@ dbEventSubscription epicsShareAPI db_add_event ( return NULL; } - pevent = freeListCalloc ( dbevEventBlockFreeList ); + pevent = freeListCalloc (dbevEventSubscriptionFreeList); if ( ! pevent ) { return NULL; } @@ -422,7 +404,7 @@ dbEventSubscription epicsShareAPI db_add_event ( epicsMutexUnlock ( evUser->lock ); if ( ! ev_que ) { - freeListFree ( dbevEventBlockFreeList, pevent ); + freeListFree ( dbevEventSubscriptionFreeList, pevent ); return NULL; } @@ -430,7 +412,7 @@ dbEventSubscription epicsShareAPI db_add_event ( pevent->nreplace = 0ul; pevent->user_sub = user_sub; pevent->user_arg = user_arg; - pevent->paddr = paddr; + pevent->chan = chan; pevent->select = (unsigned char) select; pevent->pLastLog = NULL; /* not yet in the queue */ pevent->callBackInProgress = FALSE; @@ -442,12 +424,12 @@ dbEventSubscription epicsShareAPI db_add_event ( * communication (for other types they get whatever happens to be * there upon wakeup) */ - if( paddr->no_elements == 1 && - paddr->field_size <= sizeof(union native_value)) { - pevent->valque = TRUE; + if( dbChannelElements(chan) == 1 && + dbChannelFieldSize(chan) <= sizeof(union native_value)) { + pevent->useValque = TRUE; } else { - pevent->valque = FALSE; + pevent->useValque = FALSE; } return pevent; @@ -456,93 +438,93 @@ dbEventSubscription epicsShareAPI db_add_event ( /* * db_event_enable() */ -void epicsShareAPI db_event_enable (dbEventSubscription es) +void epicsShareAPI db_event_enable (dbEventSubscription event) { - struct evSubscrip * const pevent = (struct evSubscrip *) es; - struct dbCommon * const precord = - (struct dbCommon *) pevent->paddr->precord; + struct evSubscrip * const pevent = (struct evSubscrip *) event; + struct dbCommon * const precord = dbChannelRecord(pevent->chan); - LOCKREC(precord); + LOCKREC (precord); if ( ! pevent->enabled ) { ellAdd (&precord->mlis, &pevent->node); pevent->enabled = TRUE; } - UNLOCKREC(precord); + UNLOCKREC (precord); } /* * db_event_disable() */ -void epicsShareAPI db_event_disable (dbEventSubscription es) +void epicsShareAPI db_event_disable (dbEventSubscription event) { - struct evSubscrip * const pevent = (struct evSubscrip *) es; - struct dbCommon * const precord = - (struct dbCommon *) pevent->paddr->precord; + struct evSubscrip * const pevent = (struct evSubscrip *) event; + struct dbCommon * const precord = dbChannelRecord(pevent->chan); - LOCKREC(precord); + LOCKREC (precord); if ( pevent->enabled ) { ellDelete(&precord->mlis, &pevent->node); pevent->enabled = FALSE; } - UNLOCKREC(precord); + UNLOCKREC (precord); } /* * event_remove() * event queue lock _must_ be applied + * this nulls the entry in the queue, but doesn't delete the db_field_log chunk */ -static void event_remove ( struct event_que *ev_que, +static void event_remove ( struct event_que *ev_que, unsigned short index, struct evSubscrip *placeHolder ) { - struct evSubscrip * const pEvent = ev_que->evque[index]; + struct evSubscrip * const pevent = ev_que->evque[index]; ev_que->evque[index] = placeHolder; - if ( pEvent->npend == 1u ) { - pEvent->pLastLog = NULL; + ev_que->valque[index] = NULL; + if ( pevent->npend == 1u ) { + pevent->pLastLog = NULL; } else { - assert ( pEvent->npend > 1u ); + assert ( pevent->npend > 1u ); assert ( ev_que->nDuplicates >= 1u ); ev_que->nDuplicates--; } - pEvent->npend--; + pevent->npend--; } /* * DB_CANCEL_EVENT() * - * This routine does not prevent two threads from deleting + * This routine does not prevent two threads from deleting * the same block at the same time. - * + * */ -void epicsShareAPI db_cancel_event (dbEventSubscription es) +void epicsShareAPI db_cancel_event (dbEventSubscription event) { - struct evSubscrip * const pevent = ( struct evSubscrip * ) es; + struct evSubscrip * const pevent = (struct evSubscrip *) event; unsigned short getix; - db_event_disable ( es ); + db_event_disable ( event ); /* - * flag the event as canceled by NULLing out the callback handler + * flag the event as canceled by NULLing out the callback handler * * make certain that the event isnt being accessed while * its call back changes */ - LOCKEVQUE ( pevent->ev_que ) + LOCKEVQUE (pevent->ev_que); pevent->user_sub = NULL; /* * purge this event from the queue * - * Its better to take this approach rather than waiting + * Its better to take this approach rather than waiting * for the event thread to finish removing this event * from the queue because the event thread will not - * process if we are in flow control mode. Since blocking + * process if we are in flow control mode. Since blocking * here will block CA's TCP input queue then a dead lock * would be possible. */ - for ( getix = pevent->ev_que->getix; + for ( getix = pevent->ev_que->getix; pevent->ev_que->evque[getix] != EVENTQEMPTY; ) { if ( pevent->ev_que->evque[getix] == pevent ) { assert ( pevent->ev_que->nCanceled < USHRT_MAX ); @@ -561,17 +543,17 @@ void epicsShareAPI db_cancel_event (dbEventSubscription es) } else { while ( pevent->callBackInProgress ) { - UNLOCKEVQUE ( pevent->ev_que ) + UNLOCKEVQUE (pevent->ev_que); epicsEventMustWait ( pevent->ev_que->evUser->pflush_sem ); - LOCKEVQUE ( pevent->ev_que ) + LOCKEVQUE (pevent->ev_que); } } pevent->ev_que->quota -= EVENTENTRIES; - UNLOCKEVQUE ( pevent->ev_que ) - - freeListFree ( dbevEventBlockFreeList, pevent ); + UNLOCKEVQUE (pevent->ev_que); + + freeListFree ( dbevEventSubscriptionFreeList, pevent ); return; } @@ -585,7 +567,7 @@ void epicsShareAPI db_flush_extra_labor_event (dbEventCtx ctx) { struct event_user * const evUser = (struct event_user *) ctx; - epicsMutexMustLock ( evUser->lock ); + epicsMutexMustLock ( evUser->lock ); while ( evUser->extraLaborBusy ) { epicsMutexUnlock ( evUser->lock ); epicsThreadSleep(0.1); @@ -640,38 +622,90 @@ int epicsShareAPI db_post_extra_labor (dbEventCtx ctx) } /* - * DB_POST_SINGLE_EVENT_PRIVATE() + * DB_CREATE_EVENT_LOG() * * NOTE: This assumes that the db scan lock is already applied + * (as it copies data from the record) */ -static void db_post_single_event_private (struct evSubscrip *event) -{ - struct event_que * const ev_que = event->ev_que; - db_field_log * pLog; +db_field_log* epicsShareAPI db_create_event_log (struct evSubscrip *pevent) +{ + db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList); + + if (pLog) { + struct dbChannel *chan = pevent->chan; + struct dbCommon *prec = dbChannelRecord(chan); + pLog->ctx = dbfl_context_event; + if (pevent->useValque) { + pLog->type = dbfl_type_val; + pLog->stat = prec->stat; + pLog->sevr = prec->sevr; + pLog->time = prec->time; + pLog->field_type = dbChannelFieldType(chan); + pLog->no_elements = dbChannelElements(chan); + /* + * use memcpy to avoid a bus error on + * union copy of char in the db at an odd + * address + */ + memcpy(&pLog->u.v.field, + dbChannelField(chan), + dbChannelFieldSize(chan)); + } else { + pLog->type = dbfl_type_rec; + } + } + return pLog; +} + +/* + * DB_CREATE_READ_LOG() + * + */ +db_field_log* epicsShareAPI db_create_read_log (struct dbChannel *chan) +{ + db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList); + + if (pLog) { + pLog->ctx = dbfl_context_read; + pLog->type = dbfl_type_rec; + } + return pLog; +} + +/* + * DB_QUEUE_EVENT_LOG() + * + */ +static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog) +{ + struct event_que *ev_que; int firstEventFlag; unsigned rngSpace; + ev_que = pevent->ev_que; /* * evUser ring buffer must be locked for the multiple * threads writing/reading it */ - LOCKEVQUE(ev_que) + LOCKEVQUE (ev_que); /* - * if we have an event on the queue and we are - * not saving the current value (because this is a - * string or an array) then ignore duplicate - * events (saving them without the current valuye - * serves no purpose) + * if we have an event on the queue and both the last + * event on the queue and the current event are emtpy + * (i.e. of type dbfl_type_rec), simply ignore duplicate + * events (saving empty events serves no purpose) */ - if (!event->valque && event->npend>0u) { - UNLOCKEVQUE(ev_que) + if (pevent->npend > 0u && + (*pevent->pLastLog)->type == dbfl_type_rec && + pLog->type == dbfl_type_rec) { + db_delete_field_log(pLog); + UNLOCKEVQUE (ev_que); return; } - /* - * add to task local event que + /* + * add to task local event que */ /* @@ -680,34 +714,38 @@ static void db_post_single_event_private (struct evSubscrip *event) * then replace the last event on the queue (for this monitor) */ rngSpace = ringSpace ( ev_que ); - if ( event->npend>0u && + if ( pevent->npend>0u && (ev_que->evUser->flowCtrlMode || rngSpace<=EVENTSPERQUE) ) { /* * replace last event if no space is left */ - pLog = event->pLastLog; - event->nreplace++; + if (*pevent->pLastLog) { + db_delete_field_log(*pevent->pLastLog); + *pevent->pLastLog = pLog; + } + pevent->nreplace++; /* - * the event task has already been notified about + * the event task has already been notified about * this so we dont need to post the semaphore */ firstEventFlag = 0; } /* - * Otherwise, the current entry must be available. + * Otherwise, the current entry must be available. * Fill it in and advance the ring buffer. */ else { assert ( ev_que->evque[ev_que->putix] == EVENTQEMPTY ); - pLog = &ev_que->valque[ev_que->putix]; - ev_que->evque[ev_que->putix] = event; - if (event->npend>0u) { + ev_que->evque[ev_que->putix] = pevent; + ev_que->valque[ev_que->putix] = pLog; + pevent->pLastLog = &ev_que->valque[ev_que->putix]; + if (pevent->npend>0u) { ev_que->nDuplicates++; } - event->npend++; - /* - * if the ring buffer was empty before - * adding this event + pevent->npend++; + /* + * if the ring buffer was empty before + * adding this event */ if (rngSpace==EVENTQUESIZE) { firstEventFlag = 1; @@ -718,39 +756,21 @@ static void db_post_single_event_private (struct evSubscrip *event) ev_que->putix = RNGINC ( ev_que->putix ); } - if (pLog && event->valque) { - struct dbCommon *precord = event->paddr->precord; - pLog->stat = precord->stat; - pLog->sevr = precord->sevr; - pLog->time = precord->time; + UNLOCKEVQUE (ev_que); - /* - * use memcpy to avoid a bus error on - * union copy of char in the db at an odd - * address - */ - memcpy( (char *)&pLog->field, - (char *)event->paddr->pfield, - event->paddr->field_size); - - event->pLastLog = pLog; - } - - UNLOCKEVQUE(ev_que) - - /* - * its more efficent to notify the event handler + /* + * its more efficent to notify the event handler * only after the event is ready and the lock * is off in case it runs at a higher priority * than the caller here. */ if (firstEventFlag) { - /* - * notify the event handler + /* + * notify the event handler */ epicsEventSignal(ev_que->evUser->ppendsem); } -} +} /* * DB_POST_EVENTS() @@ -763,28 +783,30 @@ void *pRecord, void *pField, unsigned int caEventMask ) -{ - struct dbCommon * const pdbc = (struct dbCommon *)pRecord; - struct evSubscrip * event; +{ + struct dbCommon * const prec = (struct dbCommon *) pRecord; + struct evSubscrip *pevent; - if (pdbc->mlis.count == 0) return DB_EVENT_OK; /* no monitors set */ + if (prec->mlis.count == 0) return DB_EVENT_OK; /* no monitors set */ + + LOCKREC (prec); + + for (pevent = (struct evSubscrip *) prec->mlis.node.next; + pevent; pevent = (struct evSubscrip *) pevent->node.next){ - LOCKREC(pdbc); - - for (event = (struct evSubscrip *) pdbc->mlis.node.next; - event; event = (struct evSubscrip *) event->node.next){ - /* * Only send event msg if they are waiting on the field which * changed or pval==NULL and waiting on alarms and alarms changed */ - if ( (event->paddr->pfield == (void *)pField || pField==NULL) && - (caEventMask & event->select) ) { - db_post_single_event_private (event); + if ( (dbChannelField(pevent->chan) == (void *)pField || pField==NULL) && + (caEventMask & pevent->select)) { + db_field_log *pLog = db_create_event_log(pevent); + pLog = dbChannelRunPreChain(pevent->chan, pLog); + if (pLog) db_queue_event_log(pevent, pLog); } } - UNLOCKREC(pdbc); + UNLOCKREC (prec); return DB_EVENT_OK; } @@ -792,14 +814,19 @@ unsigned int caEventMask /* * DB_POST_SINGLE_EVENT() */ -void epicsShareAPI db_post_single_event (dbEventSubscription es) -{ - struct evSubscrip * const event = (struct evSubscrip *) es; - struct dbCommon * const precord = event->paddr->precord; +void epicsShareAPI db_post_single_event (dbEventSubscription event) +{ + struct evSubscrip * const pevent = (struct evSubscrip *) event; + struct dbCommon * const prec = dbChannelRecord(pevent->chan); + db_field_log *pLog; - dbScanLock (precord); - db_post_single_event_private (event); - dbScanUnlock (precord); + dbScanLock (prec); + + pLog = db_create_event_log(pevent); + pLog = dbChannelRunPreChain(pevent->chan, pLog); + db_queue_event_log(pevent, pLog); + + dbScanUnlock (prec); } /* @@ -808,31 +835,35 @@ void epicsShareAPI db_post_single_event (dbEventSubscription es) static int event_read ( struct event_que *ev_que ) { db_field_log *pfl; - void ( *user_sub ) ( void *user_arg, struct dbAddr *paddr, + void ( *user_sub ) ( void *user_arg, struct dbChannel *chan, int eventsRemaining, db_field_log *pfl ); - + /* * evUser ring buffer must be locked for the multiple * threads writing/reading it */ - LOCKEVQUE ( ev_que ) - + LOCKEVQUE (ev_que); + /* * if in flow control mode drain duplicates and then * suspend processing events until flow control * mode is over */ if ( ev_que->evUser->flowCtrlMode && ev_que->nDuplicates == 0u ) { - UNLOCKEVQUE(ev_que); + UNLOCKEVQUE (ev_que); return DB_EVENT_OK; } - - while ( ev_que->evque[ev_que->getix] != EVENTQEMPTY ) { - db_field_log fl = ev_que->valque[ev_que->getix]; - struct evSubscrip *event = ev_que->evque[ev_que->getix]; - if ( event == &canceledEvent ) { + while ( ev_que->evque[ev_que->getix] != EVENTQEMPTY ) { + struct evSubscrip *pevent = ev_que->evque[ev_que->getix]; + + pfl = ev_que->valque[ev_que->getix]; + if ( pevent == &canceledEvent ) { ev_que->evque[ev_que->getix] = EVENTQEMPTY; + if (ev_que->valque[ev_que->getix]) { + db_delete_field_log(ev_que->valque[ev_que->getix]); + ev_que->valque[ev_que->getix] = NULL; + } ev_que->getix = RNGINC ( ev_que->getix ); assert ( ev_que->nCanceled > 0 ); ev_que->nCanceled--; @@ -844,12 +875,6 @@ static int event_read ( struct event_que *ev_que ) * communication. (for other types they get whatever happens * to be there upon wakeup) */ - if ( event->valque ) { - pfl = &fl; - } - else { - pfl = NULL; - } event_remove ( ev_que, ev_que->getix, EVENTQEMPTY ); ev_que->getix = RNGINC ( ev_que->getix ); @@ -858,29 +883,34 @@ static int event_read ( struct event_que *ev_que ) * create a local copy of the call back parameters while * we still have the lock */ - user_sub = event->user_sub; + user_sub = pevent->user_sub; /* * Next event pointer can be used by event tasks to determine * if more events are waiting in the queue * * Must remove the lock here so that we dont deadlock if - * this calls dbGetField() and blocks on the record lock, - * dbPutField() is in progress in another task, it has the - * record lock, and it is calling db_post_events() waiting + * this calls dbGetField() and blocks on the record lock, + * dbPutField() is in progress in another task, it has the + * record lock, and it is calling db_post_events() waiting * for the event queue lock (which this thread now has). */ if ( user_sub ) { /* * This provides a way to test to see if an event is in use - * despite the fact that the event queue does not point to - * it. + * despite the fact that the event queue does not point to + * it. */ - event->callBackInProgress = TRUE; - UNLOCKEVQUE ( ev_que ) - ( *user_sub ) ( event->user_arg, event->paddr, + pevent->callBackInProgress = TRUE; + UNLOCKEVQUE (ev_que); + /* Run post-event-queue filter chain */ + if (ellCount(&pevent->chan->post_chain)) { + dbChannelRunPostChain(pevent->chan, pfl); + } + /* Issue user callback */ + ( *user_sub ) ( pevent->user_arg, pevent->chan, ev_que->evque[ev_que->getix] != EVENTQEMPTY, pfl ); - LOCKEVQUE ( ev_que ) + LOCKEVQUE (ev_que); /* * check to see if this event has been canceled each @@ -889,22 +919,23 @@ static int event_read ( struct event_que *ev_que ) * complete sem if there are no longer any events on the * queue */ - if ( ev_que->evUser->pSuicideEvent == event ) { + if ( ev_que->evUser->pSuicideEvent == pevent ) { ev_que->evUser->pSuicideEvent = NULL; } else { - if ( event->user_sub==NULL && event->npend==0u ) { - event->callBackInProgress = FALSE; + if ( pevent->user_sub==NULL && pevent->npend==0u ) { + pevent->callBackInProgress = FALSE; epicsEventSignal ( ev_que->evUser->pflush_sem ); } else { - event->callBackInProgress = FALSE; + pevent->callBackInProgress = FALSE; } } } + db_delete_field_log(pfl); } - UNLOCKEVQUE ( ev_que ) + UNLOCKEVQUE (ev_que); return DB_EVENT_OK; } @@ -924,7 +955,7 @@ static void event_task (void *pParm) taskwdInsert ( epicsThreadGetIdSelf(), NULL, NULL ); - do{ + do { void (*pExtraLaborSub) (void *); void *pExtraLaborArg; epicsEventMustWait(evUser->ppendsem); @@ -951,7 +982,7 @@ static void event_task (void *pParm) } evUser->extraLaborBusy = FALSE; - for ( ev_que = &evUser->firstque; ev_que; + for ( ev_que = &evUser->firstque; ev_que; ev_que = ev_que->nextque ) { epicsMutexUnlock ( evUser->lock ); event_read (ev_que); @@ -959,7 +990,7 @@ static void event_task (void *pParm) } epicsMutexUnlock ( evUser->lock ); - } while( ! evUser->pendexit ); + } while ( ! evUser->pendexit ); epicsMutexDestroy(evUser->firstque.writelock); @@ -967,7 +998,7 @@ static void event_task (void *pParm) struct event_que *nextque; ev_que = evUser->firstque.nextque; - while(ev_que){ + while (ev_que) { nextque = ev_que->nextque; epicsMutexDestroy(ev_que->writelock); freeListFree(dbevEventQueueFreeList, ev_que); @@ -990,15 +1021,15 @@ static void event_task (void *pParm) * DB_START_EVENTS() */ int epicsShareAPI db_start_events ( - dbEventCtx ctx,const char *taskname, void (*init_func)(void *), + dbEventCtx ctx,const char *taskname, void (*init_func)(void *), void *init_func_arg, unsigned osiPriority ) { struct event_user * const evUser = (struct event_user *) ctx; - + epicsMutexMustLock ( evUser->lock ); - /* - * only one ca_pend_event thread may be + /* + * only one ca_pend_event thread may be * started for each evUser */ if (evUser->taskid) { @@ -1044,7 +1075,7 @@ void epicsShareAPI db_event_flow_ctrl_mode_on (dbEventCtx ctx) epicsMutexMustLock ( evUser->lock ); evUser->flowCtrlMode = TRUE; epicsMutexUnlock ( evUser->lock ); - /* + /* * notify the event handler task */ epicsEventSignal(evUser->ppendsem); @@ -1063,13 +1094,24 @@ void epicsShareAPI db_event_flow_ctrl_mode_off (dbEventCtx ctx) epicsMutexMustLock ( evUser->lock ); evUser->flowCtrlMode = FALSE; epicsMutexUnlock ( evUser->lock ); - /* + /* * notify the event handler task */ epicsEventSignal (evUser->ppendsem); #ifdef DEBUG printf("fc off %lu\n", tickGet()); #endif -} - +} +/* + * db_delete_field_log() + */ +void epicsShareAPI db_delete_field_log (db_field_log *pfl) +{ + if (pfl) { + /* Free field if reference type field log and dtor is set */ + if (pfl->type == dbfl_type_ref && pfl->u.r.dtor) pfl->u.r.dtor(pfl); + /* Free the field log chunk */ + freeListFree(dbevFieldLogFreeList, pfl); + } +} diff --git a/src/ioc/db/dbEvent.h b/src/ioc/db/dbEvent.h index db7186849..323a3d4e0 100644 --- a/src/ioc/db/dbEvent.h +++ b/src/ioc/db/dbEvent.h @@ -1,17 +1,19 @@ /*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. * Copyright (c) 2002 The University of Chicago, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ -/* - * $Revision-Id$ + +/* + * Author: Jeffrey O. Hill * - * Author: Jeff Hill - * Date: 030393 + * Ralph Lange */ #ifndef INCLdbEventh @@ -34,8 +36,9 @@ extern "C" { #endif -struct dbAddr; +struct dbChannel; struct db_field_log; +struct evSubscrip; epicsShareFunc int epicsShareAPI db_event_list ( const char *name, unsigned level); @@ -49,7 +52,7 @@ typedef void * dbEventCtx; typedef void EXTRALABORFUNC (void *extralabor_arg); epicsShareFunc dbEventCtx epicsShareAPI db_init_events (void); epicsShareFunc int epicsShareAPI db_start_events ( - dbEventCtx ctx, const char *taskname, void (*init_func)(void *), + dbEventCtx ctx, const char *taskname, void (*init_func)(void *), void *init_func_arg, unsigned osiPriority ); epicsShareFunc void epicsShareAPI db_close_events (dbEventCtx ctx); epicsShareFunc void epicsShareAPI db_event_flow_ctrl_mode_on (dbEventCtx ctx); @@ -60,18 +63,22 @@ epicsShareFunc void epicsShareAPI db_flush_extra_labor_event (dbEventCtx); epicsShareFunc int epicsShareAPI db_post_extra_labor (dbEventCtx ctx); epicsShareFunc void epicsShareAPI db_event_change_priority ( dbEventCtx ctx, unsigned epicsPriority ); -typedef void EVENTFUNC (void *user_arg, struct dbAddr *paddr, +typedef void EVENTFUNC (void *user_arg, struct dbChannel *chan, int eventsRemaining, struct db_field_log *pfl); typedef void * dbEventSubscription; epicsShareFunc dbEventSubscription epicsShareAPI db_add_event ( - dbEventCtx ctx, struct dbAddr *paddr, + dbEventCtx ctx, struct dbChannel *chan, EVENTFUNC *user_sub, void *user_arg, unsigned select); epicsShareFunc void epicsShareAPI db_cancel_event (dbEventSubscription es); epicsShareFunc void epicsShareAPI db_post_single_event (dbEventSubscription es); epicsShareFunc void epicsShareAPI db_event_enable (dbEventSubscription es); epicsShareFunc void epicsShareAPI db_event_disable (dbEventSubscription es); +epicsShareFunc struct db_field_log* epicsShareAPI db_create_event_log (struct evSubscrip *pevent); +epicsShareFunc struct db_field_log* epicsShareAPI db_create_read_log (struct dbChannel *chan); +epicsShareFunc void epicsShareAPI db_delete_field_log (struct db_field_log *pfl); + #define DB_EVENT_OK 0 #define DB_EVENT_ERROR (-1) diff --git a/src/ioc/db/dbExtractArray.c b/src/ioc/db/dbExtractArray.c new file mode 100644 index 000000000..29d532530 --- /dev/null +++ b/src/ioc/db/dbExtractArray.c @@ -0,0 +1,84 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + * + * based on dbConvert.c + * written by: Bob Dalesio, Marty Kraimer + */ + +#include + +#include "epicsTypes.h" +#include "dbAddr.h" +#define epicsExportSharedSymbols +#include "dbExtractArray.h" + +void dbExtractArrayFromRec(const dbAddr *paddr, void *pto, + long nRequest, long no_elements, long offset, long increment) +{ + char *pdst = (char *) pto; + char *psrc = (char *) paddr->pfield; + long nUpperPart; + int i; + short srcSize = paddr->field_size; + short dstSize = srcSize; + char isString = (paddr->field_type == DBF_STRING); + + if (nRequest > no_elements) nRequest = no_elements; + if (isString && srcSize > MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE; + + if (increment == 1 && dstSize == srcSize) { + nUpperPart = nRequest < no_elements - offset ? nRequest : no_elements - offset; + memcpy(pdst, &psrc[offset * srcSize], dstSize * nUpperPart); + if (nRequest > nUpperPart) + memcpy(&pdst[dstSize * nUpperPart], psrc, dstSize * (nRequest - nUpperPart)); + if (isString) + for (i = 1; i <= nRequest; i++) + pdst[dstSize*i-1] = '\0'; + } else { + for (; nRequest > 0; nRequest--, pdst += dstSize, offset += increment) { + offset %= no_elements; + memcpy(pdst, &psrc[offset*srcSize], dstSize); + if (isString) pdst[dstSize-1] = '\0'; + } + } +} + +void dbExtractArrayFromBuf(const void *pfrom, void *pto, + short field_size, short field_type, + long nRequest, long no_elements, long offset, long increment) +{ + char *pdst = (char *) pto; + char *psrc = (char *) pfrom; + int i; + short srcSize = field_size; + short dstSize = srcSize; + char isString = (field_type == DBF_STRING); + + if (nRequest > no_elements) nRequest = no_elements; + if (offset > no_elements - 1) offset = no_elements - 1; + if (isString && dstSize >= MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE - 1; + + if (increment == 1) { + memcpy(pdst, &psrc[offset * srcSize], dstSize * nRequest); + if (isString) + for (i = 1; i <= nRequest; i++) + pdst[dstSize*i] = '\0'; + } else { + for (; nRequest > 0; nRequest--, pdst += srcSize, offset += increment) { + memcpy(pdst, &psrc[offset*srcSize], dstSize); + if (isString) pdst[dstSize] = '\0'; + } + } +} diff --git a/src/ioc/db/dbExtractArray.h b/src/ioc/db/dbExtractArray.h new file mode 100644 index 000000000..7ed35847f --- /dev/null +++ b/src/ioc/db/dbExtractArray.h @@ -0,0 +1,34 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_dbExtractArray_H +#define INC_dbExtractArray_H + +#include "dbFldTypes.h" +#include "dbAddr.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareFunc void dbExtractArrayFromRec(const DBADDR *paddr, void *pto, + long nRequest, long no_elements, long offset, long increment); +epicsShareFunc void dbExtractArrayFromBuf(const void *pfrom, void *pto, + short field_size, short field_type, + long nRequest, long no_elements, long offset, long increment); + +#ifdef __cplusplus +} +#endif + +#endif // INC_dbExtractArray_H diff --git a/src/ioc/db/dbIocRegister.c b/src/ioc/db/dbIocRegister.c index 60965f200..58172bbb1 100644 --- a/src/ioc/db/dbIocRegister.c +++ b/src/ioc/db/dbIocRegister.c @@ -21,6 +21,7 @@ #include "dbNotify.h" #include "callback.h" #include "dbIocRegister.h" +#include "dbState.h" /* dbLoadDatabase */ static const iocshArg dbLoadDatabaseArg0 = { "file name",iocshArgString}; @@ -297,6 +298,57 @@ static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args) callbackSetQueueSize(args[0].ival); } +/* dbStateCreate */ +static const iocshArg dbStateArgName = { "name", iocshArgString }; +static const iocshArg * const dbStateCreateArgs[] = { &dbStateArgName }; +static const iocshFuncDef dbStateCreateFuncDef = { "dbStateCreate", 1, dbStateCreateArgs }; +static void dbStateCreateCallFunc (const iocshArgBuf *args) +{ + dbStateCreate(args[0].sval); +} + +/* dbStateSet */ +static const iocshArg * const dbStateSetArgs[] = { &dbStateArgName }; +static const iocshFuncDef dbStateSetFuncDef = { "dbStateSet", 1, dbStateSetArgs }; +static void dbStateSetCallFunc (const iocshArgBuf *args) +{ + dbStateId sid = dbStateFind(args[0].sval); + + if (sid) + dbStateSet(sid); +} + +/* dbStateClear */ +static const iocshArg * const dbStateClearArgs[] = { &dbStateArgName }; +static const iocshFuncDef dbStateClearFuncDef = { "dbStateClear", 1, dbStateClearArgs }; +static void dbStateClearCallFunc (const iocshArgBuf *args) +{ + dbStateId sid = dbStateFind(args[0].sval); + + if (sid) + dbStateClear(sid); +} + +/* dbStateShow */ +static const iocshArg dbStateShowArg1 = { "level", iocshArgInt }; +static const iocshArg * const dbStateShowArgs[] = { &dbStateArgName, &dbStateShowArg1 }; +static const iocshFuncDef dbStateShowFuncDef = { "dbStateShow", 2, dbStateShowArgs }; +static void dbStateShowCallFunc (const iocshArgBuf *args) +{ + dbStateId sid = dbStateFind(args[0].sval); + + if (sid) + dbStateShow(sid, args[1].ival); +} + +/* dbStateShowAll */ +static const iocshArg dbStateShowAllArg0 = { "level", iocshArgInt }; +static const iocshArg * const dbStateShowAllArgs[] = { &dbStateShowAllArg0 }; +static const iocshFuncDef dbStateShowAllFuncDef = { "dbStateShowAll", 1, dbStateShowAllArgs }; +static void dbStateShowAllCallFunc (const iocshArgBuf *args) +{ + dbStateShowAll(args[0].ival); +} void epicsShareAPI dbIocRegister(void) { @@ -342,4 +394,10 @@ void epicsShareAPI dbIocRegister(void) iocshRegister(&scanpiolFuncDef,scanpiolCallFunc); iocshRegister(&callbackSetQueueSizeFuncDef,callbackSetQueueSizeCallFunc); + + iocshRegister(&dbStateCreateFuncDef, dbStateCreateCallFunc); + iocshRegister(&dbStateSetFuncDef, dbStateSetCallFunc); + iocshRegister(&dbStateClearFuncDef, dbStateClearCallFunc); + iocshRegister(&dbStateShowFuncDef, dbStateShowCallFunc); + iocshRegister(&dbStateShowAllFuncDef, dbStateShowAllCallFunc); } diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c new file mode 100644 index 000000000..f4f442ba8 --- /dev/null +++ b/src/ioc/db/dbLink.c @@ -0,0 +1,648 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne + * National Laboratory. + * Copyright (c) 2002 The Regents of the University of California, as + * Operator of Los Alamos National Laboratory. + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + \*************************************************************************/ +/* dbLink.c */ +/* $Id$ */ +/* + * Original Authors: Bob Dalesio, Marty Kraimer + * Current Author: Andrew Johnson + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsThread.h" +#include "errlog.h" +#include "cantProceed.h" +#include "cvtFast.h" +#include "epicsTime.h" +#include "alarm.h" +#include "ellLib.h" +#include "dbStaticLib.h" +#include "dbBase.h" +#include "link.h" +#include "dbFldTypes.h" +#include "recSup.h" +#include "devSup.h" +#include "caeventmask.h" +#include "db_field_log.h" +#include "dbCommon.h" +#include "dbFldTypes.h" +#include "special.h" +#include "errMdef.h" +#define epicsExportSharedSymbols +#include "dbAddr.h" +#include "dbLink.h" +#include "callback.h" +#include "dbScan.h" +#include "dbLock.h" +#include "dbEvent.h" +#include "dbConvert.h" +#include "dbConvertFast.h" +#include "epicsEvent.h" +#include "dbCa.h" +#include "dbBkpt.h" +#include "dbNotify.h" +#include "dbAccessDefs.h" +#include "recGbl.h" + +static void inherit_severity(const struct pv_link *ppv_link, dbCommon *pdest, + epicsEnum16 stat, epicsEnum16 sevr) +{ + switch (ppv_link->pvlMask & pvlOptMsMode) { + case pvlOptNMS: + break; + case pvlOptMSI: + if (sevr < INVALID_ALARM) + break; + /* Fall through */ + case pvlOptMS: + recGblSetSevr(pdest, LINK_ALARM, sevr); + break; + case pvlOptMSS: + recGblSetSevr(pdest, stat, sevr); + break; + } +} + +/***************************** Constant Links *****************************/ + +static long dbConstLoadLink(struct link *plink, short dbrType, void *pbuffer) +{ + if (!plink->value.constantStr) + return S_db_badField; + + /* Constant strings are always numeric */ + if (dbrType== DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE) + dbrType = DBF_USHORT; + + return dbFastPutConvertRoutine[DBR_STRING][dbrType] + (plink->value.constantStr, pbuffer, NULL); +} + +static long dbConstGetNelements(const struct link *plink, long *nelements) +{ + *nelements = 0; + return 0; +} + +static long dbConstGetLink(struct link *plink, short dbrType, void *pbuffer, + epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest) +{ + if (pnRequest) + *pnRequest = 0; + return 0; +} + +/***************************** Database Links *****************************/ + +static long dbDbInitLink(struct link *plink, short dbfType) +{ + DBADDR dbaddr; + long status; + DBADDR *pdbAddr; + + status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr); + if (status) + return status; + + plink->type = DB_LINK; + pdbAddr = dbCalloc(1, sizeof(struct dbAddr)); + *pdbAddr = dbaddr; /* structure copy */ + plink->value.pv_link.pvt = pdbAddr; + return 0; +} + +static long dbDbAddLink(struct link *plink, short dbfType) +{ + long status = dbDbInitLink(plink, dbfType); + + if (!status) { + DBADDR *pdbAddr = (DBADDR *) plink->value.pv_link.pvt; + dbCommon *precord = plink->value.pv_link.precord; + + dbLockSetRecordLock(pdbAddr->precord); + dbLockSetMerge(precord, pdbAddr->precord); + } + return status; +} + +static void dbDbRemoveLink(struct link *plink) +{ + free(plink->value.pv_link.pvt); + plink->value.pv_link.pvt = 0; + plink->value.pv_link.getCvt = 0; + plink->value.pv_link.lastGetdbrType = 0; + plink->type = PV_LINK; + dbLockSetSplit(plink->value.pv_link.precord); +} + +static int dbDbIsLinkConnected(const struct link *plink) +{ + return TRUE; +} + +static int dbDbGetDBFtype(const struct link *plink) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + + return paddr->field_type; +} + +static long dbDbGetElements(const struct link *plink, long *nelements) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + + *nelements = paddr->no_elements; + return 0; +} + +static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, + epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest) +{ + struct pv_link *ppv_link = &plink->value.pv_link; + DBADDR *paddr = ppv_link->pvt; + dbCommon *precord = plink->value.pv_link.precord; + long status; + + /* scan passive records if link is process passive */ + if (ppv_link->pvlMask & pvlOptPP) { + unsigned char pact = precord->pact; + + precord->pact = TRUE; + status = dbScanPassive(precord, paddr->precord); + precord->pact = pact; + if (status) + return status; + } + *pstat = paddr->precord->stat; + *psevr = paddr->precord->sevr; + + if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) { + status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr); + } else { + unsigned short dbfType = paddr->field_type; + + if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE) + return S_db_badDbrtype; + + if (paddr->no_elements == 1 && (!pnRequest || *pnRequest == 1) + && paddr->special != SPC_ATTRIBUTE) { + ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType]; + status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr); + } else { + ppv_link->getCvt = NULL; + status = dbGet(paddr, dbrType, pbuffer, NULL, pnRequest, NULL); + } + ppv_link->lastGetdbrType = dbrType; + } + return status; +} + +static long dbDbGetControlLimits(const struct link *plink, double *low, + double *high) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + struct buffer { + DBRctrlDouble + double value; + } buffer; + long options = DBR_CTRL_DOUBLE; + long number_elements = 0; + long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, + NULL); + + if (status) + return status; + + *low = buffer.lower_ctrl_limit; + *high = buffer.upper_ctrl_limit; + return 0; +} + +static long dbDbGetGraphicLimits(const struct link *plink, double *low, + double *high) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + struct buffer { + DBRgrDouble + double value; + } buffer; + long options = DBR_GR_DOUBLE; + long number_elements = 0; + long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, + NULL); + + if (status) + return status; + + *low = buffer.lower_disp_limit; + *high = buffer.upper_disp_limit; + return 0; +} + +static long dbDbGetAlarmLimits(const struct link *plink, double *lolo, + double *low, double *high, double *hihi) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + struct buffer { + DBRalDouble + double value; + } buffer; + long options = DBR_AL_DOUBLE; + long number_elements = 0; + long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, + 0); + + if (status) + return status; + + *lolo = buffer.lower_alarm_limit; + *low = buffer.lower_warning_limit; + *high = buffer.upper_warning_limit; + *hihi = buffer.upper_alarm_limit; + return 0; +} + +static long dbDbGetPrecision(const struct link *plink, short *precision) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + struct buffer { + DBRprecision + double value; + } buffer; + long options = DBR_PRECISION; + long number_elements = 0; + long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, + 0); + + if (status) + return status; + + *precision = buffer.precision.dp; + return 0; +} + +static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + struct buffer { + DBRunits + double value; + } buffer; + long options = DBR_UNITS; + long number_elements = 0; + long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, + 0); + + if (status) + return status; + + strncpy(units, buffer.units, unitsSize); + return 0; +} + +static long dbDbGetAlarm(const struct link *plink, epicsEnum16 *status, + epicsEnum16 *severity) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + + if (status) + *status = paddr->precord->stat; + if (severity) + *severity = paddr->precord->sevr; + return 0; +} + +static long dbDbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + + *pstamp = paddr->precord->time; + return 0; +} + +static long dbDbPutValue(struct link *plink, short dbrType, + const void *pbuffer, long nRequest) +{ + struct pv_link *ppv_link = &plink->value.pv_link; + struct dbCommon *psrce = ppv_link->precord; + DBADDR *paddr = (DBADDR *) ppv_link->pvt; + dbCommon *pdest = paddr->precord; + long status = dbPut(paddr, dbrType, pbuffer, nRequest); + + inherit_severity(ppv_link, pdest, psrce->nsta, psrce->nsev); + if (status) + return status; + + if (paddr->pfield == (void *) &pdest->proc || + (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) { + /* if dbPutField caused asyn record to process */ + /* ask for reprocessing*/ + if (pdest->putf) { + pdest->rpro = TRUE; + } else { /* process dest record with source's PACT true */ + unsigned char pact; + + if (psrce && psrce->ppn) + dbNotifyAdd(psrce, pdest); + pact = psrce->pact; + psrce->pact = TRUE; + status = dbProcess(pdest); + psrce->pact = pact; + } + } + return status; +} + +static void dbDbScanFwdLink(struct link *plink) +{ + dbCommon *precord = plink->value.pv_link.precord; + dbAddr *paddr = (dbAddr *) plink->value.pv_link.pvt; + + dbScanPassive(precord, paddr->precord); +} + +lset dbDb_lset = { dbDbInitLink, dbDbAddLink, NULL, dbDbRemoveLink, + dbDbIsLinkConnected, dbDbGetDBFtype, dbDbGetElements, dbDbGetValue, + dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits, + dbDbGetPrecision, dbDbGetUnits, dbDbGetAlarm, dbDbGetTimeStamp, + dbDbPutValue, dbDbScanFwdLink }; + +/***************************** Generic Link API *****************************/ + +void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType) +{ + plink->value.pv_link.precord = precord; + + if (plink == &precord->tsel) + recGblTSELwasModified(plink); + + if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) { + /* Make it a DB link if possible */ + if (!dbDbInitLink(plink, dbfType)) + return; + } + + /* Make it a CA link */ + if (dbfType == DBF_INLINK) + plink->value.pv_link.pvlMask |= pvlOptInpNative; + + dbCaAddLink(plink); + if (dbfType == DBF_FWDLINK) { + char *pperiod = strrchr(plink->value.pv_link.pvname, '.'); + + if (pperiod && strstr(pperiod, "PROC")) { + plink->value.pv_link.pvlMask |= pvlOptFWD; + } else { + errlogPrintf("%s.FLNK is a Channel Access Link " + " but does not link to a PROC field\n", precord->name); + } + } +} + +void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType) +{ + plink->value.pv_link.precord = precord; + + if (plink == &precord->tsel) + recGblTSELwasModified(plink); + + if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) { + /* Can we make it a DB link? */ + if (!dbDbAddLink(plink, dbfType)) + return; + } + + /* Make it a CA link */ + if (dbfType == DBF_INLINK) + plink->value.pv_link.pvlMask |= pvlOptInpNative; + + dbCaAddLink(plink); + if (dbfType == DBF_FWDLINK) { + char *pperiod = strrchr(plink->value.pv_link.pvname, '.'); + + if (pperiod && strstr(pperiod, "PROC")) + plink->value.pv_link.pvlMask |= pvlOptFWD; + } +} + +long dbLoadLink(struct link *plink, short dbrType, void *pbuffer) +{ + switch (plink->type) { + case CONSTANT: + return dbConstLoadLink(plink, dbrType, pbuffer); + } + return S_db_notFound; +} + +void dbRemoveLink(struct link *plink) +{ + switch (plink->type) { + case DB_LINK: + dbDbRemoveLink(plink); + break; + case CA_LINK: + dbCaRemoveLink(plink); + break; + default: + cantProceed("dbRemoveLink: Unexpected link type %d\n", plink->type); + } + plink->type = PV_LINK; + plink->value.pv_link.pvlMask = 0; +} + +int dbIsLinkConnected(const struct link *plink) +{ + switch (plink->type) { + case DB_LINK: + return dbDbIsLinkConnected(plink); + case CA_LINK: + return dbCaIsLinkConnected(plink); + } + return FALSE; +} + +int dbGetLinkDBFtype(const struct link *plink) +{ + switch (plink->type) { + case DB_LINK: + return dbDbGetDBFtype(plink); + case CA_LINK: + return dbCaGetLinkDBFtype(plink); + } + return -1; +} + +long dbGetNelements(const struct link *plink, long *nelements) +{ + switch (plink->type) { + case CONSTANT: + return dbConstGetNelements(plink, nelements); + case DB_LINK: + return dbDbGetElements(plink, nelements); + case CA_LINK: + return dbCaGetNelements(plink, nelements); + } + return S_db_badField; +} + +long dbGetLinkValue(struct link *plink, short dbrType, void *pbuffer, + long *poptions, long *pnRequest) +{ + struct dbCommon *precord = plink->value.pv_link.precord; + epicsEnum16 sevr = 0, stat = 0; + long status; + + if (poptions && *poptions) { + printf("dbGetLinkValue: Use of poptions no longer supported\n"); + *poptions = 0; + } + + switch (plink->type) { + case CONSTANT: + status = dbConstGetLink(plink, dbrType, pbuffer, &stat, &sevr, + pnRequest); + break; + case DB_LINK: + status = dbDbGetValue(plink, dbrType, pbuffer, &stat, &sevr, pnRequest); + break; + case CA_LINK: + status = dbCaGetLink(plink, dbrType, pbuffer, &stat, &sevr, pnRequest); + break; + default: + cantProceed("dbGetLinkValue: Illegal link type %d\n", plink->type); + status = -1; + } + if (status) { + recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); + } else { + inherit_severity(&plink->value.pv_link, precord, stat, sevr); + } + return status; +} + +long dbGetControlLimits(const struct link *plink, double *low, double *high) +{ + switch (plink->type) { + case DB_LINK: + return dbDbGetControlLimits(plink, low, high); + case CA_LINK: + return dbCaGetControlLimits(plink, low, high); + } + return S_db_notFound; +} + +long dbGetGraphicLimits(const struct link *plink, double *low, double *high) +{ + switch (plink->type) { + case DB_LINK: + return dbDbGetGraphicLimits(plink, low, high); + case CA_LINK: + return dbCaGetGraphicLimits(plink, low, high); + } + return S_db_notFound; +} + +long dbGetAlarmLimits(const struct link *plink, double *lolo, double *low, + double *high, double *hihi) +{ + switch (plink->type) { + case DB_LINK: + return dbDbGetAlarmLimits(plink, lolo, low, high, hihi); + case CA_LINK: + return dbCaGetAlarmLimits(plink, lolo, low, high, hihi); + } + return S_db_notFound; +} + +long dbGetPrecision(const struct link *plink, short *precision) +{ + switch (plink->type) { + case DB_LINK: + return dbDbGetPrecision(plink, precision); + case CA_LINK: + return dbCaGetPrecision(plink, precision); + } + return S_db_notFound; +} + +long dbGetUnits(const struct link *plink, char *units, int unitsSize) +{ + switch (plink->type) { + case DB_LINK: + return dbDbGetUnits(plink, units, unitsSize); + case CA_LINK: + return dbCaGetUnits(plink, units, unitsSize); + } + return S_db_notFound; +} + +long dbGetAlarm(const struct link *plink, epicsEnum16 *status, + epicsEnum16 *severity) +{ + switch (plink->type) { + case DB_LINK: + return dbDbGetAlarm(plink, status, severity); + case CA_LINK: + return dbCaGetAlarm(plink, status, severity); + } + return S_db_notFound; +} + +long dbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp) +{ + switch (plink->type) { + case DB_LINK: + return dbDbGetTimeStamp(plink, pstamp); + case CA_LINK: + return dbCaGetTimeStamp(plink, pstamp); + } + return S_db_notFound; +} + +long dbPutLinkValue(struct link *plink, short dbrType, const void *pbuffer, + long nRequest) +{ + long status; + + switch (plink->type) { + case DB_LINK: + status = dbDbPutValue(plink, dbrType, pbuffer, nRequest); + break; + case CA_LINK: + status = dbCaPutLink(plink, dbrType, pbuffer, nRequest); + break; + default: + cantProceed("dbPutLinkValue: Illegal link type %d\n", plink->type); + status = -1; + } + if (status) { + struct dbCommon *precord = plink->value.pv_link.precord; + + recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); + } + return status; +} + +void dbScanFwdLink(struct link *plink) +{ + switch (plink->type) { + case DB_LINK: + dbDbScanFwdLink(plink); + break; + case CA_LINK: + dbCaScanFwdLink(plink); + break; + } +} + diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h new file mode 100644 index 000000000..aee2a848d --- /dev/null +++ b/src/ioc/db/dbLink.h @@ -0,0 +1,94 @@ +/*************************************************************************\ +* Copyright (c) 2010 The UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * dbLink.h + * + * Created on: Mar 21, 2010 + * Author: Andrew Johnson + */ + +#ifndef INC_dbLink_H +#define INC_dbLink_H + +#include "link.h" +#include "shareLib.h" +#include "epicsTypes.h" +#include "epicsTime.h" + +typedef struct lset { + long (*initLink)(struct link *plink, short dbfType); + long (*addLink)(struct link *plink, short dbfType); + long (*loadLink)(struct link *plink, short dbrType, void *pbuffer); + void (*removeLink)(struct link *plink); + int (*isLinkConnected)(const struct link *plink); + int (*getDBFtype)(const struct link *plink); + long (*getElements)(const struct link *plink, long *nelements); + long (*getValue)(struct link *plink, short dbrType, void *pbuffer, + epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest); + long (*getControlLimits)(const struct link *plink, double *lo, double *hi); + long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi); + long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo, + double *hi, double *hihi); + long (*getPrecision)(const struct link *plink, short *precision); + long (*getUnits)(const struct link *plink, char *units, int unitsSize); + long (*getAlarm)(const struct link *plink, epicsEnum16 *status, + epicsEnum16 *severity); + long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp); + long (*putValue)(struct link *plink, short dbrType, + const void *pbuffer, long nRequest); + void (*scanFwdLink)(struct link *plink); +} lset; + +#define dbGetLink(PLNK, DBRTYPE, PBUFFER, OPTIONS, NREQUEST) \ + ( ( ( (PLNK)->type == CONSTANT ) && \ + ( (NREQUEST) == 0) && \ + ( (OPTIONS) == 0) ) \ + ? 0 \ + : dbGetLinkValue((PLNK),(DBRTYPE), \ + (void *)(PBUFFER), (OPTIONS), (NREQUEST) ) ) + +#define dbPutLink(PLNK, DBRTYPE, PBUFFER, NREQUEST) \ + ( ( (PLNK)->type == CONSTANT) \ + ? 0 \ + : dbPutLinkValue( (PLNK), (DBRTYPE), (void *)(PBUFFER), (NREQUEST) ) ) + +#define dbGetSevr(PLINK, PSEVERITY) \ + dbGetAlarm((PLINK), NULL, (PSEVERITY)); + +epicsShareFunc void dbInitLink(struct dbCommon *precord, struct link *plink, + short dbfType); +epicsShareFunc void dbAddLink(struct dbCommon *precord, struct link *plink, + short dbfType); +epicsShareFunc long dbLoadLink(struct link *plink, short dbrType, + void *pbuffer); +epicsShareFunc void dbRemoveLink(struct link *plink); +epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements); +epicsShareFunc int dbIsLinkConnected(const struct link *plink); +epicsShareFunc int dbGetLinkDBFtype(const struct link *plink); +epicsShareFunc long dbGetLinkValue(struct link *, short dbrType, void *pbuffer, + long *options, long *nRequest); +epicsShareFunc long dbGetControlLimits(const struct link *plink, double *low, + double *high); +epicsShareFunc long dbGetGraphicLimits(const struct link *plink, double *low, + double *high); +epicsShareFunc long dbGetAlarmLimits(const struct link *plink, double *lolo, + double *low, double *high, double *hihi); +epicsShareFunc long dbGetPrecision(const struct link *plink, short *precision); +epicsShareFunc long dbGetUnits(const struct link *plink, char *units, + int unitsSize); +epicsShareFunc long dbGetAlarm(const struct link *plink, epicsEnum16 *status, + epicsEnum16 *severity); +epicsShareFunc long dbGetTimeStamp(const struct link *plink, + epicsTimeStamp *pstamp); +epicsShareFunc long dbPutLinkValue(struct link *, short dbrType, + const void *pbuffer, long nRequest); +epicsShareFunc void dbScanFwdLink(struct link *plink); + + +#endif /* INC_dbLink_H */ diff --git a/src/ioc/db/dbNotify.c b/src/ioc/db/dbNotify.c index 7454129dc..66974fa77 100644 --- a/src/ioc/db/dbNotify.c +++ b/src/ioc/db/dbNotify.c @@ -5,15 +5,15 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are 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. \*************************************************************************/ -/* dbNotify.c */ -/* base/src/db $Revision-Id$ */ + /* * Author: Marty Kraimer - * Date: 03-30-95 + * Andrew Johnson + * * Extracted from dbLink.c -*/ + */ #include #include @@ -26,6 +26,7 @@ #include "epicsAssert.h" #include "epicsEvent.h" #include "epicsMutex.h" +#include "epicsString.h" #include "epicsThread.h" #include "errlog.h" #include "errMdef.h" @@ -36,7 +37,7 @@ #include "dbCommon.h" #define epicsExportSharedSymbols #include "callback.h" -#include "dbAddr.h" +#include "dbChannel.h" #include "dbScan.h" #include "dbLock.h" #include "callback.h" @@ -45,7 +46,7 @@ #include "dbNotify.h" #include "epicsTime.h" #include "cantProceed.h" - + /*putNotify.state values */ typedef enum { putNotifyNotActive, @@ -55,44 +56,44 @@ typedef enum { putNotifyPutInProgress, putNotifyUserCallbackRequested, putNotifyUserCallbackActive -}putNotifyState; +} putNotifyState; /*structure attached to ppnr field of each record*/ typedef struct putNotifyRecord { - ellCheckNode waitNode; - ELLLIST restartList; /*list of putNotifys to restart*/ + ellCheckNode waitNode; + ELLLIST restartList; /*list of putNotifys to restart*/ dbCommon *precord; -}putNotifyRecord; +} putNotifyRecord; #define MAGIC 0xfedc0123 typedef struct putNotifyPvt { - ELLNODE node; /*For free list*/ - long magic; - short state; - CALLBACK callback; - ELLLIST waitList; /*list of records for current putNotify*/ - short cancelWait; - short userCallbackWait; + ELLNODE node; /*For free list*/ + long magic; + short state; + CALLBACK callback; + ELLLIST waitList; /*list of records for current putNotify*/ + short cancelWait; + short userCallbackWait; epicsEventId cancelEvent; epicsEventId userCallbackEvent; -}putNotifyPvt; +} putNotifyPvt; /* putNotify groups can span locksets if links are dynamically modified*/ /* Thus a global lock is taken while putNotify fields are accessed */ typedef struct notifyGlobal { epicsMutexId lock; ELLLIST freeList; -}notifyGlobal; +} notifyGlobal; static notifyGlobal *pnotifyGlobal = 0; - + /*Local routines*/ static void putNotifyInit(putNotify *ppn); static void putNotifyCleanup(putNotify *ppn); static void restartCheck(putNotifyRecord *ppnr); -static void callUser(dbCommon *precord,putNotify *ppn); +static void callUser(dbCommon *precord, putNotify *ppn); static void notifyCallback(CALLBACK *pcallback); -static void putNotifyCommon(putNotify *ppn,dbCommon *precord); +static void putNotifyCommon(putNotify *ppn, dbCommon *precord); #define ellSafeAdd(list,listnode) \ { \ @@ -112,9 +113,9 @@ static void putNotifyInit(putNotify *ppn) { putNotifyPvt *pputNotifyPvt; - pputNotifyPvt = (putNotifyPvt *)ellFirst(&pnotifyGlobal->freeList); - if(pputNotifyPvt) { - ellDelete(&pnotifyGlobal->freeList,&pputNotifyPvt->node); + pputNotifyPvt = (putNotifyPvt *) ellFirst(&pnotifyGlobal->freeList); + if (pputNotifyPvt) { + ellDelete(&pnotifyGlobal->freeList, &pputNotifyPvt->node); } else { pputNotifyPvt = dbCalloc(1,sizeof(putNotifyPvt)); pputNotifyPvt->cancelEvent = epicsEventCreate(epicsEventEmpty); @@ -135,26 +136,26 @@ static void putNotifyInit(putNotify *ppn) static void putNotifyCleanup(putNotify *ppn) { - putNotifyPvt *pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; + putNotifyPvt *pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; pputNotifyPvt->state = putNotifyNotActive; - ellAdd(&pnotifyGlobal->freeList,&pputNotifyPvt->node); + ellAdd(&pnotifyGlobal->freeList, &pputNotifyPvt->node); ppn->pputNotifyPvt = 0; } - + static void restartCheck(putNotifyRecord *ppnr) { dbCommon *precord = ppnr->precord; putNotify *pfirst; putNotifyPvt *pputNotifyPvt; - + assert(precord->ppn); - pfirst = (putNotify *)ellFirst(&ppnr->restartList); - if(!pfirst) { + pfirst = (putNotify *) ellFirst(&ppnr->restartList); + if (!pfirst) { precord->ppn = 0; return; } - pputNotifyPvt = (putNotifyPvt *)pfirst->pputNotifyPvt; + pputNotifyPvt = (putNotifyPvt *) pfirst->pputNotifyPvt; assert(pputNotifyPvt->state==putNotifyWaitForRestart); /* remove pfirst from restartList */ ellSafeDelete(&ppnr->restartList,&pfirst->restartNode); @@ -165,25 +166,25 @@ static void restartCheck(putNotifyRecord *ppnr) callbackRequest(&pputNotifyPvt->callback); } -static void callUser(dbCommon *precord,putNotify *ppn) +static void callUser(dbCommon *precord, putNotify *ppn) { - putNotifyPvt *pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; + putNotifyPvt *pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); (*ppn->userCallback)(ppn); epicsMutexMustLock(pnotifyGlobal->lock); - if(pputNotifyPvt->cancelWait && pputNotifyPvt->userCallbackWait) { + if (pputNotifyPvt->cancelWait && pputNotifyPvt->userCallbackWait) { errlogPrintf("%s putNotify: both cancelWait and userCallbackWait true." - "This is illegal\n",precord->name); + "This is illegal\n", precord->name); pputNotifyPvt->cancelWait = pputNotifyPvt->userCallbackWait = 0; } - if(!pputNotifyPvt->cancelWait && !pputNotifyPvt->userCallbackWait) { + if (!pputNotifyPvt->cancelWait && !pputNotifyPvt->userCallbackWait) { putNotifyCleanup(ppn); - epicsMutexUnlock(pnotifyGlobal->lock); + epicsMutexUnlock(pnotifyGlobal->lock); return; } - if(pputNotifyPvt->cancelWait) { + if (pputNotifyPvt->cancelWait) { pputNotifyPvt->cancelWait = 0; epicsEventSignal(pputNotifyPvt->cancelEvent); epicsMutexUnlock(pnotifyGlobal->lock); @@ -195,38 +196,37 @@ static void callUser(dbCommon *precord,putNotify *ppn) epicsMutexUnlock(pnotifyGlobal->lock); return; } - -static void putNotifyCommon(putNotify *ppn,dbCommon *precord) -{ - long status=0; - dbFldDes *pfldDes = ppn->paddr->pfldDes; - putNotifyPvt *pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; - if(precord->ppn && pputNotifyPvt->state!=putNotifyRestartCallbackRequested) - { /*another putNotify owns the record */ - pputNotifyPvt->state = putNotifyWaitForRestart; +static void putNotifyCommon(putNotify *ppn, dbCommon *precord) +{ + long status; + putNotifyPvt *pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + + if (precord->ppn && + pputNotifyPvt->state != putNotifyRestartCallbackRequested) { /*another putNotify owns the record */ + pputNotifyPvt->state = putNotifyWaitForRestart; ellSafeAdd(&precord->ppnr->restartList,&ppn->restartNode); epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); return; - } else if(precord->ppn){ - assert(precord->ppn==ppn); + } else if (precord->ppn) { + assert(precord->ppn == ppn); assert(pputNotifyPvt->state==putNotifyRestartCallbackRequested); } - if(precord->pact) { + if (precord->pact) { precord->ppn = ppn; ellSafeAdd(&pputNotifyPvt->waitList,&precord->ppnr->waitNode); - pputNotifyPvt->state = putNotifyRestartInProgress; + pputNotifyPvt->state = putNotifyRestartInProgress; epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); return; } - status=dbPut(ppn->paddr,ppn->dbrType,ppn->pbuffer,ppn->nRequest); - ppn->status = (status==0) ? putNotifyOK : putNotifyError; + status = dbChannelPut(ppn->chan, ppn->dbrType, ppn->pbuffer, ppn->nRequest); + ppn->status = (status == 0) ? putNotifyOK : putNotifyError; /* Check to see if dbProcess should not be called */ - if(!status /*dont process if dbPut returned error */ - &&((ppn->paddr->pfield==(void *)&precord->proc) /*If PROC call dbProcess*/ - || (pfldDes->process_passive && precord->scan==0))) { + if (!status /*dont process if dbPut returned error */ + && ((dbChannelField(ppn->chan) == (void *) & precord->proc) /*If PROC call dbProcess*/ + || (dbChannelFldDes(ppn->chan)->process_passive && precord->scan == 0))) { precord->ppn = ppn; ellSafeAdd(&pputNotifyPvt->waitList,&precord->ppnr->waitNode); pputNotifyPvt->state = putNotifyPutInProgress; @@ -235,31 +235,31 @@ static void putNotifyCommon(putNotify *ppn,dbCommon *precord) dbScanUnlock(precord); return; } - if(pputNotifyPvt->state==putNotifyRestartCallbackRequested) { + if (pputNotifyPvt->state == putNotifyRestartCallbackRequested) { restartCheck(precord->ppnr); } pputNotifyPvt->state = putNotifyUserCallbackActive; assert(precord->ppn!=ppn); - callUser(precord,ppn); + callUser(precord, ppn); } - + static void notifyCallback(CALLBACK *pcallback) { - putNotify *ppn=NULL; - dbCommon *precord; + putNotify *ppn = NULL; + dbCommon *precord; putNotifyPvt *pputNotifyPvt; callbackGetUser(ppn,pcallback); - pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; - precord = ppn->paddr->precord; + pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + precord = dbChannelRecord(ppn->chan); dbScanLock(precord); epicsMutexMustLock(pnotifyGlobal->lock); assert(precord->ppnr); assert(pputNotifyPvt->state==putNotifyRestartCallbackRequested - || pputNotifyPvt->state==putNotifyUserCallbackRequested); + || pputNotifyPvt->state==putNotifyUserCallbackRequested); assert(ellCount(&pputNotifyPvt->waitList)==0); - if(pputNotifyPvt->cancelWait) { - if(pputNotifyPvt->state==putNotifyRestartCallbackRequested) { + if (pputNotifyPvt->cancelWait) { + if (pputNotifyPvt->state == putNotifyRestartCallbackRequested) { restartCheck(precord->ppnr); } epicsEventSignal(pputNotifyPvt->cancelEvent); @@ -267,58 +267,61 @@ static void notifyCallback(CALLBACK *pcallback) dbScanUnlock(precord); return; } - if(pputNotifyPvt->state==putNotifyRestartCallbackRequested) { - putNotifyCommon(ppn,precord); + if (pputNotifyPvt->state == putNotifyRestartCallbackRequested) { + putNotifyCommon(ppn, precord); return; } /* All done. Clean up and call userCallback */ pputNotifyPvt->state = putNotifyUserCallbackActive; assert(precord->ppn!=ppn); - callUser(precord,ppn); + callUser(precord, ppn); } void epicsShareAPI dbPutNotifyInit(void) { - if(pnotifyGlobal) return; + if (pnotifyGlobal) + return; pnotifyGlobal = dbCalloc(1,sizeof(notifyGlobal)); pnotifyGlobal->lock = epicsMutexMustCreate(); ellInit(&pnotifyGlobal->freeList); } - + void epicsShareAPI dbPutNotify(putNotify *ppn) { - dbCommon *precord = ppn->paddr->precord; - short dbfType = ppn->paddr->field_type; - long status=0; + struct dbChannel *chan = ppn->chan; + dbCommon *precord = dbChannelRecord(chan); + short dbfType = dbChannelFieldType(chan); + long status = 0; putNotifyPvt *pputNotifyPvt; assert(precord); /*check for putField disabled*/ - if(precord->disp) { - if((void *)(&precord->disp) != ppn->paddr->pfield) { - ppn->status = putNotifyPutDisabled; - (*ppn->userCallback)(ppn); - return; + if (precord->disp) { + if (dbChannelField(chan) != (void *) & precord->disp) { + ppn->status = putNotifyPutDisabled; + (*ppn->userCallback)(ppn); + return; } } /* Must handle DBF_XXXLINKs as special case. - * Only dbPutField will change link fields. + * Only dbPutField will change link fields. * Also the record is not processed as a result - */ - if(dbfType>=DBF_INLINK && dbfType<=DBF_FWDLINK) { - status=dbPutField(ppn->paddr,ppn->dbrType,ppn->pbuffer,ppn->nRequest); - ppn->status = (status==0) ? putNotifyOK : putNotifyError; + */ + if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) { + status = dbChannelPutField(ppn->chan, ppn->dbrType, ppn->pbuffer, + ppn->nRequest); + ppn->status = (status == 0) ? putNotifyOK : putNotifyError; (*ppn->userCallback)(ppn); return; } dbScanLock(precord); epicsMutexMustLock(pnotifyGlobal->lock); - pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; - if(pputNotifyPvt && (pputNotifyPvt->magic!=MAGIC)) { + pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + if (pputNotifyPvt && (pputNotifyPvt->magic != MAGIC)) { printf("dbPutNotify:pputNotifyPvt was not initialized\n"); pputNotifyPvt = 0; } - if(pputNotifyPvt) { + if (pputNotifyPvt) { assert(pputNotifyPvt->state==putNotifyUserCallbackActive); pputNotifyPvt->userCallbackWait = 1; epicsMutexUnlock(pnotifyGlobal->lock); @@ -328,38 +331,38 @@ void epicsShareAPI dbPutNotify(putNotify *ppn) epicsMutexMustLock(pnotifyGlobal->lock); putNotifyCleanup(ppn); } - pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; + pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; assert(!pputNotifyPvt); putNotifyInit(ppn); - pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; - if(!precord->ppnr) {/* make sure record has a putNotifyRecord*/ + pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + if (!precord->ppnr) {/* make sure record has a putNotifyRecord*/ precord->ppnr = dbCalloc(1,sizeof(putNotifyRecord)); precord->ppnr->precord = precord; ellInit(&precord->ppnr->restartList); } - putNotifyCommon(ppn,precord); + putNotifyCommon(ppn, precord); } - + void epicsShareAPI dbNotifyCancel(putNotify *ppn) { - dbCommon *precord = ppn->paddr->precord; + dbCommon *precord = dbChannelRecord(ppn->chan); putNotifyState state; putNotifyPvt *pputNotifyPvt; assert(precord); dbScanLock(precord); epicsMutexMustLock(pnotifyGlobal->lock); - pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; - if(!pputNotifyPvt || pputNotifyPvt->state==putNotifyNotActive) { + pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + if (!pputNotifyPvt || pputNotifyPvt->state == putNotifyNotActive) { epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); return; } state = pputNotifyPvt->state; /*If callback is scheduled or active wait for it to complete*/ - if(state==putNotifyUserCallbackRequested - || state==putNotifyRestartCallbackRequested - || state==putNotifyUserCallbackActive) { + if (state == putNotifyUserCallbackRequested || state + == putNotifyRestartCallbackRequested || state + == putNotifyUserCallbackActive) { pputNotifyPvt->cancelWait = 1; epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); @@ -369,24 +372,26 @@ void epicsShareAPI dbNotifyCancel(putNotify *ppn) epicsMutexUnlock(pnotifyGlobal->lock); return; } - switch(state) { - case putNotifyNotActive: break; + switch (state) { + case putNotifyNotActive: + break; case putNotifyWaitForRestart: assert(precord->ppn); assert(precord->ppn!=ppn); - ellSafeDelete(&precord->ppnr->restartList,&ppn->restartNode); + ellSafeDelete(&precord->ppnr->restartList,&ppn->restartNode) + ; break; case putNotifyRestartInProgress: - case putNotifyPutInProgress: - { /*Take all records out of wait list */ - putNotifyRecord *ppnrWait; + case putNotifyPutInProgress: { /*Take all records out of wait list */ + putNotifyRecord *ppnrWait; - while((ppnrWait = (putNotifyRecord *)ellFirst(&pputNotifyPvt->waitList))){ - ellSafeDelete(&pputNotifyPvt->waitList,&ppnrWait->waitNode); - restartCheck(ppnrWait); - } + while ((ppnrWait = (putNotifyRecord *) ellFirst(&pputNotifyPvt->waitList))) { + ellSafeDelete(&pputNotifyPvt->waitList,&ppnrWait->waitNode); + restartCheck(ppnrWait); } - if(precord->ppn==ppn) restartCheck(precord->ppnr); + } + if (precord->ppn == ppn) + restartCheck(precord->ppnr); break; default: printf("dbNotify: illegal state for notifyCallback\n"); @@ -396,29 +401,29 @@ void epicsShareAPI dbNotifyCancel(putNotify *ppn) epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); } - + void epicsShareAPI dbNotifyCompletion(dbCommon *precord) { - putNotify *ppn = precord->ppn; + putNotify *ppn = precord->ppn; putNotifyPvt *pputNotifyPvt; epicsMutexMustLock(pnotifyGlobal->lock); assert(ppn); assert(precord->ppnr); - pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; - if(pputNotifyPvt->state!=putNotifyRestartInProgress - && pputNotifyPvt->state!=putNotifyPutInProgress) { + pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + if (pputNotifyPvt->state != putNotifyRestartInProgress + && pputNotifyPvt->state != putNotifyPutInProgress) { epicsMutexUnlock(pnotifyGlobal->lock); return; } ellSafeDelete(&pputNotifyPvt->waitList,&precord->ppnr->waitNode); - if((ellCount(&pputNotifyPvt->waitList)!=0)) { + if ((ellCount(&pputNotifyPvt->waitList)!=0)) { restartCheck(precord->ppnr); - } else if(pputNotifyPvt->state == putNotifyPutInProgress) { + } else if (pputNotifyPvt->state == putNotifyPutInProgress) { pputNotifyPvt->state = putNotifyUserCallbackRequested; restartCheck(precord->ppnr); callbackRequest(&pputNotifyPvt->callback); - } else if(pputNotifyPvt->state == putNotifyRestartInProgress) { + } else if (pputNotifyPvt->state == putNotifyRestartInProgress) { pputNotifyPvt->state = putNotifyRestartCallbackRequested; callbackRequest(&pputNotifyPvt->callback); } else { @@ -432,142 +437,140 @@ void epicsShareAPI dbNotifyAdd(dbCommon *pfrom, dbCommon *pto) putNotify *ppn = pfrom->ppn; putNotifyPvt *pputNotifyPvt; - if(pto->pact) return; /*if active it will not be processed*/ + if (pto->pact) + return; /*if active it will not be processed*/ epicsMutexMustLock(pnotifyGlobal->lock); - if(!pto->ppnr) {/* make sure record has a putNotifyRecord*/ + if (!pto->ppnr) {/* make sure record has a putNotifyRecord*/ pto->ppnr = dbCalloc(1,sizeof(putNotifyRecord)); pto->ppnr->precord = pto; ellInit(&pto->ppnr->restartList); } assert(ppn); - pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; - if(!(pto->ppn) - && (pputNotifyPvt->state==putNotifyPutInProgress) - && (pto!=ppn->paddr->precord)) { + pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + if (!pto->ppn && + (pputNotifyPvt->state == putNotifyPutInProgress) && + (pto != dbChannelRecord(ppn->chan))) { putNotifyPvt *pputNotifyPvt; pto->ppn = pfrom->ppn; - pputNotifyPvt = (putNotifyPvt *)pfrom->ppn->pputNotifyPvt; + pputNotifyPvt = (putNotifyPvt *) pfrom->ppn->pputNotifyPvt; ellSafeAdd(&pputNotifyPvt->waitList,&pto->ppnr->waitNode); } epicsMutexUnlock(pnotifyGlobal->lock); } - + typedef struct tpnInfo { epicsEventId callbackDone; - putNotify *ppn; -}tpnInfo; + putNotify *ppn; +} tpnInfo; static void dbtpnCallback(putNotify *ppn) { putNotifyStatus status = ppn->status; - tpnInfo *ptpnInfo = (tpnInfo *)ppn->usrPvt; + tpnInfo *ptpnInfo = (tpnInfo *) ppn->usrPvt; + const char *pname = dbChannelRecord(ppn->chan)->name; - if(status==0) - printf("dbtpnCallback: success record=%s\n",ppn->paddr->precord->name); + if (status == 0) + printf("dbtpnCallback: success record=%s\n", pname); else - printf("%s dbtpnCallback putNotify.status %d\n",ppn->paddr->precord->name,(int)status); + printf("%s dbtpnCallback putNotify.status %d\n", + pname, (int) status); epicsEventSignal(ptpnInfo->callbackDone); } static void tpnThread(void *pvt) { - tpnInfo *ptpnInfo = (tpnInfo *)pvt; - putNotify *ppn = (putNotify *)ptpnInfo->ppn; + tpnInfo *ptpnInfo = (tpnInfo *) pvt; + putNotify *ppn = (putNotify *) ptpnInfo->ppn; dbPutNotify(ppn); epicsEventWait(ptpnInfo->callbackDone); dbNotifyCancel(ppn); epicsEventDestroy(ptpnInfo->callbackDone); - free((void *)ppn->paddr); + free(ppn->pbuffer); + dbChannelDelete(ppn->chan); free(ppn); free(ptpnInfo); } -long epicsShareAPI dbtpn(char *pname,char *pvalue) +long epicsShareAPI dbtpn(char *pname, char *pvalue) { - long status; - tpnInfo *ptpnInfo; - DBADDR *pdbaddr=NULL; - putNotify *ppn=NULL; - char *psavevalue; - int len; + struct dbChannel *chan; + tpnInfo *ptpnInfo; + putNotify *ppn; + char *pbuffer; - len = strlen(pvalue); - /*allocate space for value immediately following DBADDR*/ - pdbaddr = dbCalloc(1,sizeof(DBADDR) + len+1); - psavevalue = (char *)(pdbaddr + 1); - strcpy(psavevalue,pvalue); - status = dbNameToAddr(pname,pdbaddr); - if(status) { - errMessage(status, "dbtpn: dbNameToAddr"); - free((void *)pdbaddr); - return(-1); + pbuffer = epicsStrDup(pvalue); + chan = dbChannelCreate(pname); + if (!chan) { + printf("dbtpn: No such channel"); + return -1; } - ppn = dbCalloc(1,sizeof(putNotify)); - ppn->paddr = pdbaddr; - ppn->pbuffer = psavevalue; + + ppn = dbCalloc(1, sizeof(putNotify)); + ppn->chan = chan; + ppn->pbuffer = pbuffer; ppn->nRequest = 1; ppn->dbrType = DBR_STRING; ppn->userCallback = dbtpnCallback; - ptpnInfo = dbCalloc(1,sizeof(tpnInfo)); + ptpnInfo = dbCalloc(1, sizeof(tpnInfo)); ptpnInfo->ppn = ppn; ptpnInfo->callbackDone = epicsEventCreate(epicsEventEmpty); ppn->usrPvt = ptpnInfo; - epicsThreadCreate("dbtpn",epicsThreadPriorityHigh, - epicsThreadGetStackSize(epicsThreadStackMedium), - tpnThread,ptpnInfo); - return(0); + epicsThreadCreate("dbtpn", epicsThreadPriorityHigh, + epicsThreadGetStackSize(epicsThreadStackMedium), tpnThread, ptpnInfo); + return 0; } - + int epicsShareAPI dbNotifyDump(void) { epicsMutexLockStatus lockStatus; dbRecordType *pdbRecordType; dbRecordNode *pdbRecordNode; - dbCommon *precord; - putNotify *ppn; - putNotify *ppnRestart; + dbCommon *precord; + putNotify *ppn; + putNotify *ppnRestart; putNotifyRecord *ppnrWait; int itry; - - - for(itry=0; itry<100; itry++) { + + for (itry = 0; itry < 100; itry++) { lockStatus = epicsMutexTryLock(pnotifyGlobal->lock); - if(lockStatus==epicsMutexLockOK) break; + if (lockStatus == epicsMutexLockOK) + break; epicsThreadSleep(.05); } - for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); - pdbRecordType; - pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { - for (pdbRecordNode=(dbRecordNode *)ellFirst(&pdbRecordType->recList); - pdbRecordNode; - pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { + for (pdbRecordType = (dbRecordType *) ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType + = (dbRecordType *) ellNext(&pdbRecordType->node)) { + for (pdbRecordNode = (dbRecordNode *) ellFirst(&pdbRecordType->recList); pdbRecordNode; pdbRecordNode + = (dbRecordNode *) ellNext(&pdbRecordNode->node)) { putNotifyPvt *pputNotifyPvt; precord = pdbRecordNode->precord; - if (!precord->name[0] || - pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) + if (!precord->name[0] || pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) + continue; + if (!precord->ppn) + continue; + if (!precord->ppnr) + continue; + if (dbChannelRecord(precord->ppn->chan) != precord) continue; - if(!precord->ppn) continue; - if(!precord->ppnr) continue; - if(precord->ppn->paddr->precord != precord) continue; ppn = precord->ppn; - pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; - printf("%s state %d ppn %p\n waitList\n", - precord->name,pputNotifyPvt->state,(void*)ppn); - ppnrWait = (putNotifyRecord *)ellFirst(&pputNotifyPvt->waitList); - while(ppnrWait) { - printf(" %s pact %d\n", - ppnrWait->precord->name,ppnrWait->precord->pact); - ppnrWait = (putNotifyRecord *)ellNext(&ppnrWait->waitNode.node); + pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + printf("%s state %d ppn %p\n waitList\n", precord->name, + pputNotifyPvt->state, (void*) ppn); + ppnrWait = (putNotifyRecord *) ellFirst(&pputNotifyPvt->waitList); + while (ppnrWait) { + printf(" %s pact %d\n", ppnrWait->precord->name, + ppnrWait->precord->pact); + ppnrWait = (putNotifyRecord *) ellNext(&ppnrWait->waitNode.node); } printf(" restartList\n"); - ppnRestart = (putNotify *)ellFirst(&precord->ppnr->restartList); - while(ppnRestart) { - printf(" %p\n", (void *)ppnRestart); - ppnRestart = (putNotify *)ellNext(&ppnRestart->restartNode.node); + ppnRestart = (putNotify *) ellFirst(&precord->ppnr->restartList); + while (ppnRestart) { + printf(" %p\n", (void *) ppnRestart); + ppnRestart = (putNotify *) ellNext(&ppnRestart->restartNode.node); } } } - if(lockStatus==epicsMutexLockOK) epicsMutexUnlock(pnotifyGlobal->lock); - return(0); + if (lockStatus == epicsMutexLockOK) + epicsMutexUnlock(pnotifyGlobal->lock); + return (0); } diff --git a/src/ioc/db/dbNotify.h b/src/ioc/db/dbNotify.h index 5c57c0d90..b7ab1460f 100644 --- a/src/ioc/db/dbNotify.h +++ b/src/ioc/db/dbNotify.h @@ -5,7 +5,7 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are 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. \*************************************************************************/ /* dbNotify.h */ @@ -18,12 +18,12 @@ #include "callback.h" #ifdef __cplusplus - /* for brain dead C++ compilers */ - struct dbCommon; - struct putNotify; extern "C" { #endif - + +struct dbCommon; +struct putNotify; + typedef struct ellCheckNode{ ELLNODE node; int isOnList; @@ -40,7 +40,7 @@ typedef struct putNotify{ ellCheckNode restartNode; /*The following members MUST be set by user*/ void (*userCallback)(struct putNotify *); - struct dbAddr *paddr; /*dbAddr set by dbNameToAddr*/ + struct dbChannel *chan; /*dbChannel*/ void *pbuffer; /*address of data*/ long nRequest; /*number of elements to be written*/ short dbrType; /*database request type*/ @@ -55,7 +55,7 @@ typedef struct putNotify{ epicsShareFunc void epicsShareAPI dbPutNotify(putNotify *pputNotify); epicsShareFunc void epicsShareAPI dbNotifyCancel(putNotify *pputNotify); -/*dbPutNotifyMapType convience function for old database access*/ +/*dbPutNotifyMapType convenience function for old database access*/ epicsShareFunc int epicsShareAPI dbPutNotifyMapType (putNotify *ppn, short oldtype); /* dbPutNotifyInit called by iocInit */ @@ -70,9 +70,9 @@ epicsShareFunc void epicsShareAPI dbNotifyCompletion(struct dbCommon *precord); /* dbtpn is test routine for put notify */ epicsShareFunc long epicsShareAPI dbtpn(char *recordname,char *value); -/* dbNotifyDump is an INVASIVE debug utility. Dont use this needlessly*/ +/* dbNotifyDump is an INVASIVE debug utility. Don't use this needlessly*/ epicsShareFunc int epicsShareAPI dbNotifyDump(void); - + /* This module provides code to handle put notify. If a put causes a record to * be processed, then a user supplied callback is called when that record * and all records processed because of that record complete processing. @@ -87,8 +87,8 @@ epicsShareFunc int epicsShareAPI dbNotifyDump(void); * * After dbPutNotify is called it may not called for the same putNotify * until the putCallback is complete. The use can call dbNotifyCancel - * to cancel the operation. - * + * to cancel the operation. + * * The user callback is called when the operation is completed. * * The other global routines (dbNotifyAdd and dbNotifyCompletion) are called by: diff --git a/src/ioc/db/dbPutNotifyBlocker.cpp b/src/ioc/db/dbPutNotifyBlocker.cpp index 38e505a43..4f9f7143b 100644 --- a/src/ioc/db/dbPutNotifyBlocker.cpp +++ b/src/ioc/db/dbPutNotifyBlocker.cpp @@ -5,21 +5,21 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are 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. \*************************************************************************/ -/* +/* * $Revision-Id$ * - * + * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 - * + * * Copyright, 1986, The Regents of the University of California. - * - * - * Author: + * + * + * Author: * Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 @@ -47,7 +47,7 @@ #include "dbPutNotifyBlocker.h" dbPutNotifyBlocker::dbPutNotifyBlocker ( epicsMutex & mutexIn ) : - mutex ( mutexIn ), pNotify ( 0 ), + mutex ( mutexIn ), pNotify ( 0 ), maxValueSize ( sizeof ( this->dbrScalarValue ) ) { memset ( & this->pn, '\0', sizeof ( this->pn ) ); @@ -55,7 +55,7 @@ dbPutNotifyBlocker::dbPutNotifyBlocker ( epicsMutex & mutexIn ) : this->pn.pbuffer = & this->dbrScalarValue; } -dbPutNotifyBlocker::~dbPutNotifyBlocker () +dbPutNotifyBlocker::~dbPutNotifyBlocker () { } @@ -70,7 +70,7 @@ void dbPutNotifyBlocker::destructor ( epicsGuard < epicsMutex > & guard ) this->~dbPutNotifyBlocker (); } -void dbPutNotifyBlocker::cancel ( +void dbPutNotifyBlocker::cancel ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); @@ -82,7 +82,7 @@ void dbPutNotifyBlocker::cancel ( this->block.signal (); } -void dbPutNotifyBlocker::expandValueBuf ( +void dbPutNotifyBlocker::expandValueBuf ( epicsGuard < epicsMutex > & guard, unsigned long newSize ) { guard.assertIdenticalMutex ( this->mutex ); @@ -127,9 +127,9 @@ extern "C" void putNotifyCompletion ( putNotify *ppn ) } } -void dbPutNotifyBlocker::initiatePutNotify ( - epicsGuard < epicsMutex > & guard, cacWriteNotify & notify, - struct dbAddr & addr, unsigned type, unsigned long count, +void dbPutNotifyBlocker::initiatePutNotify ( + epicsGuard < epicsMutex > & guard, cacWriteNotify & notify, + struct dbChannel * dbch, unsigned type, unsigned long count, const void * pValue ) { guard. assertIdenticalMutex ( this->mutex ); @@ -163,7 +163,7 @@ void dbPutNotifyBlocker::initiatePutNotify ( throw cacChannel::badType(); } - int status = dbPutNotifyMapType ( + int status = dbPutNotifyMapType ( &this->pn, static_cast ( type ) ); if ( status ) { this->pNotify = 0; @@ -171,7 +171,7 @@ void dbPutNotifyBlocker::initiatePutNotify ( } this->pn.nRequest = static_cast < unsigned > ( count ); - this->pn.paddr = &addr; + this->pn.chan = dbch; this->pn.userCallback = putNotifyCompletion; this->pn.usrPvt = this; @@ -191,29 +191,29 @@ void dbPutNotifyBlocker::show ( unsigned level ) const this->show ( guard, level ); } -void dbPutNotifyBlocker::show ( +void dbPutNotifyBlocker::show ( epicsGuard < epicsMutex > &, unsigned level ) const { - printf ( "put notify blocker at %p\n", + printf ( "put notify blocker at %p\n", static_cast ( this ) ); if ( level > 0u ) { this->block.show ( level - 1u ); } } -dbSubscriptionIO * dbPutNotifyBlocker::isSubscription () +dbSubscriptionIO * dbPutNotifyBlocker::isSubscription () { return 0; } -void * dbPutNotifyBlocker::operator new ( size_t size, +void * dbPutNotifyBlocker::operator new ( size_t size, tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE -void dbPutNotifyBlocker::operator delete ( void *pCadaver, +void dbPutNotifyBlocker::operator delete ( void *pCadaver, tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); diff --git a/src/ioc/db/dbPutNotifyBlocker.h b/src/ioc/db/dbPutNotifyBlocker.h index 620a86da3..adbb9a73a 100644 --- a/src/ioc/db/dbPutNotifyBlocker.h +++ b/src/ioc/db/dbPutNotifyBlocker.h @@ -5,20 +5,20 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are 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. \*************************************************************************/ -/* +/* * $Revision-Id$ * - * + * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 - * + * * Copyright, 1986, The Regents of the University of California. - * - * + * + * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 @@ -43,24 +43,24 @@ class dbPutNotifyBlocker : public dbBaseIO { public: dbPutNotifyBlocker ( epicsMutex & ); void destructor ( epicsGuard < epicsMutex > & ); - void initiatePutNotify ( epicsGuard < epicsMutex > &, - cacWriteNotify &, struct dbAddr &, + void initiatePutNotify ( epicsGuard < epicsMutex > &, + cacWriteNotify &, struct dbChannel *, unsigned type, unsigned long count, const void * pValue ); void cancel ( epicsGuard < epicsMutex > & ); void show ( epicsGuard < epicsMutex > &, unsigned level ) const; void show ( unsigned level ) const; - void * operator new ( size_t size, + void * operator new ( size_t size, tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & ); - epicsPlacementDeleteOperator (( void *, + epicsPlacementDeleteOperator (( void *, tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & )) private: putNotify pn; // - // Include a union of all scalar types + // Include a union of all scalar types // including fixed length strings so - // that in many cases we can avoid + // that in many cases we can avoid // allocating another buffer - // + // union { dbr_string_t strval; dbr_short_t shrtval; @@ -76,7 +76,7 @@ private: cacWriteNotify * pNotify; unsigned long maxValueSize; dbSubscriptionIO * isSubscription (); - void expandValueBuf ( + void expandValueBuf ( epicsGuard < epicsMutex > &, unsigned long newSize ); friend void putNotifyCompletion ( putNotify * ppn ); dbPutNotifyBlocker ( const dbPutNotifyBlocker & ); diff --git a/src/ioc/db/dbState.c b/src/ioc/db/dbState.c new file mode 100644 index 000000000..1d452a04f --- /dev/null +++ b/src/ioc/db/dbState.c @@ -0,0 +1,108 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include +#include + +#include "iocsh.h" +#include "epicsString.h" +#include "epicsMutex.h" +#include "ellLib.h" +#include "dbStaticLib.h" +#include "dbDefs.h" + +#include "dbState.h" + +static ELLLIST states = ELLLIST_INIT; + +typedef struct dbState { + ELLNODE node; + int status; + char *name; + epicsMutexId lock; /* FIXME: Use atomic operations instead */ +} dbState; + +dbStateId dbStateFind(const char *name) +{ + ELLNODE *node; + dbStateId id; + + for (node = ellFirst(&states); node; node = ellNext(node)) { + id = CONTAINER(node, dbState, node); + if (strcmp(id->name, name) == 0) + return id; + } + return NULL; +} + +dbStateId dbStateCreate(const char *name) +{ + dbStateId id; + + if ((id = dbStateFind(name))) + return id; + + id = callocMustSucceed(1, sizeof(dbState), "createDbState"); + id->name = epicsStrDup(name); + id->lock = epicsMutexMustCreate(); + ellAdd(&states, &id->node); + + return id; +} + +void dbStateSet(dbStateId id) +{ + if (!id) + return; + epicsMutexMustLock(id->lock); + id->status = 1; + epicsMutexUnlock(id->lock); +} + +void dbStateClear(dbStateId id) +{ + if (!id) + return; + epicsMutexMustLock(id->lock); + id->status = 0; + epicsMutexUnlock(id->lock); +} + +int dbStateGet(dbStateId id) +{ + int status; + + if (!id) + return 0; + epicsMutexMustLock(id->lock); + status = id->status; + epicsMutexUnlock(id->lock); + return status; +} + +void dbStateShow(dbStateId id, unsigned int level) +{ + if (level >=1) + printf("id %p '%s' : ", id, id->name); + printf("%s\n", dbStateGet(id) ? "TRUE" : "FALSE"); +} + +void dbStateShowAll(unsigned int level) +{ + ELLNODE *node; + dbStateId id; + + for (node = ellFirst(&states); node; node = ellNext(node)) { + id = CONTAINER(node, dbState, node); + dbStateShow(id, level+1); + } +} diff --git a/src/ioc/db/dbState.h b/src/ioc/db/dbState.h new file mode 100644 index 000000000..abd23259e --- /dev/null +++ b/src/ioc/db/dbState.h @@ -0,0 +1,92 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#ifndef INCdbStateH +#define INCdbStateH + +#include "shareLib.h" + +/** @file dbState.h + * @brief Generic IOC state facility + * + * This library provides a simple global flag facility that can be used to + * synchronize e.g. plugins with IOC-wide states, that may be derived from + * events (either soft events or hard events coming from specialized timing + * and event hardware). + * + * A subset of this API is provided as IOC Shell commands to allow + * command line debugging. + * + */ + +typedef struct dbState *dbStateId; + +/** @brief Create db state. + * + * Creates a new db state with the specified 'name', returning the new id. + * If state with that name already exists, the existing state's id is returned. + * + * Also provided as an IOC Shell command. + * + * @param name Db state name. + * @return Id of db state, NULL for failure. + */ +epicsShareFunc dbStateId dbStateCreate(const char *name); + +/** @brief Find db state. + * + * @param name Db state name. + * @return Id of db state, NULL if not found. + */ +epicsShareFunc dbStateId dbStateFind(const char *name); + +/** @brief Set db state to TRUE. + * + * Also provided as an IOC Shell command. + * + * @param id Db state id. + */ +epicsShareFunc void dbStateSet(dbStateId id); + +/** @brief Set db state to FALSE. + * + * Also provided as an IOC Shell command. + * + * @param id Db state id. + */ +epicsShareFunc void dbStateClear(dbStateId id); + +/** @brief Get db state. + * + * @param id Db state id. + * @return Current db state (0|1). + */ +epicsShareFunc int dbStateGet(dbStateId id); + +/** @brief Print info about db state. + * + * Also provided as an IOC Shell command. + * + * @param id Db state id. + * @param level Interest level. + */ +epicsShareFunc void dbStateShow(dbStateId id, unsigned int level); + +/** @brief Print info about all db states. + * + * Also provided as an IOC Shell command. + * + * @param level Interest level. + */ +epicsShareFunc void dbStateShowAll(unsigned int level); + +#endif // INCdbStateH diff --git a/src/ioc/db/dbSubscriptionIO.cpp b/src/ioc/db/dbSubscriptionIO.cpp index 3696fa5a5..cfb4251bd 100644 --- a/src/ioc/db/dbSubscriptionIO.cpp +++ b/src/ioc/db/dbSubscriptionIO.cpp @@ -4,20 +4,20 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos 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. \*************************************************************************/ -/* +/* * $Revision-Id$ * - * + * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 - * + * * Copyright, 1986, The Regents of the University of California. - * - * + * + * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 @@ -41,18 +41,18 @@ #include "dbChannelIO.h" #include "db_access_routines.h" -dbSubscriptionIO::dbSubscriptionIO ( +dbSubscriptionIO::dbSubscriptionIO ( epicsGuard < epicsMutex > & guard, epicsMutex & mutexIn, - dbContext &, dbChannelIO & chanIO, - dbAddr & addr, cacStateNotify & notifyIn, unsigned typeIn, + dbContext &, dbChannelIO & chanIO, + dbChannel * dbch, cacStateNotify & notifyIn, unsigned typeIn, unsigned long countIn, unsigned maskIn, dbEventCtx ctx ) : - mutex ( mutexIn ), count ( countIn ), notify ( notifyIn ), + mutex ( mutexIn ), count ( countIn ), notify ( notifyIn ), chan ( chanIO ), es ( 0 ), type ( typeIn ), id ( 0u ) { guard.assertIdenticalMutex ( this->mutex ); { epicsGuardRelease < epicsMutex > unguard ( guard ); - this->es = db_add_event ( ctx, & addr, + this->es = db_add_event ( ctx, dbch, dbSubscriptionEventCallback, (void *) this, maskIn ); if ( this->es == 0 ) { throw std::bad_alloc(); @@ -62,7 +62,7 @@ dbSubscriptionIO::dbSubscriptionIO ( } } -dbSubscriptionIO::~dbSubscriptionIO () +dbSubscriptionIO::~dbSubscriptionIO () { } @@ -72,7 +72,7 @@ void dbSubscriptionIO::destructor ( epicsGuard < epicsMutex > & guard ) this->~dbSubscriptionIO (); } -void dbSubscriptionIO::unsubscribe ( +void dbSubscriptionIO::unsubscribe ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); @@ -86,11 +86,11 @@ void dbSubscriptionIO::unsubscribe ( } } -void dbSubscriptionIO::channelDeleteException ( +void dbSubscriptionIO::channelDeleteException ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); - this->notify.exception ( guard, ECA_CHANDESTROY, + this->notify.exception ( guard, ECA_CHANDESTROY, this->chan.pName(guard), this->type, this->count ); } @@ -105,21 +105,21 @@ void dbSubscriptionIO::operator delete ( void * ) __FILE__, __LINE__ ); } -void * dbSubscriptionIO::operator new ( size_t size, +void * dbSubscriptionIO::operator new ( size_t size, tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE -void dbSubscriptionIO::operator delete ( void * pCadaver, +void dbSubscriptionIO::operator delete ( void * pCadaver, tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif -extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbAddr * /* paddr */, +extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbChannel * /* dbch */, int /* eventsRemaining */, struct db_field_log *pfl ) { dbSubscriptionIO * pIO = static_cast < dbSubscriptionIO * > ( pPrivate ); @@ -132,19 +132,19 @@ void dbSubscriptionIO::show ( unsigned level ) const this->show ( guard, level ); } -void dbSubscriptionIO::show ( +void dbSubscriptionIO::show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { guard.assertIdenticalMutex ( this->mutex ); - printf ( "Data base subscription IO at %p\n", + printf ( "Data base subscription IO at %p\n", static_cast ( this ) ); if ( level > 0u ) { short tmpType; if ( this->type < SHRT_MAX ) { tmpType = static_cast < short > ( this->type ); printf ( "\ttype %s, count %lu, channel at %p\n", - dbf_type_to_text ( tmpType ), this->count, + dbf_type_to_text ( tmpType ), this->count, static_cast ( &this->chan ) ); } else { @@ -154,7 +154,7 @@ void dbSubscriptionIO::show ( } } -dbSubscriptionIO * dbSubscriptionIO::isSubscription () +dbSubscriptionIO * dbSubscriptionIO::isSubscription () { return this; } diff --git a/src/ioc/db/db_access.c b/src/ioc/db/db_access.c index 5596f1a0e..99663773c 100644 --- a/src/ioc/db/db_access.c +++ b/src/ioc/db/db_access.c @@ -4,15 +4,13 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos 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. \*************************************************************************/ -/* $Revision-Id$ */ - /* Interface between old database access and new * * Author: Marty Kraimer - * Date: 6-1-90 + * Andrew Johnson */ #include @@ -24,6 +22,7 @@ #include "epicsConvert.h" #include "dbDefs.h" +#include "dbChannel.h" #include "errlog.h" #include "ellLib.h" #include "epicsTime.h" @@ -42,10 +41,6 @@ #include "dbEvent.h" #include "db_access_routines.h" -#ifndef NULL -#define NULL 0 -#endif - #define oldDBF_STRING 0 #define oldDBF_INT 1 @@ -108,50 +103,36 @@ extern unsigned short dbDBRnewToDBRold[DBR_ENUM+1]; typedef char DBSTRING[MAX_STRING_SIZE]; -#ifndef MAX_STRING_SIZE -#define MAX_STRING_SIZE 40 -#endif - -/* - * DB_PROCESS - * - * process database records - */ -void db_process(struct dbAddr *paddr) +struct dbChannel * dbChannel_create(const char *pname) { - long status = dbProcess(paddr->precord); - - if (status) errMessage(status, "db_process failed"); -} - -/* - * DB_NAME_TO_ADDR - */ -int epicsShareAPI db_name_to_addr(const char *pname, struct dbAddr *paddr) -{ - long status; + dbChannel *chan = dbChannelCreate(pname); short ftype; - status = dbNameToAddr(pname, paddr); - if (!status) { - ftype = paddr->dbr_field_type; - if (INVALID_DB_REQ(ftype)) { - errlogPrintf("%s dbNameToAddr failed\n", pname); - return -2; - } - paddr->dbr_field_type = dbDBRnewToDBRold[ftype]; - return 0; + if (!chan) + return NULL; + + ftype = chan->addr.dbr_field_type; + if (INVALID_DB_REQ(ftype)) { + dbChannelDelete(chan); + return NULL; } - else - return -1; + + chan->addr.dbr_field_type = dbDBRnewToDBRold[ftype]; + + if (dbChannelOpen(chan)) { + dbChannelDelete(chan); + return NULL; + } + + return chan; } - -int epicsShareAPI db_get_field(struct dbAddr *paddr, - int buffer_type, void *pbuffer, int no_elements, void *pfl) + +int dbChannel_get(struct dbChannel *chan, + int buffer_type, void *pbuffer, long no_elements, void *pfl) { long nRequest = no_elements; - int result = db_get_field_and_count( - paddr, buffer_type, pbuffer, &nRequest, pfl); + int result = dbChannel_get_count( + chan, buffer_type, pbuffer, &nRequest, pfl); if (nRequest < no_elements) { /* The database request returned fewer elements than requested, so * fill the remainder of the buffer with zeros. @@ -168,8 +149,8 @@ int epicsShareAPI db_get_field(struct dbAddr *paddr, /* Performs the work of the public db_get_field API, but also returns the number * of elements actually copied to the buffer. The caller is responsible for * zeroing the remaining part of the buffer. */ -int epicsShareAPI db_get_field_and_count( - struct dbAddr *paddr, int buffer_type, +int epicsShareAPI dbChannel_get_count( + struct dbChannel *chan, int buffer_type, void *pbuffer, long *nRequest, void *pfl) { long status; @@ -184,26 +165,26 @@ int epicsShareAPI db_get_field_and_count( switch(buffer_type) { case(oldDBR_STRING): - status = dbGetField(paddr, DBR_STRING, pbuffer, &zero, nRequest, pfl); + status = dbChannelGetField(chan, DBR_STRING, pbuffer, &zero, nRequest, pfl); break; /* case(oldDBR_INT): */ case(oldDBR_SHORT): - status = dbGetField(paddr, DBR_SHORT, pbuffer, &zero, nRequest, pfl); + status = dbChannelGetField(chan, DBR_SHORT, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_FLOAT): - status = dbGetField(paddr, DBR_FLOAT, pbuffer, &zero, nRequest, pfl); + status = dbChannelGetField(chan, DBR_FLOAT, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_ENUM): - status = dbGetField(paddr, DBR_ENUM, pbuffer, &zero, nRequest, pfl); + status = dbChannelGetField(chan, DBR_ENUM, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_CHAR): - status = dbGetField(paddr, DBR_CHAR, pbuffer, &zero, nRequest, pfl); + status = dbChannelGetField(chan, DBR_CHAR, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_LONG): - status = dbGetField(paddr, DBR_LONG, pbuffer, &zero, nRequest, pfl); + status = dbChannelGetField(chan, DBR_LONG, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_DOUBLE): - status = dbGetField(paddr, DBR_DOUBLE, pbuffer, &zero, nRequest, pfl); + status = dbChannelGetField(chan, DBR_DOUBLE, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_STS_STRING): @@ -216,10 +197,10 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS; - status = dbGetField(paddr, DBR_STRING, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; - status = dbGetField(paddr, DBR_STRING, pold->value, &zero, + status = dbChannelGetField(chan, DBR_STRING, pold->value, &zero, nRequest, pfl); } break; @@ -232,10 +213,10 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS; - status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; - status = dbGetField(paddr, DBR_SHORT, &pold->value, &zero, + status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &zero, nRequest, pfl); } break; @@ -247,10 +228,10 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS; - status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; - status = dbGetField(paddr, DBR_FLOAT, &pold->value, &zero, + status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &zero, nRequest, pfl); } break; @@ -262,10 +243,10 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS; - status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; - status = dbGetField(paddr, DBR_ENUM, &pold->value, &zero, + status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &zero, nRequest, pfl); } break; @@ -277,10 +258,10 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS; - status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; - status = dbGetField(paddr, DBR_UCHAR, &pold->value, &zero, + status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &zero, nRequest, pfl); } break; @@ -292,10 +273,10 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS; - status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; - status = dbGetField(paddr, DBR_LONG, &pold->value, &zero, + status = dbChannelGetField(chan, DBR_LONG, &pold->value, &zero, nRequest, pfl); } break; @@ -307,11 +288,11 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS; - status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; options = 0; - status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options, + status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options, nRequest, pfl); } break; @@ -325,12 +306,12 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS | DBR_TIME; - status = dbGetField(paddr, DBR_STRING, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; - status = dbGetField(paddr, DBR_STRING, pold->value, &options, + status = dbChannelGetField(chan, DBR_STRING, pold->value, &options, nRequest, pfl); } break; @@ -344,12 +325,12 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS | DBR_TIME; - status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; - status = dbGetField(paddr, DBR_SHORT, &pold->value, &options, + status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options, nRequest, pfl); } break; @@ -362,12 +343,12 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS | DBR_TIME; - status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; - status = dbGetField(paddr, DBR_FLOAT, &pold->value, &options, + status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options, nRequest, pfl); } break; @@ -380,12 +361,12 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS | DBR_TIME; - status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; - status = dbGetField(paddr, DBR_ENUM, &pold->value, &options, + status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &options, nRequest, pfl); } break; @@ -398,12 +379,12 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS | DBR_TIME; - status = dbGetField(paddr, DBR_CHAR, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_CHAR, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; - status = dbGetField(paddr, DBR_CHAR, &pold->value, &options, + status = dbChannelGetField(chan, DBR_CHAR, &pold->value, &options, nRequest, pfl); } break; @@ -416,12 +397,12 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS | DBR_TIME; - status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; - status = dbGetField(paddr, DBR_LONG, &pold->value, &options, + status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options, nRequest, pfl); } break; @@ -434,12 +415,12 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS | DBR_TIME; - status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; - status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options, + status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options, nRequest, pfl); } break; @@ -457,7 +438,7 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG; - status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); @@ -469,7 +450,7 @@ int epicsShareAPI db_get_field_and_count( pold->lower_warning_limit = newSt.lower_warning_limit; pold->lower_alarm_limit = newSt.lower_alarm_limit; options = 0; - status = dbGetField(paddr, DBR_SHORT, &pold->value, &options, + status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options, nRequest, pfl); } break; @@ -486,7 +467,7 @@ int epicsShareAPI db_get_field_and_count( options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE | DBR_AL_DOUBLE; - status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->precision = newSt.precision.dp; @@ -499,7 +480,7 @@ int epicsShareAPI db_get_field_and_count( pold->upper_warning_limit = epicsConvertDoubleToFloat(newSt.upper_warning_limit); pold->lower_warning_limit = epicsConvertDoubleToFloat(newSt.lower_warning_limit); options = 0; - status = dbGetField(paddr, DBR_FLOAT, &pold->value, &options, + status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options, nRequest, pfl); } break; @@ -515,7 +496,7 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG; - status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); @@ -527,7 +508,7 @@ int epicsShareAPI db_get_field_and_count( pold->lower_warning_limit = newSt.lower_warning_limit; pold->lower_alarm_limit = newSt.lower_alarm_limit; options = 0; - status = dbGetField(paddr, DBR_UCHAR, &pold->value, &options, + status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &options, nRequest, pfl); } break; @@ -542,7 +523,7 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG; - status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); @@ -554,7 +535,7 @@ int epicsShareAPI db_get_field_and_count( pold->lower_warning_limit = newSt.lower_warning_limit; pold->lower_alarm_limit = newSt.lower_alarm_limit; options = 0; - status = dbGetField(paddr, DBR_LONG, &pold->value, &options, + status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options, nRequest, pfl); } break; @@ -571,7 +552,7 @@ int epicsShareAPI db_get_field_and_count( options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE | DBR_AL_DOUBLE; - status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->precision = newSt.precision.dp; @@ -584,7 +565,7 @@ int epicsShareAPI db_get_field_and_count( pold->lower_warning_limit = newSt.lower_warning_limit; pold->lower_alarm_limit = newSt.lower_alarm_limit; options = 0; - status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options, + status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options, nRequest, pfl); } break; @@ -603,7 +584,7 @@ int epicsShareAPI db_get_field_and_count( options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG | DBR_AL_LONG; - status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); @@ -617,7 +598,7 @@ int epicsShareAPI db_get_field_and_count( pold->upper_ctrl_limit = newSt.upper_ctrl_limit; pold->lower_ctrl_limit = newSt.lower_ctrl_limit; options = 0; - status = dbGetField(paddr, DBR_SHORT, &pold->value, &options, + status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options, nRequest, pfl); } break; @@ -635,7 +616,7 @@ int epicsShareAPI db_get_field_and_count( options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE | DBR_CTRL_DOUBLE | DBR_AL_DOUBLE; - status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->precision = newSt.precision.dp; @@ -650,7 +631,7 @@ int epicsShareAPI db_get_field_and_count( pold->upper_ctrl_limit = epicsConvertDoubleToFloat(newSt.upper_ctrl_limit); pold->lower_ctrl_limit = epicsConvertDoubleToFloat(newSt.lower_ctrl_limit); options = 0; - status = dbGetField(paddr, DBR_FLOAT, &pold->value, &options, + status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options, nRequest, pfl); } break; @@ -667,7 +648,7 @@ int epicsShareAPI db_get_field_and_count( memset(pold, '\0', sizeof(struct dbr_ctrl_enum)); /* first get status and severity */ options = DBR_STATUS | DBR_ENUM_STRS; - status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; no_str = newSt.no_str; @@ -677,7 +658,7 @@ int epicsShareAPI db_get_field_and_count( strncpy(pold->strs[i], newSt.strs[i], sizeof(pold->strs[i])); /*now get values*/ options = 0; - status = dbGetField(paddr, DBR_ENUM, &pold->value, &options, + status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &options, nRequest, pfl); } break; @@ -694,7 +675,7 @@ int epicsShareAPI db_get_field_and_count( options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG | DBR_AL_LONG; - status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); @@ -708,7 +689,7 @@ int epicsShareAPI db_get_field_and_count( pold->upper_ctrl_limit = newSt.upper_ctrl_limit; pold->lower_ctrl_limit = newSt.lower_ctrl_limit; options = 0; - status = dbGetField(paddr, DBR_UCHAR, &pold->value, &options, + status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &options, nRequest, pfl); } break; @@ -725,7 +706,7 @@ int epicsShareAPI db_get_field_and_count( options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG | DBR_AL_LONG; - status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); @@ -739,7 +720,7 @@ int epicsShareAPI db_get_field_and_count( pold->upper_ctrl_limit = newSt.upper_ctrl_limit; pold->lower_ctrl_limit = newSt.lower_ctrl_limit; options = 0; - status = dbGetField(paddr, DBR_LONG, &pold->value, &options, + status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options, nRequest, pfl); } break; @@ -757,7 +738,7 @@ int epicsShareAPI db_get_field_and_count( options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE | DBR_CTRL_DOUBLE | DBR_AL_DOUBLE; - status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->precision = newSt.precision.dp; @@ -772,7 +753,7 @@ int epicsShareAPI db_get_field_and_count( pold->upper_ctrl_limit = newSt.upper_ctrl_limit; pold->lower_ctrl_limit = newSt.lower_ctrl_limit; options = 0; - status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options, + status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options, nRequest, pfl); } break; @@ -785,13 +766,13 @@ int epicsShareAPI db_get_field_and_count( } newSt; options = DBR_STATUS; - status = dbGetField(paddr, DBR_STRING, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->ackt = newSt.ackt; pold->acks = newSt.acks; options = 0; - status = dbGetField(paddr, DBR_STRING, pold->value, + status = dbChannelGetField(chan, DBR_STRING, pold->value, &options, nRequest, pfl); } break; @@ -807,7 +788,7 @@ int epicsShareAPI db_get_field_and_count( break; } dbInitEntry(pdbbase, &dbEntry); - status = dbFindRecord(&dbEntry, paddr->precord->name); + status = dbFindRecord(&dbEntry, dbChannelRecord(chan)->name); if (!status) name = dbGetRecordTypeName(&dbEntry); dbFinishEntry(&dbEntry); if (status) break; @@ -825,165 +806,163 @@ int epicsShareAPI db_get_field_and_count( if (status) return -1; return 0; } - -/* DB_PUT_FIELD put a field and convert it to the desired type */ -int epicsShareAPI db_put_field(struct dbAddr *paddr, int src_type, - const void *psrc, int no_elements) +int dbChannel_put(struct dbChannel *chan, int src_type, + const void *psrc, long no_elements) { long status; - switch(src_type) { + switch (src_type) { case(oldDBR_STRING): - status = dbPutField(paddr, DBR_STRING, psrc, no_elements); + status = dbChannelPutField(chan, DBR_STRING, psrc, no_elements); break; /* case(oldDBR_INT): */ case(oldDBR_SHORT): - status = dbPutField(paddr, DBR_SHORT, psrc, no_elements); + status = dbChannelPutField(chan, DBR_SHORT, psrc, no_elements); break; case(oldDBR_FLOAT): - status = dbPutField(paddr, DBR_FLOAT, psrc, no_elements); + status = dbChannelPutField(chan, DBR_FLOAT, psrc, no_elements); break; case(oldDBR_ENUM): - status = dbPutField(paddr, DBR_ENUM, psrc, no_elements); + status = dbChannelPutField(chan, DBR_ENUM, psrc, no_elements); break; case(oldDBR_CHAR): - status = dbPutField(paddr, DBR_UCHAR, psrc, no_elements); + status = dbChannelPutField(chan, DBR_UCHAR, psrc, no_elements); break; case(oldDBR_LONG): - status = dbPutField(paddr, DBR_LONG, psrc, no_elements); + status = dbChannelPutField(chan, DBR_LONG, psrc, no_elements); break; case(oldDBR_DOUBLE): - status = dbPutField(paddr, DBR_DOUBLE, psrc, no_elements); + status = dbChannelPutField(chan, DBR_DOUBLE, psrc, no_elements); break; case(oldDBR_STS_STRING): - status = dbPutField(paddr, DBR_STRING, + status = dbChannelPutField(chan, DBR_STRING, ((const struct dbr_sts_string *)psrc)->value, no_elements); break; /* case(oldDBR_STS_INT): */ case(oldDBR_STS_SHORT): - status = dbPutField(paddr, DBR_SHORT, + status = dbChannelPutField(chan, DBR_SHORT, &((const struct dbr_sts_short *)psrc)->value, no_elements); break; case(oldDBR_STS_FLOAT): - status = dbPutField(paddr, DBR_FLOAT, + status = dbChannelPutField(chan, DBR_FLOAT, &((const struct dbr_sts_float *)psrc)->value, no_elements); break; case(oldDBR_STS_ENUM): - status = dbPutField(paddr, DBR_ENUM, + status = dbChannelPutField(chan, DBR_ENUM, &((const struct dbr_sts_enum *)psrc)->value, no_elements); break; case(oldDBR_STS_CHAR): - status = dbPutField(paddr, DBR_UCHAR, + status = dbChannelPutField(chan, DBR_UCHAR, &((const struct dbr_sts_char *)psrc)->value, no_elements); break; case(oldDBR_STS_LONG): - status = dbPutField(paddr, DBR_LONG, + status = dbChannelPutField(chan, DBR_LONG, &((const struct dbr_sts_long *)psrc)->value, no_elements); break; case(oldDBR_STS_DOUBLE): - status = dbPutField(paddr, DBR_DOUBLE, + status = dbChannelPutField(chan, DBR_DOUBLE, &((const struct dbr_sts_double *)psrc)->value, no_elements); break; case(oldDBR_TIME_STRING): - status = dbPutField(paddr, DBR_TIME, + status = dbChannelPutField(chan, DBR_TIME, ((const struct dbr_time_string *)psrc)->value, no_elements); break; /* case(oldDBR_TIME_INT): */ case(oldDBR_TIME_SHORT): - status = dbPutField(paddr, DBR_SHORT, + status = dbChannelPutField(chan, DBR_SHORT, &((const struct dbr_time_short *)psrc)->value, no_elements); break; case(oldDBR_TIME_FLOAT): - status = dbPutField(paddr, DBR_FLOAT, + status = dbChannelPutField(chan, DBR_FLOAT, &((const struct dbr_time_float *)psrc)->value, no_elements); break; case(oldDBR_TIME_ENUM): - status = dbPutField(paddr, DBR_ENUM, + status = dbChannelPutField(chan, DBR_ENUM, &((const struct dbr_time_enum *)psrc)->value, no_elements); break; case(oldDBR_TIME_CHAR): - status = dbPutField(paddr, DBR_UCHAR, + status = dbChannelPutField(chan, DBR_UCHAR, &((const struct dbr_time_char *)psrc)->value, no_elements); break; case(oldDBR_TIME_LONG): - status = dbPutField(paddr, DBR_LONG, + status = dbChannelPutField(chan, DBR_LONG, &((const struct dbr_time_long *)psrc)->value, no_elements); break; case(oldDBR_TIME_DOUBLE): - status = dbPutField(paddr, DBR_DOUBLE, + status = dbChannelPutField(chan, DBR_DOUBLE, &((const struct dbr_time_double *)psrc)->value, no_elements); break; case(oldDBR_GR_STRING): /* no struct dbr_gr_string - use dbr_sts_string instead */ - status = dbPutField(paddr, DBR_STRING, + status = dbChannelPutField(chan, DBR_STRING, ((const struct dbr_sts_string *)psrc)->value, no_elements); break; /* case(oldDBR_GR_INT): */ case(oldDBR_GR_SHORT): - status = dbPutField(paddr, DBR_SHORT, + status = dbChannelPutField(chan, DBR_SHORT, &((const struct dbr_gr_short *)psrc)->value, no_elements); break; case(oldDBR_GR_FLOAT): - status = dbPutField(paddr, DBR_FLOAT, + status = dbChannelPutField(chan, DBR_FLOAT, &((const struct dbr_gr_float *)psrc)->value, no_elements); break; case(oldDBR_GR_ENUM): - status = dbPutField(paddr, DBR_ENUM, + status = dbChannelPutField(chan, DBR_ENUM, &((const struct dbr_gr_enum *)psrc)->value, no_elements); break; case(oldDBR_GR_CHAR): - status = dbPutField(paddr, DBR_UCHAR, + status = dbChannelPutField(chan, DBR_UCHAR, &((const struct dbr_gr_char *)psrc)->value, no_elements); break; case(oldDBR_GR_LONG): - status = dbPutField(paddr, DBR_LONG, + status = dbChannelPutField(chan, DBR_LONG, &((const struct dbr_gr_long *)psrc)->value, no_elements); break; case(oldDBR_GR_DOUBLE): - status = dbPutField(paddr, DBR_DOUBLE, + status = dbChannelPutField(chan, DBR_DOUBLE, &((const struct dbr_gr_double *)psrc)->value, no_elements); break; case(oldDBR_CTRL_STRING): /* no struct dbr_ctrl_string - use dbr_sts_string instead */ - status = dbPutField(paddr, DBR_STRING, + status = dbChannelPutField(chan, DBR_STRING, ((const struct dbr_sts_string *)psrc)->value, no_elements); break; /* case(oldDBR_CTRL_INT): */ case(oldDBR_CTRL_SHORT): - status = dbPutField(paddr, DBR_SHORT, + status = dbChannelPutField(chan, DBR_SHORT, &((const struct dbr_ctrl_short *)psrc)->value, no_elements); break; case(oldDBR_CTRL_FLOAT): - status = dbPutField(paddr, DBR_FLOAT, + status = dbChannelPutField(chan, DBR_FLOAT, &((const struct dbr_ctrl_float *)psrc)->value, no_elements); break; case(oldDBR_CTRL_ENUM): - status = dbPutField(paddr, DBR_ENUM, + status = dbChannelPutField(chan, DBR_ENUM, &((const struct dbr_ctrl_enum *)psrc)->value, no_elements); break; case(oldDBR_CTRL_CHAR): - status = dbPutField(paddr, DBR_UCHAR, + status = dbChannelPutField(chan, DBR_UCHAR, &((const struct dbr_ctrl_char *)psrc)->value, no_elements); break; case(oldDBR_CTRL_LONG): - status = dbPutField(paddr, DBR_LONG, + status = dbChannelPutField(chan, DBR_LONG, &((const struct dbr_ctrl_long *)psrc)->value, no_elements); break; case(oldDBR_CTRL_DOUBLE): - status = dbPutField(paddr, DBR_DOUBLE, + status = dbChannelPutField(chan, DBR_DOUBLE, &((const struct dbr_ctrl_double *)psrc)->value, no_elements); break; case(oldDBR_PUT_ACKT): - status = dbPutField(paddr, DBR_PUT_ACKT, psrc, no_elements); + status = dbChannelPutField(chan, DBR_PUT_ACKT, psrc, no_elements); break; case(oldDBR_PUT_ACKS): - status = dbPutField(paddr, DBR_PUT_ACKS, psrc, no_elements); + status = dbChannelPutField(chan, DBR_PUT_ACKS, psrc, no_elements); break; default: @@ -993,7 +972,7 @@ int epicsShareAPI db_put_field(struct dbAddr *paddr, int src_type, return 0; } - + epicsShareFunc int epicsShareAPI dbPutNotifyMapType (putNotify *ppn, short oldtype) { switch(oldtype) { diff --git a/src/ioc/db/db_access_routines.h b/src/ioc/db/db_access_routines.h index a1f445c6f..d890f1d1c 100644 --- a/src/ioc/db/db_access_routines.h +++ b/src/ioc/db/db_access_routines.h @@ -1,11 +1,10 @@ /*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* base/include/db_access_routines.h */ @@ -22,26 +21,21 @@ extern "C" { #endif #include "shareLib.h" -#include "dbAddr.h" epicsShareExtern struct dbBase *pdbbase; epicsShareExtern volatile int interruptAccept; /* - * old db access API - * (included here because these routines use dbAccess.h and their - * prototypes must also be included in db_access.h) + * Adaptors for db_access users */ -epicsShareFunc int epicsShareAPI db_name_to_addr( - const char *pname, DBADDR *paddr); -epicsShareFunc int epicsShareAPI db_put_field( - DBADDR *paddr, int src_type,const void *psrc, int no_elements); -epicsShareFunc int epicsShareAPI db_get_field( - DBADDR *paddr, int dest_type,void *pdest, int no_elements, void *pfl); -epicsShareFunc int epicsShareAPI db_get_field_and_count( - struct dbAddr *paddr, int buffer_type, - void *pbuffer, long *nRequest, void *pfl); +epicsShareFunc struct dbChannel * dbChannel_create(const char *pname); +epicsShareFunc int dbChannel_get(struct dbChannel *chan, + int buffer_type, void *pbuffer, long no_elements, void *pfl); +epicsShareFunc int dbChannel_put(struct dbChannel *chan, int src_type, + const void *psrc, long no_elements); +epicsShareFunc int epicsShareAPI dbChannel_get_count(struct dbChannel *chan, + int buffer_type, void *pbuffer, long *nRequest, void *pfl); #ifdef __cplusplus diff --git a/src/ioc/db/db_field_log.h b/src/ioc/db/db_field_log.h index 2012f49f6..1f1d212d0 100644 --- a/src/ioc/db/db_field_log.h +++ b/src/ioc/db/db_field_log.h @@ -1,19 +1,26 @@ /*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. * Copyright (c) 2002 The University of Chicago, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ -/* $Revision-Id$ - * Author: Jeffrey O. Hill - * Date: 4-1-89 + +/* + * Author: Jeffrey O. Hill + * + * Ralph Lange */ + #ifndef INCLdb_field_logh #define INCLdb_field_logh +#include "epicsTime.h" + #ifdef __cplusplus extern "C" { #endif @@ -27,7 +34,7 @@ extern "C" { * will most likely change infrequently. * */ -union native_value{ +union native_value { short dbf_int; short dbf_short; float dbf_float; @@ -36,7 +43,7 @@ union native_value{ long dbf_long; double dbf_double; #ifdef DB_EVENT_LOG_STRINGS - char dbf_string[MAXSTRINGSIZE]; + char dbf_string[MAX_STRING_SIZE]; #endif }; @@ -44,12 +51,48 @@ union native_value{ * structure to log the state of a data base field at the time * an event is triggered. */ +struct db_field_log; +typedef void (dbfl_freeFunc)(struct db_field_log *pfl); + +/* Types of db_field_log: rec = use record, val = val inside, ref = reference inside */ +typedef enum dbfl_type { + dbfl_type_rec = 0, + dbfl_type_val, + dbfl_type_ref +} dbfl_type; + +/* Context of db_field_log: event = subscription update, read = read reply */ +typedef enum dbfl_context { + dbfl_context_read = 0, + dbfl_context_event +} dbfl_context; + +#define dbflTypeStr(t) (t==dbfl_type_val?"val":t==dbfl_type_rec?"rec":"ref") + +struct dbfl_val { + union native_value field; /* Field value */ +}; + +struct dbfl_ref { + dbfl_freeFunc *dtor; /* Callback to free filter-allocated resources */ + void *pvt; /* Private pointer */ + void *field; /* Field value */ +}; + typedef struct db_field_log { - unsigned short stat; /* Alarm Status */ - unsigned short sevr; /* Alarm Severity */ - epicsTimeStamp time; /* time stamp */ - union native_value field; /* field value */ -}db_field_log; + enum dbfl_type type:2; /* type (union) selector */ + enum dbfl_context ctx:1; /* context (operation type) */ + epicsTimeStamp time; /* Time stamp */ + unsigned short stat; /* Alarm Status */ + unsigned short sevr; /* Alarm Severity */ + short field_type; /* DBF type of data */ + short field_size; /* Data size */ + long no_elements; /* No of array elements */ + union { + struct dbfl_val v; + struct dbfl_ref r; + } u; +} db_field_log; #ifdef __cplusplus } diff --git a/src/ioc/db/db_test.c b/src/ioc/db/db_test.c index 5f44e28ca..2db7b57d7 100644 --- a/src/ioc/db/db_test.c +++ b/src/ioc/db/db_test.c @@ -5,16 +5,15 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are 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. \*************************************************************************/ -/* share/src/db @(#)db_test.c 1.10 2/3/94 */ + /* database access subroutines */ /* * Author: Bob Dalesio - * Date: 4/15/88 + * Andrew Johnson */ #include -#include #include #include @@ -22,707 +21,234 @@ #include "errlog.h" #include "cadef.h" #include "epicsStdio.h" +#include "epicsStdlib.h" +#include "epicsString.h" + #define epicsExportSharedSymbols +#include "dbChannel.h" #include "db_access_routines.h" #include "dbNotify.h" #include "db_test.h" #include "dbCommon.h" - #define MAX_ELEMS 10 -int epicsShareAPI gft(char *pname) + +int gft(char *pname) { - char tgf_buffer[MAX_ELEMS*MAX_STRING_SIZE+sizeof(struct dbr_ctrl_double)]; - struct dbAddr addr; - struct dbAddr *paddr = &addr; - short number_elements; - int i; - int status; + char tgf_buffer[MAX_ELEMS*MAX_STRING_SIZE + sizeof(struct dbr_ctrl_double)]; + struct dbChannel *chan; + struct dbCommon *precord; + long elements; + short type; + int i; - if (pname==0 || ((*pname < ' ') || (*pname > 'z'))) { - printf("\nusage \"pv name\"\n"); - return(1); - } - /* convert name to database address */ - status=db_name_to_addr(pname,&addr); - if(status) { - printf("db_name_to_addr failed\n"); - return(1); - } - printf(" Record Name: %s\n",pname); - printf("Record Address: 0x%p\n",(void *)addr.precord); - printf(" Field Type: %d\n",addr.dbr_field_type); - printf(" Field Address: 0x%p\n",addr.pfield); - printf(" Field Size: %d\n",addr.field_size); - printf(" No Elements: %ld\n",addr.no_elements); - number_elements = - ((addr.no_elements > MAX_ELEMS)?MAX_ELEMS:addr.no_elements); + chan = dbChannel_create(pname); + if (!chan) { + printf("Channel couldn't be created\n"); + return 1; + } - for(i=0; i<=LAST_BUFFER_TYPE; i++) { - if(addr.dbr_field_type==0) { - if( (i!=DBR_STRING) - && (i!=DBR_STS_STRING) - && (i!=DBR_TIME_STRING) - && (i!=DBR_GR_STRING) - && (i!=DBR_CTRL_STRING)) continue; - } - if(db_get_field(paddr,i,tgf_buffer,number_elements,NULL)<0) - printf("\t%s Failed\n",dbr_text[i]); - else - ca_dump_dbr (i,number_elements,tgf_buffer); - } - return(0); + precord = dbChannelRecord(chan); + elements = dbChannelElements(chan); + type = dbChannelExportType(chan); + + printf(" Record Name: %s\n", precord->name); + printf("Record Address: 0x%p\n", precord); + printf(" Export Type: %d\n", type); + printf(" Field Address: 0x%p\n", dbChannelField(chan)); + printf(" Field Size: %d\n", dbChannelFieldSize(chan)); + printf(" No Elements: %ld\n", elements); + + if (elements > MAX_ELEMS) + elements = MAX_ELEMS; + + for (i = 0; i <= LAST_BUFFER_TYPE; i++) { + if (type == 0) { + if ((i != DBR_STRING) && (i != DBR_STS_STRING) && + (i != DBR_TIME_STRING) && (i != DBR_GR_STRING) && + (i != DBR_CTRL_STRING)) + continue; + } + if (dbChannel_get(chan, i, tgf_buffer, elements, NULL) < 0) + printf("\t%s Failed\n", dbr_text[i]); + else + ca_dump_dbr(i, elements, tgf_buffer); + } + + dbChannelDelete(chan); + return 0; } - + /* * TPF * Test put field */ -int epicsShareAPI pft(char *pname,char *pvalue) +int epicsShareAPI pft(char *pname, char *pvalue) { - struct dbAddr addr; - struct dbAddr *paddr = &addr; - char buffer[500]; - short shortvalue; - long longvalue; - float floatvalue; - unsigned char charvalue; - double doublevalue; - int status; + struct dbChannel *chan; + struct dbCommon *precord; + long elements; + short type; + char buffer[500]; + short shortvalue; + long longvalue; + float floatvalue; + unsigned char charvalue; + double doublevalue; - if (pname==0 || pvalue==0 - || ((*pname < ' ') || (*pname > 'z')) - || ((*pvalue < ' ') || (*pvalue > 'z'))){ - printf("\nusage \"pv name\",\"value\"\n"); - return(1); - } + chan = dbChannel_create(pname); + if (!chan) { + printf("Channel couldn't be created\n"); + return 1; + } - /* convert name to database address */ + precord = dbChannelRecord(chan); + elements = dbChannelElements(chan); + type = dbChannelExportType(chan); - /* convert name to database address */ - status=db_name_to_addr(pname,&addr); - if(status) { - printf("db_name_to_addr failed\n"); - return(1); - } - printf(" Record Name: %s\n",pname); - printf("Record Address: 0x%p\n",(void *)addr.precord); - printf(" Field Type: %d\n",addr.dbr_field_type); - printf(" Field Address: 0x%p\n",addr.pfield); - printf(" Field Size: %d\n",addr.field_size); - printf(" No Elements: %ld\n",addr.no_elements); - if (db_put_field(paddr,DBR_STRING,pvalue,1) < 0) printf("\n\t failed "); - if (db_get_field(paddr,DBR_STRING,buffer,1,NULL) < 0) printf("\n\tfailed"); - else ca_dump_dbr(DBR_STRING,1,buffer); - if(addr.dbr_field_type<=DBF_STRING || addr.dbr_field_type==DBF_ENUM) - return(0); - if(sscanf(pvalue,"%hd",&shortvalue)==1) { - if (db_put_field(paddr,DBR_SHORT,&shortvalue,1) < 0) - printf("\n\t SHORT failed "); - if (db_get_field(paddr,DBR_SHORT,buffer,1,NULL) < 0) - printf("\n\t SHORT GET failed"); - else ca_dump_dbr(DBR_SHORT,1,buffer); - } - if(sscanf(pvalue,"%ld",&longvalue)==1) { - if (db_put_field(paddr,DBR_LONG,&longvalue,1) < 0) - printf("\n\t LONG failed "); - if (db_get_field(paddr,DBR_LONG,buffer,1,NULL) < 0) - printf("\n\t LONG GET failed"); - else ca_dump_dbr(DBR_LONG,1,buffer); - } - if(epicsScanFloat(pvalue, &floatvalue)==1) { - if (db_put_field(paddr,DBR_FLOAT,&floatvalue,1) < 0) - printf("\n\t FLOAT failed "); - if (db_get_field(paddr,DBR_FLOAT,buffer,1,NULL) < 0) - printf("\n\t FLOAT GET failed"); - else ca_dump_dbr(DBR_FLOAT,1,buffer); - } - if(epicsScanFloat(pvalue, &floatvalue)==1) { - doublevalue=floatvalue; - if (db_put_field(paddr,DBR_DOUBLE,&doublevalue,1) < 0) - printf("\n\t DOUBLE failed "); - if (db_get_field(paddr,DBR_DOUBLE,buffer,1,NULL) < 0) - printf("\n\t DOUBLE GET failed"); - else ca_dump_dbr(DBR_DOUBLE,1,buffer); - } - if(sscanf(pvalue,"%hd",&shortvalue)==1) { - charvalue=(unsigned char)shortvalue; - if (db_put_field(paddr,DBR_CHAR,&charvalue,1) < 0) - printf("\n\t CHAR failed "); - if (db_get_field(paddr,DBR_CHAR,buffer,1,NULL) < 0) - printf("\n\t CHAR GET failed"); - else ca_dump_dbr(DBR_CHAR,1,buffer); - } - if(sscanf(pvalue,"%hd",&shortvalue)==1) { - if (db_put_field(paddr,DBR_ENUM,&shortvalue,1) < 0) - printf("\n\t ENUM failed "); - if (db_get_field(paddr,DBR_ENUM,buffer,1,NULL) < 0) - printf("\n\t ENUM GET failed"); - else ca_dump_dbr(DBR_ENUM,1,buffer); - } - printf("\n"); - return(0); -} + printf(" Record Name: %s\n", precord->name); + printf("Record Address: 0x%p\n", precord); + printf(" Export Type: %d\n", type); + printf(" Field Address: 0x%p\n", dbChannelField(chan)); + printf(" Field Size: %d\n", dbChannelFieldSize(chan)); + printf(" No Elements: %ld\n", elements); -#if 0 -/* - * PRINT_RETURNED - * - * print out the values in a database access interface structure - */ -static void print_returned(type,count,pbuffer) - short type; - char *pbuffer; - short count; -{ - short i; + if (dbChannel_put(chan, DBR_STRING,pvalue, 1) < 0) + printf("\n\t failed "); + if (dbChannel_get(chan, DBR_STRING,buffer, 1, NULL) < 0) + printf("\n\tfailed"); + else + ca_dump_dbr(DBR_STRING,1, buffer); - printf("%s\t",dbr_text[type]); - switch(type){ - case (DBR_STRING): - for(i=0; istatus,pvalue->severity); - printf("\tValue: %s",pvalue->value); - break; - } - case (DBR_STS_ENUM): - case (DBR_STS_SHORT): - { - struct dbr_sts_short *pvalue - = (struct dbr_sts_short *)pbuffer; - short *pshort = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pshort++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",*pshort); - } - break; - } - case (DBR_STS_FLOAT): - { - struct dbr_sts_float *pvalue - = (struct dbr_sts_float *)pbuffer; - float *pfloat = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pfloat++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%6.4f ",*pfloat); - } - break; - } - case (DBR_STS_CHAR): - { - struct dbr_sts_char *pvalue - = (struct dbr_sts_char *)pbuffer; - unsigned char *pchar = &pvalue->value; - short value; - - printf("%2d %2d",pvalue->status,pvalue->severity); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pchar++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - value=*pchar; - printf("%d ",value); - } - break; - } - case (DBR_STS_LONG): - { - struct dbr_sts_long *pvalue - = (struct dbr_sts_long *)pbuffer; - dbr_long_t *plong = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,plong++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",*plong); - } - break; - } - case (DBR_STS_DOUBLE): - { - struct dbr_sts_double *pvalue - = (struct dbr_sts_double *)pbuffer; - double *pdouble = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pdouble++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%6.4f ",(float)(*pdouble)); - } - break; - } - case (DBR_TIME_STRING): - { - struct dbr_time_string *pvalue - = (struct dbr_time_string *) pbuffer; - printf("%2d %2d",pvalue->status,pvalue->severity); - printf("\tTimeStamp: %u %u", - pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); - printf("\tValue: "); - printf("%s",pvalue->value); - break; - } - case (DBR_TIME_SHORT): - case (DBR_TIME_ENUM): - { - struct dbr_time_short *pvalue - = (struct dbr_time_short *)pbuffer; - short *pshort = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - printf("\tTimeStamp: %u %u", - pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pshort++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",*pshort); - } - break; - } - case (DBR_TIME_FLOAT): - { - struct dbr_time_float *pvalue - = (struct dbr_time_float *)pbuffer; - float *pfloat = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - printf("\tTimeStamp: %u %u", - pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pfloat++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%6.4f ",*pfloat); - } - break; - } - case (DBR_TIME_CHAR): - { - struct dbr_time_char *pvalue - = (struct dbr_time_char *)pbuffer; - unsigned char *pchar = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - printf("\tTimeStamp: %u %u", - pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pchar++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",(short)(*pchar)); - } - break; - } - case (DBR_TIME_LONG): - { - struct dbr_time_long *pvalue - = (struct dbr_time_long *)pbuffer; - dbr_long_t *plong = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - printf("\tTimeStamp: %u %u", - pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,plong++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",*plong); - } - break; - } - case (DBR_TIME_DOUBLE): - { - struct dbr_time_double *pvalue - = (struct dbr_time_double *)pbuffer; - double *pdouble = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - printf("\tTimeStamp: %u %u", - pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pdouble++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%6.4f ",(float)(*pdouble)); - } - break; - } - case (DBR_GR_SHORT): - { - struct dbr_gr_short *pvalue - = (struct dbr_gr_short *)pbuffer; - short *pshort = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf("\n\t%8d %8d %8d %8d %8d %8d", - pvalue->upper_disp_limit,pvalue->lower_disp_limit, - pvalue->upper_alarm_limit,pvalue->upper_warning_limit, - pvalue->lower_warning_limit,pvalue->lower_alarm_limit); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pshort++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",*pshort); - } - break; - } - case (DBR_GR_FLOAT): - { - struct dbr_gr_float *pvalue - = (struct dbr_gr_float *)pbuffer; - float *pfloat = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", - pvalue->precision, - pvalue->upper_disp_limit,pvalue->lower_disp_limit, - pvalue->upper_alarm_limit,pvalue->upper_warning_limit, - pvalue->lower_warning_limit,pvalue->lower_alarm_limit); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pfloat++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%6.4f ",*pfloat); - } - break; - } - case (DBR_GR_ENUM): - case (DBR_CTRL_ENUM): - { - struct dbr_gr_enum *pvalue - = (struct dbr_gr_enum *)pbuffer; - printf("%2d %2d",pvalue->status, - pvalue->severity); - printf("\tValue: %d",pvalue->value); - if(pvalue->no_str>0) { - printf("\n\t%3d",pvalue->no_str); - for (i = 0; i < pvalue->no_str; i++) - printf("\n\t%.26s",pvalue->strs[i]); - } - break; - } - case (DBR_GR_CHAR): - { - struct dbr_gr_char *pvalue - = (struct dbr_gr_char *)pbuffer; - unsigned char *pchar = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf("\n\t%8d %8d %8d %8d %8d %8d", - pvalue->upper_disp_limit,pvalue->lower_disp_limit, - pvalue->upper_alarm_limit,pvalue->upper_warning_limit, - pvalue->lower_warning_limit,pvalue->lower_alarm_limit); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pchar++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",(short)(*pchar)); - } - break; - } - case (DBR_GR_LONG): - { - struct dbr_gr_long *pvalue - = (struct dbr_gr_long *)pbuffer; - dbr_long_t *plong = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf("\n\t%8d %8d %8d %8d %8d %8d", - pvalue->upper_disp_limit,pvalue->lower_disp_limit, - pvalue->upper_alarm_limit,pvalue->upper_warning_limit, - pvalue->lower_warning_limit,pvalue->lower_alarm_limit); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,plong++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",*plong); - } - break; - } - case (DBR_GR_DOUBLE): - { - struct dbr_gr_double *pvalue - = (struct dbr_gr_double *)pbuffer; - double *pdouble = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", - pvalue->precision, - (float)(pvalue->upper_disp_limit), - (float)(pvalue->lower_disp_limit), - (float)(pvalue->upper_alarm_limit), - (float)(pvalue->upper_warning_limit), - (float)(pvalue->lower_warning_limit), - (float)(pvalue->lower_alarm_limit)); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pdouble++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%6.4f ",(float)(*pdouble)); - } - break; - } - case (DBR_CTRL_SHORT): - { - struct dbr_ctrl_short *pvalue - = (struct dbr_ctrl_short *)pbuffer; - short *pshort = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf("\n\t%8d %8d %8d %8d %8d %8d", - pvalue->upper_disp_limit,pvalue->lower_disp_limit, - pvalue->upper_alarm_limit,pvalue->upper_warning_limit, - pvalue->lower_warning_limit,pvalue->lower_alarm_limit); - printf(" %8d %8d", - pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pshort++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",*pshort); - } - break; - } - case (DBR_CTRL_FLOAT): - { - struct dbr_ctrl_float *pvalue - = (struct dbr_ctrl_float *)pbuffer; - float *pfloat = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", - pvalue->precision, - pvalue->upper_disp_limit,pvalue->lower_disp_limit, - pvalue->upper_alarm_limit,pvalue->upper_warning_limit, - pvalue->lower_warning_limit,pvalue->lower_alarm_limit); - printf(" %8.3f %8.3f", - pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pfloat++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%6.4f ",*pfloat); - } - break; - } - case (DBR_CTRL_CHAR): - { - struct dbr_ctrl_char *pvalue - = (struct dbr_ctrl_char *)pbuffer; - unsigned char *pchar = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf("\n\t%8d %8d %8d %8d %8d %8d", - pvalue->upper_disp_limit,pvalue->lower_disp_limit, - pvalue->upper_alarm_limit,pvalue->upper_warning_limit, - pvalue->lower_warning_limit,pvalue->lower_alarm_limit); - printf(" %8d %8d", - pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pchar++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%4d ",(short)(*pchar)); - } - break; - } - case (DBR_CTRL_LONG): - { - struct dbr_ctrl_long *pvalue - = (struct dbr_ctrl_long *)pbuffer; - dbr_long_t *plong = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf("\n\t%8d %8d %8d %8d %8d %8d", - pvalue->upper_disp_limit,pvalue->lower_disp_limit, - pvalue->upper_alarm_limit,pvalue->upper_warning_limit, - pvalue->lower_warning_limit,pvalue->lower_alarm_limit); - printf(" %8d %8d", - pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,plong++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",*plong); - } - break; - } - case (DBR_CTRL_DOUBLE): - { - struct dbr_ctrl_double *pvalue - = (struct dbr_ctrl_double *)pbuffer; - double *pdouble = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", - pvalue->precision, - (float)(pvalue->upper_disp_limit), - (float)(pvalue->lower_disp_limit), - (float)(pvalue->upper_alarm_limit), - (float)(pvalue->upper_warning_limit), - (float)(pvalue->lower_warning_limit), - (float)(pvalue->lower_alarm_limit)); - printf(" %8.3f %8.3f", - (float)(pvalue->upper_ctrl_limit), - (float)(pvalue->lower_ctrl_limit)); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pdouble++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%6.6f ",(float)(*pdouble)); - } - break; - } - case (DBR_STSACK_STRING): - { - struct dbr_stsack_string *pvalue - = (struct dbr_stsack_string *)pbuffer; - printf("%2d %2d",pvalue->status,pvalue->severity); - printf(" %2d %2d",pvalue->ackt,pvalue->acks); - printf(" %s",pvalue->value); - break; - } - case (DBR_CLASS_NAME): - { - printf(" %s",(char *)pbuffer); - break; - } + if (sscanf(pvalue, "%hd", &shortvalue) == 1) { + if (dbChannel_put(chan, DBR_SHORT,&shortvalue, 1) < 0) + printf("\n\t SHORT failed "); + if (dbChannel_get(chan, DBR_SHORT,buffer, 1, NULL) < 0) + printf("\n\t SHORT GET failed"); + else + ca_dump_dbr(DBR_SHORT,1, buffer); + } + if (sscanf(pvalue, "%ld", &longvalue) == 1) { + if (dbChannel_put(chan, DBR_LONG,&longvalue, 1) < 0) + printf("\n\t LONG failed "); + if (dbChannel_get(chan, DBR_LONG,buffer, 1, NULL) < 0) + printf("\n\t LONG GET failed"); + else + ca_dump_dbr(DBR_LONG,1, buffer); + } + if (epicsScanFloat(pvalue, &floatvalue) == 1) { + if (dbChannel_put(chan, DBR_FLOAT,&floatvalue, 1) < 0) + printf("\n\t FLOAT failed "); + if (dbChannel_get(chan, DBR_FLOAT,buffer, 1, NULL) < 0) + printf("\n\t FLOAT GET failed"); + else + ca_dump_dbr(DBR_FLOAT,1, buffer); + } + if (epicsScanFloat(pvalue, &floatvalue) == 1) { + doublevalue = floatvalue; + if (dbChannel_put(chan, DBR_DOUBLE,&doublevalue, 1) < 0) + printf("\n\t DOUBLE failed "); + if (dbChannel_get(chan, DBR_DOUBLE,buffer, 1, NULL) < 0) + printf("\n\t DOUBLE GET failed"); + else + ca_dump_dbr(DBR_DOUBLE,1, buffer); + } + if (sscanf(pvalue, "%hd", &shortvalue) == 1) { + charvalue = (unsigned char) shortvalue; + if (dbChannel_put(chan, DBR_CHAR,&charvalue, 1) < 0) + printf("\n\t CHAR failed "); + if (dbChannel_get(chan, DBR_CHAR,buffer, 1, NULL) < 0) + printf("\n\t CHAR GET failed"); + else + ca_dump_dbr(DBR_CHAR,1, buffer); + } + if (sscanf(pvalue, "%hd", &shortvalue) == 1) { + if (dbChannel_put(chan, DBR_ENUM,&shortvalue, 1) < 0) + printf("\n\t ENUM failed "); + if (dbChannel_get(chan, DBR_ENUM,buffer, 1, NULL) < 0) + printf("\n\t ENUM GET failed"); + else + ca_dump_dbr(DBR_ENUM,1, buffer); } printf("\n"); + return (0); } -#endif - + + typedef struct tpnInfo { epicsEventId callbackDone; - putNotify *ppn; -}tpnInfo; + putNotify *ppn; + struct dbChannel *chan; +} tpnInfo; static void tpnCallback(putNotify *ppn) { - struct dbAddr *pdbaddr = (struct dbAddr *)ppn->paddr; - tpnInfo *ptpnInfo = (tpnInfo *)ppn->usrPvt; + tpnInfo *ptpnInfo = (tpnInfo *) ppn->usrPvt; putNotifyStatus status = ppn->status; - char *pname = pdbaddr->precord->name; + const char *pname = dbChannelRecord(ppn->chan)->name; - if(status==0) - printf("tpnCallback: success record=%s\n",pname); + if (status == 0) + printf("tpnCallback: success record=%s\n", pname); else - printf("%s tpnCallback status = %d\n",pname,status); + printf("%s tpnCallback status = %d\n", pname, status); epicsEventSignal(ptpnInfo->callbackDone); } static void tpnThread(void *pvt) { - tpnInfo *ptpnInfo = (tpnInfo *)pvt; - putNotify *ppn = (putNotify *)ptpnInfo->ppn; + tpnInfo *ptpnInfo = (tpnInfo *) pvt; + putNotify *ppn = (putNotify *) ptpnInfo->ppn; dbPutNotify(ppn); epicsEventWait(ptpnInfo->callbackDone); dbNotifyCancel(ppn); epicsEventDestroy(ptpnInfo->callbackDone); - free((void *)ppn->paddr); + free(ppn->pbuffer); + dbChannelDelete(ppn->chan); free(ppn); free(ptpnInfo); } - -int epicsShareAPI tpn(char *pname,char *pvalue) +int epicsShareAPI tpn(char *pname, char *pvalue) { - long status; - tpnInfo *ptpnInfo; - struct dbAddr *pdbaddr=NULL; - putNotify *ppn=NULL; - char *psavevalue; - int len; + struct dbChannel *chan; + tpnInfo *ptpnInfo; + putNotify *ppn; + char *pbuffer; - if (pname==0 || pvalue==0 - || ((*pname < ' ') || (*pname > 'z')) - || ((*pvalue < ' ') || (*pvalue > 'z'))){ - printf("\nusage \"pv name\",\"value\"\n"); - return(1); + chan = dbChannel_create(pname); + if (!chan) { + printf("Channel couldn't be created\n"); + return 1; } - len = strlen(pvalue); - /*allocate space for value immediately following DBADDR*/ - pdbaddr = calloc(1,sizeof(struct dbAddr) + len+1); - if(!pdbaddr) { - printf("calloc failed\n"); - return(-1); + + pbuffer = epicsStrDup(pvalue); + ppn = calloc(1, sizeof(putNotify)); + if (!ppn) { + printf("calloc failed\n"); + return -1; } - psavevalue = (char *)(pdbaddr + 1); - strcpy(psavevalue,pvalue); - status = db_name_to_addr(pname,pdbaddr); - if(status) { - printf("db_name_to_addr failed\n"); - free((void *)pdbaddr); - return(-1); - } - ppn = calloc(1,sizeof(putNotify)); - if(!pdbaddr) { - printf("calloc failed\n"); - return(-1); - } - ppn->paddr = pdbaddr; - ppn->pbuffer = psavevalue; + ppn->chan = chan; + ppn->pbuffer = pbuffer; ppn->nRequest = 1; - if(dbPutNotifyMapType(ppn,DBR_STRING)) { - printf("dbPutNotifyMapType failed\n"); - printf("calloc failed\n"); - return(-1); + if (dbPutNotifyMapType(ppn, DBR_STRING)) { + printf("dbPutNotifyMapType failed\n"); + printf("calloc failed\n"); + return -1; } ppn->userCallback = tpnCallback; - ptpnInfo = calloc(1,sizeof(tpnInfo)); - if(!ptpnInfo) { - printf("calloc failed\n"); - return(-1); + ptpnInfo = calloc(1, sizeof(tpnInfo)); + if (!ptpnInfo) { + printf("calloc failed\n"); + return -1; } ptpnInfo->ppn = ppn; ptpnInfo->callbackDone = epicsEventCreate(epicsEventEmpty); ppn->usrPvt = ptpnInfo; - epicsThreadCreate("tpn",epicsThreadPriorityHigh, - epicsThreadGetStackSize(epicsThreadStackMedium), - tpnThread,ptpnInfo); - return(0); + epicsThreadCreate("tpn", epicsThreadPriorityHigh, + epicsThreadGetStackSize(epicsThreadStackMedium), tpnThread, ptpnInfo); + return 0; } diff --git a/src/ioc/db/recGbl.c b/src/ioc/db/recGbl.c index 7cdf69a19..80c55eb64 100644 --- a/src/ioc/db/recGbl.c +++ b/src/ioc/db/recGbl.c @@ -6,12 +6,10 @@ * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* recGbl.c */ -/* $Revision-Id$ */ /* * Author: Marty Kraimer - * Date: 11-7-90 + * Andrew Johnson */ #include @@ -34,6 +32,7 @@ #include "caeventmask.h" #define epicsExportSharedSymbols #include "dbAccessDefs.h" +#include "dbLink.h" #include "dbNotify.h" #include "dbCa.h" #include "dbEvent.h" @@ -166,60 +165,17 @@ void epicsShareAPI recGblGetControlDouble( return; } - + int epicsShareAPI recGblInitConstantLink( struct link *plink,short dbftype,void *pdest) { - if(plink->type != CONSTANT) return(FALSE); - if(!plink->value.constantStr) return(FALSE); - switch(dbftype) { - case DBF_STRING: - strcpy((char *)pdest,plink->value.constantStr); - break; - case DBF_CHAR : { - epicsInt16 value; - epicsInt8 *pvalue = (epicsInt8 *)pdest; + long status = dbLoadLink(plink, dbftype, pdest); - sscanf(plink->value.constantStr,"%hi",&value); - *pvalue = value; - } - break; - case DBF_UCHAR : { - epicsUInt16 value; - epicsUInt8 *pvalue = (epicsUInt8 *)pdest; - - sscanf(plink->value.constantStr,"%hu",&value); - *pvalue = value; - } - break; - case DBF_SHORT : - sscanf(plink->value.constantStr,"%hi",(epicsInt16 *)pdest); - break; - case DBF_USHORT : - case DBF_ENUM : - case DBF_MENU : - case DBF_DEVICE : - sscanf(plink->value.constantStr,"%hu",(epicsUInt16 *)pdest); - break; - case DBF_LONG : - *(epicsInt32 *)pdest = strtol(plink->value.constantStr, NULL, 0); - break; - case DBF_ULONG : - *(epicsUInt32 *)pdest = strtoul(plink->value.constantStr, NULL, 10); - break; - case DBF_FLOAT : - epicsScanFloat(plink->value.constantStr, (epicsFloat32 *)pdest); - break; - case DBF_DOUBLE : - epicsScanDouble(plink->value.constantStr, (epicsFloat64 *)pdest); - break; - default: - epicsPrintf("Error in recGblInitConstantLink: Illegal DBF type\n"); - return(FALSE); - } - return(TRUE); + if (status) + return FALSE; + return TRUE; } - + unsigned short epicsShareAPI recGblResetAlarms(void *precord) { dbCommon *pdbc = precord; diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index abaff1860..86f4853a1 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -27,7 +27,31 @@ dbTestHarness_SRCS_RTEMS = rtemsTestHarness.c TESTSPEC_vxWorks = dbTestHarness.munch; epicsRunDbTests TESTSPEC_RTEMS = dbTestHarness.boot; epicsRunDbTests +TESTPROD_HOST += dbChannelTest +dbChannelTest_SRCS += dbChannelTest.c xRecord_registerRecordDeviceDriver.cpp +OBJS_IOC_vxWorks += dbChannelTest +TESTS += dbChannelTest + +TESTPROD_HOST += chfPluginTest +chfPluginTest_SRCS += chfPluginTest.c xRecord_registerRecordDeviceDriver.cpp +OBJS_IOC_vxWorks += chfPluginTest +TESTS += chfPluginTest + +TESTPROD_HOST += arrShorthandTest +arrShorthandTest_SRCS += arrShorthandTest.c xRecord_registerRecordDeviceDriver.cpp +OBJS_IOC_vxWorks += arrShorthandTest +TESTS += arrShorthandTest + +TESTPROD_HOST += dbStateTest +dbStateTest_SRCS += dbStateTest.c +OBJS_IOC_vxWorks += dbStateTest +TESTS += dbStateTest + TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES +dbChannelTest$(DEP): $(COMMON_DIR)/xRecord.h +chfPluginTest$(DEP): $(COMMON_DIR)/xRecord.h +arrShorthandTest$(DEP): $(COMMON_DIR)/xRecord.h +dbStateTest$(DEP): $(COMMON_DIR)/xRecord.h diff --git a/src/ioc/db/test/arrShorthandTest.c b/src/ioc/db/test/arrShorthandTest.c new file mode 100644 index 000000000..669eab9ca --- /dev/null +++ b/src/ioc/db/test/arrShorthandTest.c @@ -0,0 +1,139 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + + /* + * Test the shorthand array notation [ start : incr : end ] + * by registering a thin fake arr plugin + * and checking if values are forwarded correctly + */ + +#include + +#include "chfPlugin.h" +#include "dbStaticLib.h" +#include "dbAccessDefs.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +typedef struct myStruct { + epicsInt32 start; + epicsInt32 incr; + epicsInt32 end; +} myStruct; + +static const +chfPluginArgDef opts[] = { + chfInt32 (myStruct, start, "s", 0, 1), + chfInt32 (myStruct, incr, "i", 0, 1), + chfInt32 (myStruct, end, "e", 0, 1), + chfPluginArgEnd +}; + +static myStruct my; + +static void * allocPvt(void) +{ + my.start = 0; + my.incr = 1; + my.end = -1; + return &my; +} + +static chfPluginIf myPif = { + allocPvt, + NULL, /* freePvt, */ + + NULL, /* parse_error, */ + NULL, /* parse_ok, */ + + NULL, /* channel_open, */ + NULL, /* channelRegisterPre, */ + NULL, /* channelRegisterPost, */ + NULL, /* channel_report, */ + NULL /* channel_close */ +}; + +static int checkValues(epicsUInt32 s, epicsUInt32 i, epicsUInt32 e) { + if (s == my.start && i == my.incr && e == my.end) + return 1; + else + return 0; +} + +static void testHead (char* title) { + testDiag("--------------------------------------------------------"); + testDiag(title); + testDiag("--------------------------------------------------------"); +} + +void xRecord_registerRecordDeviceDriver(struct dbBase *); + +MAIN(chfPluginTest) +{ + dbChannel *pch; + + testPlan(26); + + db_init_events(); + + if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + testAbort("Database description not loaded"); + + xRecord_registerRecordDeviceDriver(pdbbase); + if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + testAbort("Test database not loaded"); + + testHead("Register plugin"); + testOk(!chfPluginRegister("arr", &myPif, opts), "register fake arr plugin"); + +#define TESTBAD(Title, Expr) \ + testDiag(Title); \ + testOk(!(pch = dbChannelCreate("x." Expr)), "dbChannelCreate (" Expr ") fails"); \ + if (pch) dbChannelDelete(pch); + +#define TESTGOOD(Title, Expr, Start, Incr, End) \ + testDiag(Title); \ + testOk(!!(pch = dbChannelCreate("x." Expr)), "dbChannelCreate (" Expr ")"); \ + testOk(checkValues(Start, Incr, End), "parameters set correctly: s=%d i=%d e=%d", Start, Incr, End); \ + if (pch) dbChannelDelete(pch); + + TESTBAD("no parameters []", "[]"); + TESTBAD("invalid char at beginning [x", "[x"); + TESTBAD("invalid char after 1st arg [2x", "[2x"); + TESTBAD("invalid char after 2nd arg [2:3x", "[2:3x"); + TESTBAD("invalid char after 3rd arg [2:3:4x", "[2:3:4x"); + + TESTGOOD("one element [index]", "[2]", 2, 1, 2); + TESTGOOD("to end [s:]", "[2:]", 2, 1, -1); + TESTGOOD("to end [s::]", "[2::]", 2, 1, -1); + TESTGOOD("to end with incr [s:i:]", "[2:3:]", 2, 3, -1); + TESTGOOD("from beginning [:e]", "[:2]", 0, 1, 2); + TESTGOOD("from beginning [::e]", "[::2]", 0, 1, 2); + TESTGOOD("from begin with incr [:i:e]", "[:3:2]", 0, 3, 2); + TESTGOOD("range [s:e]", "[2:4]", 2, 1, 4); + TESTGOOD("range [s::e]", "[2::4]", 2, 1, 4); + TESTGOOD("range with incr [s:i:e]", "[2:3:4]", 2, 3, 4); + + dbFreeBase(pdbbase); + + return testDone(); +} + + +#define GEN_SIZE_OFFSET +#include "xRecord.h" + +#include +#include + +static rset xRSET; +epicsExportAddress(rset,xRSET); diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c new file mode 100644 index 000000000..20db71830 --- /dev/null +++ b/src/ioc/db/test/chfPluginTest.c @@ -0,0 +1,832 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#include "chfPlugin.h" +#include "dbStaticLib.h" +#include "dbAccessDefs.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#define PATTERN 0x55555555 +#define TYPE_START 0xAAA +#define R_LEVEL 42 + +/* Expected / actually run callback bit definitions */ +#define e_alloc 0x00000001 +#define e_free 0x00000002 +#define e_error 0x00000004 +#define e_ok 0x00000008 +#define e_open 0x00000010 +#define e_reg_pre 0x00000020 +#define e_reg_post 0x00000040 +#define e_report 0x00000080 +#define e_close 0x00000100 +#define e_pre 0x00000200 +#define e_post 0x00000400 +#define e_dtor 0x00000800 + +unsigned int e1, e2, c1, c2; +unsigned int offset; +int drop = -1; +db_field_log *dtorpfl; + +#define e_any (e_alloc | e_free | e_error | e_ok | e_open \ +| e_reg_pre | e_reg_post | e_pre | e_post | e_dtor | e_report | e_close) + +typedef struct myStruct { + int sent1; + char flag; + int sent2; + epicsUInt32 ival; + int sent3; + double dval; + int sent4; + int enumval; + int sent5; + char str[20]; + int sent6; + char c; + char c1[2]; + int offpre; + int offpost; +} myStruct; + +static const +chfPluginEnumType colorEnum[] = { {"R", 1}, {"G", 2}, {"B", 4}, {NULL,0} }; + +static const +chfPluginArgDef strictOpts[] = { + chfInt32 (myStruct, ival, "i" , 1, 0), + chfBoolean(myStruct, flag, "f" , 1, 0), + chfDouble (myStruct, dval, "d" , 1, 0), + chfString (myStruct, str, "s" , 1, 0), + chfEnum (myStruct, enumval, "c" , 1, 0, colorEnum), + chfPluginArgEnd +}; + +static const +chfPluginArgDef noconvOpts[] = { + chfInt32 (myStruct, ival, "i" , 0, 0), + chfBoolean(myStruct, flag, "f" , 0, 0), + chfDouble (myStruct, dval, "d" , 0, 0), + chfString (myStruct, str, "s" , 0, 0), + chfEnum (myStruct, enumval, "c" , 0, 0, colorEnum), + chfPluginArgEnd +}; + +static const +chfPluginArgDef sloppyOpts[] = { + chfInt32 (myStruct, ival, "i" , 0, 1), + chfBoolean(myStruct, flag, "f" , 0, 1), + chfDouble (myStruct, dval, "d" , 0, 1), + chfString (myStruct, str, "s" , 0, 1), + chfEnum (myStruct, enumval, "c" , 0, 1, colorEnum), + chfPluginArgEnd +}; + +/* Options defs with not enough room provided */ +static const +chfPluginArgDef brokenOpts1[] = { + chfInt32 (myStruct, c1, "i" , 0, 1), + chfPluginArgEnd +}; + +static const +chfPluginArgDef brokenOpts2[] = { + chfDouble(myStruct, c1, "d" , 0, 1), + chfPluginArgEnd +}; + +static const +chfPluginArgDef brokenOpts3[] = { + chfString(myStruct, c1, "s" , 0, 1), + chfPluginArgEnd +}; + +static const +chfPluginArgDef brokenOpts4[] = { + chfEnum (myStruct, c1, "c" , 0, 1, colorEnum), + chfPluginArgEnd +}; + +int p_ok_return = 0; +int c_open_return = 0; +void *puser1, *puser2; + +static void clearStruct(void *p) { + myStruct *my = (myStruct*) p; + + if (!my) return; + memset(my, 0, sizeof(myStruct)); + my->sent1 = my->sent2 = my->sent3 = my->sent4 = + my->sent5 = my->sent6 = PATTERN; + my->ival = 12; + my->flag = 1; + my->dval = 1.234e5; + strcpy(my->str, "hello"); + my->enumval = 4; +} + +static char inst(void* user) { + return user == puser1 ? '1' : user == puser2 ? '2' : 'x'; +} + +static void * allocPvt(void) +{ + myStruct *my = (myStruct*) calloc(1, sizeof(myStruct)); + + if (!puser1) { + puser1 = my; + testOk(e1 & e_alloc, "allocPvt (1) called"); + c1 |= e_alloc; + } else if (!puser2) { + puser2 = my; + testOk(e2 & e_alloc, "allocPvt (2) called"); + c2 |= e_alloc; + } + clearStruct (my); + return my; +} + +static void freePvt(void *user) +{ + if (user == puser1) { + testOk(e1 & e_free, "freePvt (1) called"); + c1 |= e_free; + free(user); + puser1 = NULL; + } else if (user == puser2) { + testOk(e2 & e_free, "freePvt (2) called"); + c2 |= e_free; + free(user); + puser2 = NULL; + } else + testFail("freePvt: user pointer invalid"); +} + +static void parse_error(void *user) +{ + if (user == puser1) { + testOk(e1 & e_error, "parse_error (1) called"); + c1 |= e_error; + } else if (user == puser2) { + testOk(e2 & e_error, "parse_error (2) called"); + c2 |= e_error; + } else + testFail("parse_error: user pointer invalid"); +} + +static int parse_ok(void *user) +{ + if (user == puser1) { + testOk(e1 & e_ok, "parse_ok (1) called"); + c1 |= e_ok; + } else if (user == puser2) { + testOk(e2 & e_ok, "parse_ok (2) called"); + c2 |= e_ok; + } else + testFail("parse_ok: user pointer invalid"); + + return p_ok_return; +} + +static long channel_open(dbChannel *chan, void *user) +{ + if (user == puser1) { + testOk(e1 & e_open, "channel_open (1) called"); + c1 |= e_open; + } else if (user == puser2) { + testOk(e2 & e_open, "channel_open (2) called"); + c2 |= e_open; + } else + testFail("channel_open: user pointer invalid"); + + return c_open_return; +} + +static void dbfl_free1(db_field_log *pfl) { + testOk(e1 & e_dtor, "dbfl_free (1) called"); + testOk(dtorpfl == pfl, "dbfl_free (1): db_field_log pointer correct"); + dtorpfl = NULL; + c1 |= e_dtor; +} + +static void dbfl_free2(db_field_log *pfl) { + testOk(e2 & e_dtor, "dbfl_free (2) called"); + testOk(dtorpfl == pfl, "dbfl_free (2): db_field_log pointer correct"); + dtorpfl = NULL; + c2 |= e_dtor; +} + +static db_field_log * pre(void *user, dbChannel *chan, db_field_log *pLog) { + myStruct *my = (myStruct*)user; + dbfl_freeFunc *dtor = NULL; + + if (my == puser1) { + testOk(e1 & e_pre, "pre (1) called"); + testOk(!(c2 & e_pre), + "pre (2) was not called before pre (1)"); + c1 |= e_pre; + dtor = dbfl_free1; + } else if (my == puser2) { + testOk(e2 & e_pre, "pre (2) called"); + testOk(!(e1 & e_pre) || c1 & e_pre, + "pre (1) was called before pre (2)"); + c2 |= e_pre; + dtor = dbfl_free2; + } else { + testFail("pre: user pointer invalid"); + testSkip(1, "Can't check order of pre(1)/pre(2)"); + } + testOk(!(c1 & e_post), + "post (1) was not called before pre (%c)", inst(user)); + testOk(!(c2 & e_post), + "post (2) was not called before pre (%c)", inst(user)); + + if (!testOk(pLog->field_type == TYPE_START + my->offpre, + "pre (%c) got field log of expected type", inst(user))) + testDiag("expected: %d, got %d", + TYPE_START + my->offpre, pLog->field_type); + pLog->field_type++; + + if (my->offpre == 0) { /* The first one registers a dtor and saves pfl */ + pLog->u.r.dtor = dtor; + dtorpfl = pLog; + } + + if (my->offpre == drop) { + testDiag("pre (%c) is dropping the field log", inst(user)); + return NULL; + } + return pLog; +} + +static db_field_log * post(void *user, dbChannel *chan, db_field_log *pLog) { + myStruct *my = (myStruct*)user; + dbfl_freeFunc *dtor = NULL; + + if (my == puser1) { + testOk(e1 & e_post, "post (1) called"); + testOk(!(c2 & e_post), + "post (2) was not called before post (1)"); + c1 |= e_post; + dtor = dbfl_free1; + } else if (my == puser2) { + testOk(e2 & e_post, "post (2) called"); + testOk(!(e1 & e_post) || c1 & e_post, + "post (1) was called before post (2)"); + c2 |= e_post; + dtor = dbfl_free2; + } else { + testFail("post: user pointer invalid"); + testSkip(1, "Can't check order of post(1)/post(2)"); + } + testOk(!(e1 & e_pre) || c1 & e_pre, + "pre (1) was called before post (%c)", inst(user)); + testOk(!(e2 & e_pre) || c2 & e_pre, + "pre (2) was called before post (%c)", inst(user)); + + if (!testOk(pLog->field_type == TYPE_START + my->offpost, + "post (%c) got field log of expected type", inst(user))) + testDiag("expected: %d, got %d", + TYPE_START + my->offpost, pLog->field_type); + pLog->field_type++; + + if (my->offpost == 0) { /* The first one registers a dtor and saves pfl */ + pLog->u.r.dtor = dtor; + dtorpfl = pLog; + } + + if (my->offpost == drop) { + testDiag("post (%c) is dropping the field log", inst(user)); + return NULL; + } + return pLog; +} + +static void channelRegisterPre(dbChannel *chan, void *user, + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) +{ + myStruct *my = (myStruct*)user; + + if (my == puser1) { + testOk(e1 & e_reg_pre, "register_pre (1) called"); + testOk(!(c2 & e_reg_pre), + "register_pre (2) was not called before register_pre (1)"); + c1 |= e_reg_pre; + } else if (my == puser2) { + testOk(e2 & e_reg_pre, "register_pre (2) called"); + testOk(!(e1 & e_reg_pre) || c1 & e_reg_pre, + "register_pre (1) was called before register_pre (2)"); + c2 |= e_reg_pre; + } else { + testFail("register_pre: user pointer invalid"); + testSkip(1, "Can't check order of register_pre(1)/register_pre(2)"); + } + testOk(!(c1 & e_reg_post), + "register_post (1) was not called before register_pre (%c)", inst(user)); + testOk(!(c2 & e_reg_post), + "register_post (2) was not called before register_pre (%c)", inst(user)); + + my->offpre = offset++; + probe->field_type++; + *cb_out = pre; + *arg_out = user; +} + +static void channelRegisterPost(dbChannel *chan, void *user, + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) +{ + myStruct *my = (myStruct*)user; + + if (my == puser1) { + testOk(e1 & e_reg_post, "register_post (1) called"); + testOk(!(c2 & e_reg_post), + "register_post (2) was not called before register_post (1)"); + c1 |= e_reg_post; + } else if (my == puser2) { + testOk(e2 & e_reg_post, "register_post (2) called"); + testOk(!(e1 & e_reg_post) || c1 & e_reg_post, + "register_post (1) was called before register_post (2)"); + c2 |= e_reg_post; + } else { + testFail("register_post: user pointer invalid"); + testSkip(1, "Can't check order of register_post(1)/register_post(2)"); + } + testOk(!(e1 & e_reg_pre) || c1 & e_reg_pre, + "register_pre (1) was called before register_post (%c)", inst(user)); + testOk(!(e2 & e_reg_pre) || c2 & e_reg_pre, + "register_pre (2) was called before register_post (%c)", inst(user)); + + my->offpost = offset++; + probe->field_type++; + *cb_out = post; + *arg_out = user; +} + +static void channel_report(dbChannel *chan, void *user, int level, + const unsigned short indent) +{ + testOk(level == R_LEVEL - 1, "channel_report: level correct"); + if (user == puser1) { + testOk(e1 & e_report, "channel_report (1) called"); + c1 |= e_report; + } else if (user == puser2) { + testOk(e2 & e_report, "channel_report (2) called"); + c2 |= e_report; + } else + testFail("channel_report: user pointer invalid"); +} + +static void channel_close(dbChannel *chan, void *user) +{ + if (user == puser1) { + testOk(e1 & e_close, "channel_close (1) called"); + c1 |= e_close; + } else if (user == puser2) { + testOk(e2 & e_close, "channel_close (2) called"); + c2 |= e_close; + } else + testFail("channel_close: user pointer invalid"); +} + +static chfPluginIf myPif = { + allocPvt, + freePvt, + + parse_error, + parse_ok, + + channel_open, + channelRegisterPre, + channelRegisterPost, + channel_report, + channel_close +}; + +static chfPluginIf prePif = { + allocPvt, + freePvt, + + parse_error, + parse_ok, + + channel_open, + channelRegisterPre, + NULL, + channel_report, + channel_close +}; + +static chfPluginIf postPif = { + allocPvt, + freePvt, + + parse_error, + parse_ok, + + channel_open, + NULL, + channelRegisterPost, + channel_report, + channel_close +}; + +static int checkValues(myStruct *my, + epicsUInt32 i, int f, double d, char *s, int c) { + if (!my) return 0; + if (my->sent1 == PATTERN && my->sent2 == PATTERN && my->sent3 == PATTERN + && my->sent4 == PATTERN && my->sent5 == PATTERN && my->sent6 == PATTERN + && my->ival == i && my->flag == f && my->dval == d && my->enumval == c + && (strcmp(s, my->str) == 0)) { + return 1; + } else { + return 0; + } +} + +static void testHead (char* title) { + testDiag("--------------------------------------------------------"); + testDiag(title); + testDiag("--------------------------------------------------------"); +} + +void xRecord_registerRecordDeviceDriver(struct dbBase *); + +MAIN(chfPluginTest) +{ + dbChannel *pch; + db_field_log *pfl; + + testPlan(1351); + + db_init_events(); + + /* Enum to string conversion */ + testHead("Enum to string conversion"); + testOk(strcmp(chfPluginEnumString(colorEnum, 1, "-"), "R") == 0, + "Enum to string: R"); + testOk(strcmp(chfPluginEnumString(colorEnum, 2, "-"), "G") == 0, + "Enum to string: G"); + testOk(strcmp(chfPluginEnumString(colorEnum, 4, "-"), "B") == 0, + "Enum to string: B"); + testOk(strcmp(chfPluginEnumString(colorEnum, 3, "-"), "-") == 0, + "Enum to string: invalid index"); + + if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + testAbort("Database description not loaded"); + + xRecord_registerRecordDeviceDriver(pdbbase); + if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + testAbort("Test database not loaded"); + + testHead("Try to register buggy plugins"); + testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts1), + "not enough storage for integer"); + testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts2), + "not enough storage for double"); + testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts3), + "not enough storage for string"); + testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts4), + "not enough storage for enum"); + + testHead("Register plugins"); + testOk(!chfPluginRegister("strict", &myPif, strictOpts), + "register plugin strict"); + testOk(!chfPluginRegister("noconv", &myPif, noconvOpts), + "register plugin noconv"); + testOk(!chfPluginRegister("sloppy", &myPif, sloppyOpts), + "register plugin sloppy"); + testOk(!chfPluginRegister("pre", &prePif, sloppyOpts), + "register plugin pre"); + testOk(!chfPluginRegister("post", &postPif, sloppyOpts), + "register plugin post"); + + /* STRICT parsing: mandatory, no conversion */ + + /* All perfect */ + testHead("STRICT parsing: all ok"); + e1 = e_alloc | e_ok; c1 = 0; + testOk(!!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "strict parsing: JSON correct"); + testOk(checkValues(puser1, 1, 0, 1.2e15, "bar", 1), + "guards intact, values correct"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + e1 = e_close | e_free; c1 = 0; + if (pch) dbChannelDelete(pch); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + + /* Any one missing must fail */ + testHead("STRICT parsing: any missing parameter must fail"); + e1 = e_alloc | e_error | e_free; c1 = 0; + testOk(!(pch = dbChannelCreate( + "x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), + "strict parsing: c missing"); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + e1 = e_alloc | e_error | e_free; c1 = 0; + testOk(!(pch = dbChannelCreate( + "x.{\"strict\":{\"f\":false,\"i\":1,\"d\":1.2e15,\"c\":\"R\"}}")), + "strict parsing: s missing"); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + e1 = e_alloc | e_error | e_free; c1 = 0; + testOk(!(pch = dbChannelCreate( + "x.{\"strict\":{\"i\":1,\"c\":\"R\",\"f\":false,\"s\":\"bar\"}}")), + "strict parsing: d missing"); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + e1 = e_alloc | e_error | e_free; c1 = 0; + testOk(!(pch = dbChannelCreate( + "x.{\"strict\":{\"d\":1.2e15,\"c\":\"R\",\"i\":1,\"s\":\"bar\"}}")), + "strict parsing: f missing"); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + e1 = e_alloc | e_error | e_free; c1 = 0; + testOk(!(pch = dbChannelCreate( + "x.{\"strict\":{\"c\":\"R\",\"s\":\"bar\",\"f\":false,\"d\":1.2e15}}")), + "strict parsing: i missing"); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + + /* NOCONV parsing: optional, no conversion */ + + /* Any one missing must leave the default intact */ + testHead("NOCONV parsing: missing parameters get default value"); + e1 = e_alloc | e_ok; c1 = 0; + testOk(!!(pch = dbChannelCreate( + "x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), + "noconv parsing: c missing"); + testOk(checkValues(puser1, 1, 0, 1.2e15, "bar", 4), + "guards intact, values correct"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + e1 = e_close | e_free; c1 = 0; + if (pch) dbChannelDelete(pch); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + + e1 = e_any; + testOk(!!(pch = dbChannelCreate( + "x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"c\":\"R\"}}")), + "noconv parsing: s missing"); + testOk(checkValues(puser1, 1, 0, 1.2e15, "hello", 1), + "guards intact, values correct"); + if (pch) dbChannelDelete(pch); + + testOk(!!(pch = dbChannelCreate( + "x.{\"noconv\":{\"i\":1,\"f\":false,\"s\":\"bar\",\"c\":\"R\"}}")), + "noconv parsing: d missing"); + testOk(checkValues(puser1, 1, 0, 1.234e5, "bar", 1), + "guards intact, values correct"); + if (pch) dbChannelDelete(pch); + + testOk(!!(pch = dbChannelCreate( + "x.{\"noconv\":{\"i\":1,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), + "noconv parsing: f missing"); + testOk(checkValues(puser1, 1, 1, 1.2e15, "bar", 1), + "guards intact, values correct"); + if (pch) dbChannelDelete(pch); + + testOk(!!(pch = dbChannelCreate( + "x.{\"noconv\":{\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), + "noconv parsing: i missing"); + testOk(checkValues(puser1, 12, 0, 1.2e15, "bar", 1), + "guards intact, values correct"); + if (pch) dbChannelDelete(pch); + + /* Reject wrong types */ +#define WRONGTYPETEST(Var, Val, Typ) \ + e1 = e_alloc | e_error | e_free; c1 = 0; \ + testOk(!(pch = dbChannelCreate("x.{\"noconv\":{\""#Var"\":"#Val"}}")), \ + "noconv parsing: wrong type "#Typ" for "#Var); \ + testOk(!puser1, "user part cleaned up"); \ + if (!testOk(c1 == e1, "all expected calls happened")) \ + testDiag("expected %#x - called %#x", e1, c1); + + testHead("NOCONV parsing: rejection of wrong parameter types"); + + WRONGTYPETEST(i, 123.0, double); + WRONGTYPETEST(i, true, boolean); + WRONGTYPETEST(i, "1", string); + WRONGTYPETEST(f, "false", string); + WRONGTYPETEST(f, 0.0, double); + WRONGTYPETEST(f, 1, integer); + WRONGTYPETEST(d, "1.2", string); + WRONGTYPETEST(d, true, boolean); + WRONGTYPETEST(d, 123, integer); + WRONGTYPETEST(s, 1.23, double); + WRONGTYPETEST(s, true, boolean); + WRONGTYPETEST(s, 123, integer); + WRONGTYPETEST(c, 1.23, double); + WRONGTYPETEST(c, true, boolean); + WRONGTYPETEST(c, 2, integer); + + /* SLOPPY parsing: optional, with conversion */ + +#define CONVTESTGOOD(Var, Val, Typ, Ival, Fval, Dval, Sval, Cval) \ + e1 = e_alloc | e_ok; c1 = 0; \ + testOk(!!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), \ + "sloppy parsing: "#Typ" (good) for "#Var); \ + testOk(checkValues(puser1, Ival, Fval, Dval, Sval, Cval), \ + "guards intact, values correct"); \ + if (!testOk(c1 == e1, "create channel: all expected calls happened")) \ + testDiag("expected %#x - called %#x", e1, c1); \ + e1 = e_close | e_free; c1 = 0; \ + if (pch) dbChannelDelete(pch); \ + testOk(!puser1, "user part cleaned up"); \ + if (!testOk(c1 == e1, "delete channel: all expected calls happened")) \ + testDiag("expected %#x - called %#x", e1, c1); + +#define CONVTESTBAD(Var, Val, Typ) \ + e1 = e_alloc | e_error | e_free; c1 = 0; \ + testOk(!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), \ + "sloppy parsing: "#Typ" (bad) for "#Var); \ + testOk(!puser1, "user part cleaned up"); \ + if (!testOk(c1 == e1, "create channel: all expected calls happened")) \ + testDiag("expected %#x - called %#x", e1, c1); + + /* To integer */ + testHead("SLOPPY parsing: conversion to integer"); + CONVTESTGOOD(i, "123e4", positive string, 123, 1, 1.234e5, "hello", 4); + CONVTESTGOOD(i, "-12345", negative string, -12345, 1, 1.234e5, "hello", 4); + CONVTESTBAD(i, "9234567890", out-of-range string); + CONVTESTBAD(i, ".4", invalid string); + CONVTESTGOOD(i, false, valid boolean, 0, 1, 1.234e5, "hello", 4); + CONVTESTGOOD(i, 3456.789, valid double, 3456, 1, 1.234e5, "hello", 4); + CONVTESTBAD(i, 34.7e14, out-of-range double); + + /* To boolean */ + testHead("SLOPPY parsing: conversion to boolean"); + CONVTESTGOOD(f, "false", valid string, 12, 0, 1.234e5, "hello", 4); + CONVTESTGOOD(f, "False", capital valid string, 12, 0, 1.234e5, "hello", 4); + CONVTESTGOOD(f, "0", 0 string, 12, 0, 1.234e5, "hello", 4); + CONVTESTGOOD(f, "15", 15 string, 12, 1, 1.234e5, "hello", 4); + CONVTESTBAD(f, ".4", invalid .4 string); + CONVTESTBAD(f, "Flase", misspelled invalid string); + CONVTESTGOOD(f, 0, zero integer, 12, 0, 1.234e5, "hello", 4); + CONVTESTGOOD(f, 12, positive integer, 12, 1, 1.234e5, "hello", 4); + CONVTESTGOOD(f, -1234, negative integer, 12, 1, 1.234e5, "hello", 4); + CONVTESTGOOD(f, 0.4, positive non-zero double, 12, 1, 1.234e5, "hello", 4); + CONVTESTGOOD(f, 0.0, zero double, 12, 0, 1.234e5, "hello", 4); + CONVTESTGOOD(f, -0.0, minus-zero double, 12, 0, 1.234e5, "hello", 4); + CONVTESTGOOD(f, -1.24e14, negative double, 12, 1, 1.234e5, "hello", 4); + + /* To double */ + testHead("SLOPPY parsing: conversion to double"); + CONVTESTGOOD(d, "123e4", positive double string, 12, 1, 1.23e6, "hello", 4); + CONVTESTGOOD(d, "-7.89e-14", negative double string, 12, 1, -7.89e-14, "hello", 4); + CONVTESTGOOD(d, "123", positive integer string, 12, 1, 123.0, "hello", 4); + CONVTESTGOOD(d, "-1234567", negative integer string, 12, 1, -1.234567e6, "hello", 4); + CONVTESTBAD(d, "1.67e407", out-of-range double string); + CONVTESTBAD(d, "blubb", invalid blubb string); + CONVTESTGOOD(d, 123, positive integer, 12, 1, 123.0, "hello", 4); + CONVTESTGOOD(d, -12345, negative integer, 12, 1, -1.2345e4, "hello", 4); + CONVTESTGOOD(d, true, true boolean, 12, 1, 1.0, "hello", 4); + CONVTESTGOOD(d, false, false boolean, 12, 1, 0.0, "hello", 4); + + /* To string */ + testHead("SLOPPY parsing: conversion to string"); + CONVTESTGOOD(s, 12345, positive integer, 12, 1, 1.234e5, "12345", 4); + CONVTESTGOOD(s, -1234567891, negative integer, 12, 1, 1.234e5, "-1234567891", 4); + CONVTESTGOOD(s, true, true boolean, 12, 1, 1.234e5, "true", 4); + CONVTESTGOOD(s, false, false boolean, 12, 1, 1.234e5, "false", 4); + CONVTESTGOOD(s, 123e4, small positive double, 12, 1, 1.234e5, "1230000", 4); + CONVTESTGOOD(s, -123e24, negative double, 12, 1, 1.234e5, "-1.23e+26", 4); + CONVTESTGOOD(s, -1.23456789123456789e26, large rounded negative double, 12, 1, 1.234e5, "-1.234567891235e+26", 4); + + /* To Enum */ + testHead("SLOPPY parsing: conversion to enum"); + CONVTESTGOOD(c, 2, valid integer choice, 12, 1, 1.234e5, "hello", 2); + CONVTESTBAD(c, 3, invalid integer choice); + CONVTESTBAD(c, 3.2, double); + CONVTESTGOOD(c, "R", valid string choice, 12, 1, 1.234e5, "hello", 1); + CONVTESTBAD(c, "blubb", invalid string choice); + + /* Registering and running filter callbacks */ + +#define CHAINTEST1(Type, Json, ExpReg, ExpRun, DType) \ + testHead("Filter chain test, "Type" filter"); \ + offset = 0; \ + e1 = e_alloc | e_ok; c1 = 0; \ + testOk(!!(pch = dbChannelCreate("x."Json)), "filter chains: create channel with "Type" filter"); \ + if (!testOk(c1 == e1, "create channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + e1 = e_open | ExpReg; c1 = 0; \ + testOk(!dbChannelOpen(pch), "dbChannelOpen returned channel"); \ + if (!testOk(c1 == e1, "open channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + e1 = ExpRun; c1 = 0; \ + testOk(!!(pfl = db_create_read_log(pch)), "create db_field_log"); \ + pfl->type = dbfl_type_ref; \ + pfl->field_type = TYPE_START; \ + testOk(!!(pfl = dbChannelRunPreChain(pch, pfl)), "run pre eventq chain"); \ + testOk(!!(pfl = dbChannelRunPostChain(pch, pfl)), "run post eventq chain"); \ + testOk(pfl->field_type == TYPE_START + DType, "final data type is correct"); \ + db_delete_field_log(pfl); \ + if (!testOk(c1 == e1, "run filter chains: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + e1 = e_report; c1 = 0; \ + dbChannelShow(pch, R_LEVEL, 0); \ + if (!testOk(c1 == e1, "report: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + e1 = e_close | e_free; c1 = 0; \ + if (pch) dbChannelDelete(pch); \ + if (!testOk(c1 == e1, "delete channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + +#define CHAINTEST2(Type, Json, ExpReg1, ExpRun1, ExpReg2, ExpRun2, DType) \ + testHead("Filter chain test, "Type" filters"); \ + offset = 0; \ + e1 = e_alloc | e_ok; c1 = 0; \ + e2 = e_alloc | e_ok; c2 = 0; \ + testOk(!!(pch = dbChannelCreate("x."Json)), "filter chains: create channel with "Type" filters"); \ + if (!testOk(c1 == e1, "create channel (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + if (!testOk(c2 == e2, "create channel (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \ + e1 = e_open | ExpReg1; c1 = 0; \ + e2 = e_open | ExpReg2; c2 = 0; \ + if (pch) testOk(!dbChannelOpen(pch), "dbChannelOpen returned channel"); \ + if (!testOk(c1 == e1, "open channel (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + if (!testOk(c2 == e2, "open channel (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \ + e1 = ExpRun1; c1 = 0; \ + e2 = ExpRun2; c2 = 0; \ + if (pch) testOk(!!(pfl = db_create_read_log(pch)), "create db_field_log"); \ + pfl->type = dbfl_type_ref; \ + pfl->field_type = TYPE_START; \ + if (pch) testOk(!!(pfl = dbChannelRunPreChain(pch, pfl)) || (drop >=0 && drop <= 1), "run pre eventq chain"); \ + if (pch && (drop < 0 || drop >= 2)) testOk(!!(pfl = dbChannelRunPostChain(pch, pfl)) || drop >=2, "run post eventq chain"); \ + if (pfl) testOk(pfl->field_type == TYPE_START + DType, "final data type is correct"); \ + if (pfl) db_delete_field_log(pfl); \ + if (!testOk(c1 == e1, "run filter chains (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + if (!testOk(c2 == e2, "run filter chains (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \ + e1 = e_report; c1 = 0; \ + e2 = e_report; c2 = 0; \ + dbChannelShow(pch, R_LEVEL, 0); \ + if (!testOk(c1 == e1, "report (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + if (!testOk(c2 == e2, "report (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \ + e1 = e_close | e_free; c1 = 0; \ + e2 = e_close | e_free; c2 = 0; \ + if (pch) dbChannelDelete(pch); \ + if (!testOk(c1 == e1, "delete channel (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + if (!testOk(c2 == e2, "delete channel (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); + + CHAINTEST1("1 pre", "{\"pre\":{}}", e_reg_pre, e_pre | e_dtor, 1); /* One filter, pre chain */ + CHAINTEST1("1 post", "{\"post\":{}}", e_reg_post, e_post | e_dtor, 1); /* One filter, post chain */ + CHAINTEST1("1 both", "{\"sloppy\":{}}", e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, 2); /* One, both chains */ + CHAINTEST2("2 pre", "{\"pre\":{},\"pre\":{}}", e_reg_pre, e_pre | e_dtor, e_reg_pre, e_pre, 2); /* Two filters, pre chain */ + CHAINTEST2("2 post", "{\"post\":{},\"post\":{}}", e_reg_post, e_post | e_dtor, e_reg_post, e_post, 2); /* Two filters, post chain */ + CHAINTEST2("2 both", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains */ + e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, e_reg_pre | e_reg_post, e_pre | e_post, 4); + CHAINTEST2("1 pre, 1 post", "{\"pre\":{},\"post\":{}}", e_reg_pre, e_pre | e_dtor, e_reg_post, e_post, 2); /* Two, pre then post */ + CHAINTEST2("1 post, 1 pre", "{\"post\":{},\"pre\":{}}", e_reg_post, e_post, e_reg_pre, e_pre | e_dtor, 2); /* Two, post then pre */ + CHAINTEST2("1 pre, 1 both", "{\"pre\":{},\"sloppy\":{}}", /* Two, pre then both */ + e_reg_pre, e_pre | e_dtor, e_reg_pre | e_reg_post, e_pre | e_post, 3); + CHAINTEST2("1 both, 1 pre", "{\"sloppy\":{},\"pre\":{}}", /* Two, both then pre */ + e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, e_reg_pre, e_pre, 3); + CHAINTEST2("1 post, 1 both", "{\"post\":{},\"sloppy\":{}}", /* Two, post then both */ + e_reg_post, e_post, e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, 3); + CHAINTEST2("1 both, 1 post", "{\"sloppy\":{},\"post\":{}}", /* Two, both then post */ + e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, e_reg_post, e_post, 3); + + /* Plugins dropping updates */ + drop = 0; + CHAINTEST2("2 both (drop at 0)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 0 */ + e_reg_pre | e_reg_post, e_pre, e_reg_pre | e_reg_post, 0, -1); + drop = 1; + CHAINTEST2("2 both (drop at 1)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 1 */ + e_reg_pre | e_reg_post, e_pre, e_reg_pre | e_reg_post, e_pre, -1); + drop = 2; + CHAINTEST2("2 both (drop at 2)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 2 */ + e_reg_pre | e_reg_post, e_pre | e_post, e_reg_pre | e_reg_post, e_pre, -1); + drop = 3; + CHAINTEST2("2 both (drop at 3)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 3 */ + e_reg_pre | e_reg_post, e_pre | e_post, e_reg_pre | e_reg_post, e_pre | e_post, -1); + drop = -1; + + dbFreeBase(pdbbase); + + return testDone(); +} + +#define GEN_SIZE_OFFSET +#include "xRecord.h" + +#include +#include + +static rset xRSET; +epicsExportAddress(rset,xRSET); diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c new file mode 100644 index 000000000..94de616f7 --- /dev/null +++ b/src/ioc/db/test/dbChannelTest.c @@ -0,0 +1,261 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. + \*************************************************************************/ + +/* + * Author: Andrew Johnson + * Ralph Lange + */ + +#include "dbChannel.h" +#include "dbStaticLib.h" +#include "dbAccessDefs.h" +#include "recSup.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +/* Expected call bit definitions */ +#define e_start 0x00000001 +#define e_abort 0x00000002 +#define e_end 0x00000004 +#define e_null 0x00000008 +#define e_boolean 0x00000010 +#define e_integer 0x00000020 +#define e_double 0x00000040 +#define e_string 0x00000080 +#define e_start_map 0x00000100 +#define e_map_key 0x00000200 +#define e_end_map 0x00000400 +#define e_start_array 0x00000800 +#define e_end_array 0x00001000 +#define e_open 0x00002000 +#define e_reg_pre 0x00004000 +#define e_reg_post 0x00008000 +#define e_report 0x00010000 +#define e_close 0x00020000 + +#define r_any (e_start | e_abort | e_end | \ + e_null | e_boolean | e_integer | e_double | e_string | \ + e_start_map | e_map_key | e_end_map | e_start_array | e_end_array) +#define r_scalar (e_start | e_abort | e_end | \ + e_null | e_boolean | e_integer | e_double | e_string) + +unsigned int e, r; +#define p_ret(x) return r & x ? parse_continue : parse_stop + +parse_result p_start(chFilter *filter) +{ + testOk(e & e_start, "parse_start called"); + p_ret(e_start); +} + +void p_abort(chFilter *filter) +{ + testOk(e & e_abort, "parse_abort called"); +} + +parse_result p_end(chFilter *filter) +{ + testOk(e & e_end, "parse_end called"); + p_ret(e_end); +} + +parse_result p_null(chFilter *filter) +{ + testOk(e & e_null, "parse_null called"); + p_ret(e_null); +} +parse_result p_boolean(chFilter *filter, int boolVal) +{ + testOk(e & e_boolean, "parse_boolean called, val = %d", boolVal); + p_ret(e_boolean); +} +parse_result p_integer(chFilter *filter, long integerVal) +{ + testOk(e & e_integer, "parse_integer called, val = %ld", integerVal); + p_ret(e_integer); +} +parse_result p_double(chFilter *filter, double doubleVal) +{ + testOk(e & e_double, "parse_double called, val = %g", doubleVal); + p_ret(e_double); +} +parse_result p_string(chFilter *filter, const char *stringVal, size_t stringLen) +{ + testOk(e & e_string, "parse_string called, val = '%.*s'", (int) stringLen, + stringVal); + p_ret(e_string); +} + +parse_result p_start_map(chFilter *filter) +{ + testOk(e & e_start_map, "parse_start_map called"); + p_ret(e_start_map); +} +parse_result p_map_key(chFilter *filter, const char *key, size_t stringLen) +{ + testOk(e & e_map_key, "parse_map_key called, key = '%.*s'", (int) stringLen, key); + p_ret(e_map_key); +} +parse_result p_end_map(chFilter *filter) +{ + testOk(e & e_end_map, "parse_end_map called"); + p_ret(e_end_map); +} + +parse_result p_start_array(chFilter *filter) +{ + testOk(e & e_start_array, "parse_start_array called"); + p_ret(e_start_array); +} +parse_result p_end_array(chFilter *filter) +{ + testOk(e & e_end_array, "parse_end_array called"); + p_ret(e_end_array); +} + +long c_open(chFilter *filter) +{ + testOk(e & e_open, "channel_open called"); + return 0; +} +void c_reg_pre(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) +{ + testOk(e & e_reg_pre, "channel_register_pre called"); +} +void c_reg_post(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) +{ + testOk(e & e_reg_post, "channel_register_post called"); +} +void c_report(chFilter *filter, int level, const unsigned short indent) +{ + testOk(e & e_report, "channel_report called, level = %d", level); +} +void c_close(chFilter *filter) +{ + testOk(e & e_close, "channel_close called"); +} + +chFilterIf testIf = + { p_start, p_abort, p_end, p_null, p_boolean, p_integer, p_double, + p_string, p_start_map, p_map_key, p_end_map, p_start_array, p_end_array, + c_open, c_reg_pre, c_reg_post, c_report, c_close }; + +void xRecord_registerRecordDeviceDriver(struct dbBase *); + +MAIN(testDbChannel) /* dbChannelTest is an API routine... */ +{ + dbChannel *pch; + + testPlan(66); + + if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + testAbort("Database description not loaded"); + + xRecord_registerRecordDeviceDriver(pdbbase); + if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + testAbort("Test database not loaded"); + + r = e = 0; + /* dbChannelTest() checks record and field names */ + testOk1(!dbChannelTest("x.NAME")); + testOk1(!dbChannelTest("x.VAL")); + testOk1(!dbChannelTest("x.")); + testOk1(!dbChannelTest("x")); + testOk(dbChannelTest("y"), "Test, nonexistent record"); + testOk(dbChannelTest("x.NOFIELD"), "Test, nonexistent field"); + + /* dbChannelTest() allows but ignores field modifiers */ + testOk1(!dbChannelTest("x.NAME$")); + testOk1(!dbChannelTest("x.{}")); + testOk1(!dbChannelTest("x.VAL{\"json\":true}")); + + /* dbChannelCreate() accepts field modifiers */ + testOk1(!!(pch = dbChannelCreate("x.{}"))); + if (pch) dbChannelDelete(pch); + testOk1(!!(pch = dbChannelCreate("x.VAL{}"))); + if (pch) dbChannelDelete(pch); + testOk1(!!(pch = dbChannelCreate("x.NAME$"))); + testOk1(pch && pch->addr.no_elements > 1); + if (pch) dbChannelDelete(pch); + testOk1(!!(pch = dbChannelCreate("x.NAME${}"))); + testOk1(pch && pch->addr.no_elements > 1); + if (pch) dbChannelDelete(pch); + + /* dbChannelCreate() rejects bad PVs */ + testOk(!dbChannelCreate("y"), "Create, bad record"); + testOk(!dbChannelCreate("x.NOFIELD"), "Create, bad field"); + testOk(!dbChannelCreate("x.{not-json}"), "Create, bad JSON"); + testOk(!dbChannelCreate("x.{\"none\":null}"), "Create, bad filter"); + + dbRegisterFilter("any", &testIf, NULL); + + /* Parser event rejection by filter */ + e = e_start; + testOk1(!dbChannelCreate("x.{\"any\":null}")); + + r = e_start; + e = e_start | e_null | e_abort; + testOk1(!dbChannelCreate("x.{\"any\":null}")); + + r = e_start | e_null; + e = e_start | e_null | e_end; + testOk1(!dbChannelCreate("x.{\"any\":null}")); + + /* Successful parsing... */ + r = r_any; + e = e_start | e_null | e_end; + testOk1(!!(pch = dbChannelCreate("x.{\"any\":null}"))); + e = e_close; + if (pch) dbChannelDelete(pch); + + dbRegisterFilter("scalar", &testIf, NULL); + + e = e_start | e_null | e_end; + testOk1(!!(pch = dbChannelCreate("x.{\"scalar\":null}"))); + + e = e_report; + dbChannelShow(pch, 1, 2); + + e = e_close; + if (pch) dbChannelDelete(pch); + + e = e_start | e_start_array | e_boolean | e_integer | e_end_array + | e_end; + testOk1(!!(pch = dbChannelCreate("x.{\"any\":[true,1]}"))); + e = e_close; + if (pch) dbChannelDelete(pch); + + e = e_start | e_start_map | e_map_key | e_double | e_string | e_end_map + | e_end; + testOk1(!!(pch = dbChannelCreate("x.{\"any\":{\"a\":2.7183,\"b\":\"c\"}}"))); + e = e_close; + if (pch) dbChannelDelete(pch); + + /* More event rejection */ + r = r_scalar; + e = e_start | e_start_array | e_abort; + testOk1(!dbChannelCreate("x.{\"scalar\":[null]}")); + + e = e_start | e_start_map | e_abort; + testOk1(!dbChannelCreate("x.{\"scalar\":{}}")); + + dbFreeBase(pdbbase); + + return testDone(); +} + +#define GEN_SIZE_OFFSET +#include "xRecord.h" + +#include +#include + +static rset xRSET; +epicsExportAddress(rset,xRSET); diff --git a/src/ioc/db/test/dbChannelTest.db b/src/ioc/db/test/dbChannelTest.db new file mode 100644 index 000000000..a6fa08e40 --- /dev/null +++ b/src/ioc/db/test/dbChannelTest.db @@ -0,0 +1,2 @@ +record(x, x) {} + diff --git a/src/ioc/db/test/dbStateTest.c b/src/ioc/db/test/dbStateTest.c new file mode 100644 index 000000000..ae7b6512c --- /dev/null +++ b/src/ioc/db/test/dbStateTest.c @@ -0,0 +1,56 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#include "dbState.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +MAIN(dbStateTest) +{ + dbStateId red, red2, blue, blue2; + int i; + + testPlan(20); + + testOk(!dbStateFind("y"), "Finding nonexisting state fails"); + + testOk(!!(red = dbStateCreate("red")), "Create state 'red'"); + testOk((red2 = dbStateFind("red")) == red, "Find 'red' returns correct id"); + testOk((red2 = dbStateCreate("red")) == red, "Create existing 'red' returns correct id"); + testOk(!dbStateFind("y"), "Finding nonexisting state still fails"); + + testOk(!!(blue = dbStateCreate("blue")), "Create state 'blue'"); + testOk((blue2 = dbStateFind("blue")) == blue, "Find 'blue' returns correct id"); + testOk((blue2 = dbStateCreate("blue")) == blue, "Create existing 'blue' returns correct id"); + testOk(!dbStateFind("y"), "Finding nonexisting state still fails"); + + testOk((i = dbStateGet(red)) == 0, "Default 'red' state is 0"); + testOk((i = dbStateGet(blue)) == 0, "Default 'blue' state is 0"); + dbStateSet(red); + testOk((i = dbStateGet(red)) == 1, "After setting, 'red' state is 1"); + testOk((i = dbStateGet(blue)) == 0, "'blue' state is 0"); + dbStateSet(blue); + testOk((i = dbStateGet(blue)) == 1, "After setting, 'blue' state is 1"); + testOk((i = dbStateGet(red)) == 1, "'red' state is 1"); + dbStateClear(blue); + testOk((i = dbStateGet(blue)) == 0, "After clearing, 'blue' state is 0"); + testOk((i = dbStateGet(red)) == 1, "'red' state is 1"); + dbStateClear(red); + testOk((i = dbStateGet(red)) == 0, "After clearing, 'red' state is 0"); + testOk((i = dbStateGet(blue)) == 0, "'red' state is 0"); + + testOk(!dbStateFind("y"), "Finding nonexisting state still fails"); + + return testDone(); +} diff --git a/src/ioc/db/test/xRecord.dbd b/src/ioc/db/test/xRecord.dbd new file mode 100644 index 000000000..4837871a4 --- /dev/null +++ b/src/ioc/db/test/xRecord.dbd @@ -0,0 +1,12 @@ +# This is a combined minimal DBD and DB file + +recordtype(x) { + field(NAME, DBF_STRING) { + prompt("Record Name") + special(SPC_NOMOD) + size(61) + } + field(VAL, DBF_LONG) { + prompt("Value") + } +} diff --git a/src/ioc/dbStatic/dbBase.h b/src/ioc/dbStatic/dbBase.h index 55268ea3a..6be2465d5 100644 --- a/src/ioc/dbStatic/dbBase.h +++ b/src/ioc/dbStatic/dbBase.h @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos 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. \*************************************************************************/ /* $Revision-Id$ * @@ -19,7 +19,7 @@ #include "dbFldTypes.h" #include "ellLib.h" #include "dbDefs.h" - + typedef struct dbMenu { ELLNODE node; char *name; @@ -67,7 +67,7 @@ typedef struct brkTable { /* breakpoint table */ long number; /*number of brkInt in this table*/ struct brkInt *paBrkInt; /* ptr to array of brkInts */ }brkTable; - + typedef struct dbFldDes{ /* field description */ char *prompt; /*Prompt string for DCT*/ char *name; /*Field name*/ @@ -128,7 +128,7 @@ typedef struct dbVariableDef { ELLNODE node; char *name; char *type; - + }dbVariableDef; typedef struct dbRecordType { @@ -164,6 +164,7 @@ typedef struct dbBase { ELLLIST functionList; ELLLIST variableList; ELLLIST bptList; + ELLLIST filterList; void *pathPvt; struct dbPvd *ppvd; struct gphPvt *pgpHash; diff --git a/src/ioc/dbStatic/dbPvdLib.c b/src/ioc/dbStatic/dbPvdLib.c index 43f9cd40f..49060366b 100644 --- a/src/ioc/dbStatic/dbPvdLib.c +++ b/src/ioc/dbStatic/dbPvdLib.c @@ -181,6 +181,7 @@ void dbPvdFreeMem(dbBase *pdbbase) ellDelete(&pbucket->list, (ELLNODE *)ppvdNode); free(ppvdNode); } + epicsMutexUnlock(pbucket->lock); epicsMutexDestroy(pbucket->lock); free(pbucket); } diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 8841e1aeb..0bcf825fb 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos 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. \*************************************************************************/ /* $Revision-Id$ */ @@ -41,7 +41,7 @@ #include "guigroup.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" - + int dbStaticDebug = 0; static char *pNullString = ""; #define messagesize 100 @@ -191,7 +191,7 @@ sizeof(promptINST_IO)/sizeof(char *), sizeof(promptBBGPIB_IO)/sizeof(char *), sizeof(promptRF_IO)/sizeof(char *), sizeof(promptVXI_IO)/sizeof(char *)}; - + /*forward references for private routines*/ static FILE *openOutstream(const char *filename); static void finishOutstream(FILE *stream); @@ -203,7 +203,7 @@ static void zeroDbentry(DBENTRY *pdbentry); static char *getpMessage(DBENTRY *pdbentry); static long putPvLink(DBENTRY *pdbentry,short pvlMask,const char *pvname); static long epicsShareAPI dbAddOnePath (DBBASE *pdbbase, const char *path, unsigned length); - + /* internal routines*/ static FILE *openOutstream(const char *filename) { @@ -347,7 +347,7 @@ void dbFreeLinkContents(struct link *plink) if(plink->text) free(plink->text); memset((char *)plink,0,sizeof(struct link)); } - + void dbFreePath(DBBASE *pdbbase) { ELLLIST *ppathList; @@ -366,7 +366,7 @@ void dbFreePath(DBBASE *pdbbase) return; } - + static long mapLINKTtoFORMT(DBLINK *plink,dbFldDes *pflddes,int *ind) { switch(plink->type) { @@ -407,7 +407,7 @@ static long mapLINKTtoFORMT(DBLINK *plink,dbFldDes *pflddes,int *ind) } return(S_dbLib_badLink); } - + static void entryErrMessage(DBENTRY *pdbentry,long status,char *mess) { char message[200]; @@ -439,7 +439,7 @@ static void entryErrMessage(DBENTRY *pdbentry,long status,char *mess) strcat(pmessage,mess); errMessage(status,pmessage); } - + static void zeroDbentry(DBENTRY *pdbentry) { /*NOTE that pdbbase, message, and formpvt MUST NOT be set to NULL*/ @@ -466,7 +466,7 @@ static long putPvLink(DBENTRY *pdbentry,short pvlMask,const char *pvname) dbFldDes *pflddes; DBLINK *plink; char *pname; - + dbGetFieldAddress(pdbentry); pflddes = pdbentry->pflddes; if(!pflddes) return(-1); @@ -489,7 +489,7 @@ static long putPvLink(DBENTRY *pdbentry,short pvlMask,const char *pvname) } return(S_dbLib_badLink); } - + /*Public only for dbStaticNoRun*/ dbDeviceMenu *dbGetDeviceMenu(DBENTRY *pdbentry) { @@ -526,7 +526,7 @@ dbDeviceMenu *dbGetDeviceMenu(DBENTRY *pdbentry) pflddes->ftPvt = pdbDeviceMenu; return(pdbDeviceMenu); } - + /* Beginning of Public Routines */ #define INC_SIZE 256 @@ -555,7 +555,7 @@ void epicsShareAPI dbCatString(char **string,int *stringLength,char *src,char *s strcat(*string,src); *stringLength += strlen(src); } - + dbBase * epicsShareAPI dbAllocBase(void) { dbBase *pdbbase; @@ -568,6 +568,7 @@ dbBase * epicsShareAPI dbAllocBase(void) ellInit(&pdbbase->functionList); ellInit(&pdbbase->variableList); ellInit(&pdbbase->bptList); + ellInit(&pdbbase->filterList); gphInitPvt(&pdbbase->pgpHash,256); dbPvdInitPvt(pdbbase); return (pdbbase); @@ -595,7 +596,7 @@ void epicsShareAPI dbFreeBase(dbBase *pdbbase) brkTable *pbrkTableNext; int i; DBENTRY dbentry; - + dbInitEntry(pdbbase,&dbentry); pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); @@ -620,7 +621,7 @@ void epicsShareAPI dbFreeBase(dbBase *pdbbase) free((void *)pdbFldDes->initial); if(pdbFldDes->field_type==DBF_DEVICE && pdbFldDes->ftPvt) { dbDeviceMenu *pdbDeviceMenu; - + pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt; free((void *)pdbDeviceMenu->papChoice); free((void *)pdbDeviceMenu); @@ -729,12 +730,12 @@ void epicsShareAPI dbFreeBase(dbBase *pdbbase) free((void *)pdbbase); return; } - + DBENTRY * epicsShareAPI dbAllocEntry(dbBase *pdbbase) { DBENTRY *pdbentry; - pdbentry = dbmfMalloc(sizeof(DBENTRY)); + pdbentry = dbmfMalloc(sizeof(DBENTRY)); memset(pdbentry,'\0',sizeof(DBENTRY)); pdbentry->pdbbase = pdbbase; return(pdbentry); @@ -780,7 +781,7 @@ void epicsShareAPI dbCopyEntryContents(DBENTRY *pfrom,DBENTRY *pto) pto->formpvt = NULL; } - + long epicsShareAPI dbPath(DBBASE *pdbbase,const char *path) { if(!pdbbase) return(-1); @@ -796,7 +797,7 @@ long epicsShareAPI dbAddPath(DBBASE *pdbbase,const char *path) const char *plast; unsigned expectingPath; unsigned sawMissingPath; - + if(!pdbbase) return(-1); ppathList = (ELLLIST *)pdbbase->pathPvt; if(!ppathList) { @@ -865,7 +866,7 @@ static long epicsShareAPI dbAddOnePath (DBBASE *pdbbase, const char *path, unsig { ELLLIST *ppathList; dbPathNode *pdbPathNode; - + if(!pdbbase) return(-1); ppathList = (ELLLIST *)pdbbase->pathPvt; @@ -877,7 +878,7 @@ static long epicsShareAPI dbAddOnePath (DBBASE *pdbbase, const char *path, unsig return 0; } - + long epicsShareAPI dbWriteRecord(DBBASE *ppdbbase,const char *filename, const char *precordTypename,int level) { @@ -974,7 +975,7 @@ long epicsShareAPI dbWriteRecordFP( dbFinishEntry(pdbentry); return(0); } - + long epicsShareAPI dbWriteMenu( DBBASE *ppdbbase,const char *filename,const char *menuName) { @@ -1017,7 +1018,7 @@ long epicsShareAPI dbWriteMenuFP(DBBASE *pdbbase,FILE *fp,const char *menuName) } return(0); } - + long epicsShareAPI dbWriteRecordType( DBBASE *pdbbase,const char *filename,const char *recordTypeName) { @@ -1114,7 +1115,7 @@ long epicsShareAPI dbWriteRecordTypeFP( } return(0); } - + long epicsShareAPI dbWriteDevice(DBBASE *pdbbase,const char *filename) { FILE *stream; @@ -1156,7 +1157,7 @@ long epicsShareAPI dbWriteDeviceFP(DBBASE *pdbbase,FILE *fp) } return(0); } - + long epicsShareAPI dbWriteDriver(DBBASE *pdbbase,const char *filename) { FILE *stream; @@ -1227,7 +1228,7 @@ long epicsShareAPI dbWriteVariableFP(DBBASE *pdbbase,FILE *fp) } return(0); } - + long epicsShareAPI dbWriteBreaktable(DBBASE *pdbbase,const char *filename) { FILE *stream; @@ -1262,7 +1263,7 @@ long epicsShareAPI dbWriteBreaktableFP(DBBASE *pdbbase,FILE *fp) } return(0); } - + long epicsShareAPI dbFindRecordType(DBENTRY *pdbentry,const char *recordType) { dbBase *pdbbase = pdbentry->pdbbase; @@ -1306,7 +1307,7 @@ int epicsShareAPI dbGetNRecordTypes(DBENTRY *pdbentry) { return(ellCount(&pdbentry->pdbbase->recordTypeList)); } - + long epicsShareAPI dbPutRecordAttribute( DBENTRY *pdbentry, const char *name, const char*value) { @@ -1382,7 +1383,7 @@ long epicsShareAPI dbGetRecordAttribute(DBENTRY *pdbentry, const char *pname) { return dbGetAttributePart(pdbentry, &pname); } - + long epicsShareAPI dbFirstField(DBENTRY *pdbentry,int dctonly) { @@ -1426,7 +1427,7 @@ long epicsShareAPI dbNextField(DBENTRY *pdbentry,int dctonly) indfield++; } } - + int epicsShareAPI dbGetFieldType(DBENTRY *pdbentry) { dbFldDes *pflddes = pdbentry->pflddes; @@ -1450,7 +1451,7 @@ int epicsShareAPI dbGetNFields(DBENTRY *pdbentry,int dctonly) n = 0; for(indfield=0; indfieldno_fields; indfield++) { pflddes = precordType->papFldDes[indfield]; - if(dctonly && (pflddes->field_type==DBF_DEVICE) + if(dctonly && (pflddes->field_type==DBF_DEVICE) && (ellCount(&precordType->devList)==0) ) continue; if(!dctonly || pflddes->promptgroup) n++; } @@ -1488,7 +1489,7 @@ int epicsShareAPI dbGetPromptGroup(DBENTRY *pdbentry) if(!pflddes) return(0); return(pflddes->promptgroup); } - + long epicsShareAPI dbCreateRecord(DBENTRY *pdbentry,const char *precordName) { dbRecordType *precordType = pdbentry->precordType; @@ -1535,7 +1536,7 @@ long epicsShareAPI dbCreateRecord(DBENTRY *pdbentry,const char *precordName) if(!ppvd) {errMessage(-1,"Logic Err: Could not add to PVD");return(-1);} return(0); } - + long epicsShareAPI dbDeleteAliases(DBENTRY *pdbentry) { dbBase *pdbbase = pdbentry->pdbbase; @@ -1653,7 +1654,7 @@ long epicsShareAPI dbFindRecord(DBENTRY *pdbentry, const char *pname) return dbFindField(pdbentry, ++pname); return 0; } - + long epicsShareAPI dbFirstRecord(DBENTRY *pdbentry) { dbRecordType *precordType = pdbentry->precordType; @@ -1706,7 +1707,7 @@ char * epicsShareAPI dbGetRecordName(DBENTRY *pdbentry) if(!precnode) return NULL; return precnode->recordname; } - + long epicsShareAPI dbRenameRecord(DBENTRY *pdbentry,const char *newName) { dbBase *pdbbase = pdbentry->pdbbase; @@ -1752,7 +1753,7 @@ long epicsShareAPI dbRenameRecord(DBENTRY *pdbentry,const char *newName) /*Leave pdbentry pointing to newly renamed record*/ return(dbFindRecord(pdbentry,newName)); } - + long epicsShareAPI dbVisibleRecord(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; @@ -1778,7 +1779,7 @@ int epicsShareAPI dbIsVisibleRecord(DBENTRY *pdbentry) if(!precnode) return 0; return precnode->flags & DBRN_FLAGS_VISIBLE ? 1 : 0; } - + long epicsShareAPI dbCreateAlias(DBENTRY *pdbentry, const char *alias) { dbRecordType *precordType = pdbentry->precordType; @@ -1828,7 +1829,7 @@ int epicsShareAPI dbIsAlias(DBENTRY *pdbentry) if(!precnode) return 0; return precnode->flags & DBRN_FLAGS_ISALIAS ? 1 : 0; } - + long epicsShareAPI dbCopyRecord(DBENTRY *pdbentry,const char *newRecordName,int overWriteOK) { dbRecordType *precordType = pdbentry->precordType; @@ -1882,7 +1883,7 @@ long epicsShareAPI dbCopyRecord(DBENTRY *pdbentry,const char *newRecordName,int /*Leave pdbentry pointing to newRecordName*/ return(dbFindRecord(pdbentry,newRecordName)); } - + long epicsShareAPI dbFindFieldPart(DBENTRY *pdbentry,const char **ppname) { dbRecordType *precordType = pdbentry->precordType; @@ -1967,7 +1968,7 @@ long epicsShareAPI dbFindField(DBENTRY *pdbentry,const char *pname) int epicsShareAPI dbFoundField(DBENTRY *pdbentry) { return((pdbentry->pfield) ? TRUE : FALSE); } - + char * epicsShareAPI dbGetString(DBENTRY *pdbentry) { dbFldDes *pflddes = pdbentry->pflddes; @@ -1997,7 +1998,7 @@ char * epicsShareAPI dbGetString(DBENTRY *pdbentry) case DBF_DEVICE: return(dbGetStringNum(pdbentry)); case DBF_INLINK: - case DBF_OUTLINK: + case DBF_OUTLINK: if(!pfield) {strcpy(message,"Field not found"); return(message);} plink = (DBLINK *)pfield; switch(plink->type) { @@ -2138,7 +2139,7 @@ char * epicsShareAPI dbGetString(DBENTRY *pdbentry) } return (message); } - + static void cvtDecimalOrHexToShort(char *from,short *value) { if(strspn(from,"0x")==2 || strspn(from,"0X")==2) { @@ -2264,7 +2265,7 @@ long epicsShareAPI dbPutString(DBENTRY *pdbentry,const char *pstring) goto done; } switch (plink->type) { - case CONSTANT: + case CONSTANT: case PV_LINK: { short ppOpt = 0; short msOpt = 0; @@ -2428,7 +2429,7 @@ long epicsShareAPI dbPutString(DBENTRY *pdbentry,const char *pstring) } break; case GPIB_IO: { - char *end; + char *end; if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); pstr = end + 1; @@ -2545,7 +2546,7 @@ done: } return(status); } - + char * epicsShareAPI dbVerify(DBENTRY *pdbentry,const char *pstring) { dbFldDes *pflddes = pdbentry->pflddes; @@ -2591,13 +2592,13 @@ char * epicsShareAPI dbVerify(DBENTRY *pdbentry,const char *pstring) return(message); } switch (pflddes->field_type) { - case DBF_CHAR : + case DBF_CHAR : if(value<-128 || value>127) { strcpy(message,"must have -128<=value<=127"); return(message); } return(NULL); - case DBF_SHORT : + case DBF_SHORT : if(value<-32768 || value>32767) { strcpy(message,"must have -32768<=value<=32767"); return(message); @@ -2626,14 +2627,14 @@ char * epicsShareAPI dbVerify(DBENTRY *pdbentry,const char *pstring) return(message); } switch (pflddes->field_type) { - case DBF_UCHAR : + case DBF_UCHAR : if(value>255) { strcpy(message,"must have 0<=value<=255"); return(message); } return(NULL); case DBF_ENUM: - case DBF_USHORT : + case DBF_USHORT : if(value>65535) { strcpy(message,"must have 0<=value<=65535"); return(message); @@ -2646,7 +2647,7 @@ char * epicsShareAPI dbVerify(DBENTRY *pdbentry,const char *pstring) } } case DBF_FLOAT: - case DBF_DOUBLE: { + case DBF_DOUBLE: { double value; char *endp; @@ -2661,7 +2662,7 @@ char * epicsShareAPI dbVerify(DBENTRY *pdbentry,const char *pstring) dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; char *pchoice; int i; - + if(!pdbMenu) return(NULL); for (i = 0; i < pdbMenu->nChoice; i++) { if(!(pchoice = pdbMenu->papChoiceValue[i])) continue; @@ -2699,7 +2700,7 @@ char * epicsShareAPI dbVerify(DBENTRY *pdbentry,const char *pstring) strcpy(message,"Not a valid field type"); return (message); } - + char *epicsShareAPI dbGetRange(DBENTRY *pdbentry) { dbFldDes *pflddes = pdbentry->pflddes; @@ -2729,14 +2730,14 @@ char *epicsShareAPI dbGetRange(DBENTRY *pdbentry) strcpy(message,"Not a valid field type"); return (message); } - + long epicsShareAPI dbFirstInfo(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; - + pdbentry->pinfonode = NULL; if (!precnode) return (S_dbLib_recNotFound); - + pdbentry->pinfonode = (dbInfoNode *)ellFirst(&precnode->infoList); return (pdbentry->pinfonode ? 0 : S_dbLib_infoNotFound); } @@ -2745,11 +2746,11 @@ long epicsShareAPI dbNextInfo(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; dbInfoNode *pinfo; - + if (!precnode) return (S_dbLib_recNotFound); pinfo = pdbentry->pinfonode; if (!pinfo) return (S_dbLib_infoNotFound); - + pinfo = (dbInfoNode *)ellNext(&pinfo->node); pdbentry->pinfonode = pinfo; return (pinfo ? 0 : S_dbLib_infoNotFound); @@ -2759,10 +2760,10 @@ long epicsShareAPI dbFindInfo(DBENTRY *pdbentry,const char *name) { dbRecordNode *precnode = pdbentry->precnode; dbInfoNode *pinfo; - + pdbentry->pinfonode = NULL; if (!precnode) return(S_dbLib_recNotFound); - + pinfo = (dbInfoNode *)ellFirst(&precnode->infoList); while (pinfo) { if (!strcmp(pinfo->name, name)) { @@ -2778,7 +2779,7 @@ long epicsShareAPI dbDeleteInfo(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; dbInfoNode *pinfo = pdbentry->pinfonode; - + if (!precnode) return (S_dbLib_recNotFound); if (!pinfo) return (S_dbLib_infoNotFound); ellDelete(&precnode->infoList,&pinfo->node); @@ -2841,11 +2842,11 @@ long epicsShareAPI dbPutInfo(DBENTRY *pdbentry,const char *name,const char *stri dbInfoNode *pinfo; dbRecordNode *precnode = pdbentry->precnode; if (!precnode) return (S_dbLib_recNotFound); - + dbFindInfo(pdbentry, name); pinfo = pdbentry->pinfonode; if (pinfo) return (dbPutInfoString(pdbentry, string)); - + /*Create new info node*/ pinfo = calloc(1,sizeof(dbInfoNode)); if (!pinfo) return (S_dbLib_outMem); @@ -2875,7 +2876,7 @@ brkTable * epicsShareAPI dbFindBrkTable(dbBase *pdbbase,const char *name) if(!pgph) return(NULL); return((brkTable *)pgph->userPvt); } - + dbMenu * epicsShareAPI dbFindMenu(dbBase *pdbbase,const char *name) { GPHENTRY *pgph; @@ -2933,7 +2934,7 @@ int epicsShareAPI dbGetNMenuChoices(DBENTRY *pdbentry) } return (-1); } - + char * epicsShareAPI dbGetMenuStringFromIndex(DBENTRY *pdbentry, int index) { dbFldDes *pflddes = pdbentry->pflddes; @@ -2996,13 +2997,13 @@ int epicsShareAPI dbGetMenuIndexFromString(DBENTRY *pdbentry, const char *choice } return (-1); } - + drvSup * epicsShareAPI dbFindDriver(dbBase *pdbbase, const char *name) { GPHENTRY *pgph = gphFind(pdbbase->pgpHash,name,&pdbbase->drvList); if (!pgph) return NULL; return (drvSup *) pgph->userPvt; } - + int epicsShareAPI dbAllocForm(DBENTRY *psave) { DBENTRY dbEntry; @@ -3076,7 +3077,7 @@ done: dbFinishEntry(pdbentry); return(nlines); } - + long epicsShareAPI dbFreeForm(DBENTRY *pdbentry) { if(pdbentry->formpvt) { @@ -3093,7 +3094,7 @@ char ** epicsShareAPI dbGetFormPrompt(DBENTRY *pdbentry) if(!pform) return(NULL); return(pform->prompt); } - + char ** epicsShareAPI dbGetFormValue(DBENTRY *pdbentry) { struct form *pform = pdbentry->formpvt; @@ -3105,7 +3106,7 @@ char ** epicsShareAPI dbGetFormValue(DBENTRY *pdbentry) if(!plink) return(NULL); value = pform->value; switch(pform->linkType) { - case FORM_CONSTANT: + case FORM_CONSTANT: if(plink->value.constantStr) { strcpy(*value,plink->value.constantStr); } else { @@ -3263,7 +3264,7 @@ char ** epicsShareAPI dbGetFormValue(DBENTRY *pdbentry) } return(pform->value); } - + long epicsShareAPI dbPutForm(DBENTRY *pdbentry,char **value) { struct form *pform = pdbentry->formpvt; @@ -3279,7 +3280,7 @@ long epicsShareAPI dbPutForm(DBENTRY *pdbentry,char **value) if(!plink) return(S_dbLib_badLink); verify = pform->verify; switch(pform->linkType) { - case FORM_CONSTANT: + case FORM_CONSTANT: **verify = 0; /*Initialize to no error*/ if(**value == '\0') break; dvalue = epicsStrtod(*value,&endp); @@ -3580,7 +3581,7 @@ long epicsShareAPI dbPutForm(DBENTRY *pdbentry,char **value) **verify = 0; break; case FORM_VXI_IO: - plink->value.vxiio.flag = + plink->value.vxiio.flag = ((strchr(*value,'Y')||strchr(*value,'y') ? VXIDYNAMIC : VXISTATIC)); value++; verify++; lvalue = strtol(*value,&endp,0); @@ -3619,7 +3620,7 @@ long epicsShareAPI dbPutForm(DBENTRY *pdbentry,char **value) } return(status); } - + char ** epicsShareAPI dbVerifyForm(DBENTRY *pdbentry,char **value) { struct form *pform = pdbentry->formpvt; @@ -3664,7 +3665,7 @@ char * epicsShareAPI dbGetRelatedField(DBENTRY *psave) dbFinishEntry(pdbentry); return(rtnval); } - + int epicsShareAPI dbGetNLinks(DBENTRY *pdbentry) { dbRecordType *precordType = pdbentry->precordType; @@ -3715,7 +3716,7 @@ int epicsShareAPI dbGetLinkType(DBENTRY *pdbentry) } return(-1); } - + long epicsShareAPI dbCvtLinkToConstant(DBENTRY *pdbentry) { dbFldDes *pflddes; @@ -3779,7 +3780,7 @@ long epicsShareAPI dbCvtLinkToPvlink(DBENTRY *pdbentry) } return(S_dbLib_badLink); } - + void epicsShareAPI dbDumpPath(DBBASE *pdbbase) { ELLLIST *ppathList; @@ -3821,7 +3822,7 @@ void epicsShareAPI dbDumpMenu(dbBase *pdbbase,const char *menuName) } dbWriteMenuFP(pdbbase,stdout,menuName); } - + void epicsShareAPI dbDumpRecordType(DBBASE *pdbbase,const char *recordTypeName) { dbRecordType *pdbRecordType; @@ -3863,7 +3864,7 @@ void epicsShareAPI dbDumpRecordType(DBBASE *pdbbase,const char *recordTypeName) if(recordTypeName) break; } } - + void epicsShareAPI dbDumpField( DBBASE *pdbbase,const char *recordTypeName,const char *fname) { @@ -3955,7 +3956,7 @@ void epicsShareAPI dbDumpField( if(recordTypeName) break; } } - + void epicsShareAPI dbDumpDevice(DBBASE *pdbbase,const char *recordTypeName) { dbRecordType *pdbRecordType; @@ -4051,7 +4052,7 @@ void epicsShareAPI dbDumpBreaktable(DBBASE *pdbbase,const char *name) } return; } - + static char *bus[VXI_IO+1] = {"","","VME","CAMAC","AB", "GPIB","BITBUS","","","","","","INST","BBGPIB","VXI"}; void epicsShareAPI dbReportDeviceConfig(dbBase *pdbbase,FILE *report) diff --git a/src/ioc/dbStatic/link.h b/src/ioc/dbStatic/link.h index c9818f6f0..ef3cfc69d 100644 --- a/src/ioc/dbStatic/link.h +++ b/src/ioc/dbStatic/link.h @@ -1,27 +1,24 @@ /*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* link.h */ -/* base/include $Revision-Id$ */ +/* $Id$ */ /* - * Original Author: Bob Dalesio - * Current Author: Marty Kraimer - * Date: 6-1-90 + * Original Authors: Bob Dalesio, Marty Kraimer */ +#ifndef INC_link_H +#define INC_link_H + #include "dbDefs.h" #include "shareLib.h" -#ifndef INClinkh -#define INClinkh 1 - #ifdef __cplusplus extern "C" { #endif @@ -55,8 +52,8 @@ epicsShareExtern maplinkType pamaplinkType[]; /* structure of a PV_LINK DB_LINK and a CA_LINK */ /*Options defined by pvlMask */ #define pvlOptMsMode 0x3 /*Maximize Severity mode selection*/ -#define pvlOptNMS 0 /*Dont Maximize Severity*/ -#define pvlOptMS 1 /*Maximize Severity allways*/ +#define pvlOptNMS 0 /*Don't Maximize Severity*/ +#define pvlOptMS 1 /*Maximize Severity always*/ #define pvlOptMSI 2 /*Maximize Severity if INVALID*/ #define pvlOptMSS 3 /*Maximize Severity and copy Status*/ #define pvlOptPP 0x4 /*Process Passive*/ @@ -77,14 +74,15 @@ struct macro_link { }; struct dbCommon; +struct pvlet; struct pv_link { - char *pvname; /*pvname to link to*/ - struct dbCommon *precord; /*Address of record containing link*/ - void *pvt; /*CA or DB private*/ - LINKCVT getCvt; /*input conversion function*/ - short pvlMask; /*Options mask*/ - short lastGetdbrType; /*last dbrType for DB or CA get*/ + char *pvname; /* pvname link points to */ + struct dbCommon *precord; /* Address of record owning link */ + void *pvt; /* CA or DB private */ + LINKCVT getCvt; /* input conversion function */ + short pvlMask; /* Options mask */ + short lastGetdbrType; /* last dbrType for DB or CA get */ }; /* structure of a VME io channel */ @@ -150,13 +148,12 @@ struct bbgpibio { /* structure of an instrument io link */ struct instio { - char *string; /* the cat of location. - signal.parameter */ + char *string; }; /* structure of a vxi link */ struct vxiio{ - short flag; /* 0 = frame/slot, 1 = SA */ + short flag; /* 0 = frame/slot, 1 = SA */ short frame; short slot; short la; /* logical address if flag =1 */ @@ -165,7 +162,7 @@ struct vxiio{ }; /* union of possible address structures */ -union value{ +union value { char *constantStr; /*constant string*/ struct macro_link macro_link; /* link containing macro substitution*/ struct pv_link pv_link; /* link to process variable*/ @@ -180,9 +177,9 @@ union value{ struct vxiio vxiio; /* vxi io */ }; -struct link{ - union value value; - short type; +struct link { + union value value; + short type; char *text; /* original INP/OUT link text */ }; @@ -191,4 +188,4 @@ typedef struct link DBLINK; #ifdef __cplusplus } #endif -#endif /*INClinkh*/ +#endif /* INC_link_H */ diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 9f9c1a385..18b0c6cd5 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -41,7 +41,6 @@ #include "taskwd.h" #include "callback.h" #include "dbCommon.h" -#include "dbLock.h" #include "devSup.h" #include "drvSup.h" #include "menuConvert.h" @@ -59,6 +58,7 @@ #include "initHooks.h" #include "epicsExit.h" #include "epicsSignal.h" +#include "dbChannel.h" #define epicsExportSharedSymbols #include "epicsRelease.h" @@ -426,48 +426,18 @@ static void doInitRecord0(dbRecordType *pdbRecordType, dbCommon *precord, static void doResolveLinks(dbRecordType *pdbRecordType, dbCommon *precord, void *user) { + dbFldDes **papFldDes = pdbRecordType->papFldDes; + short *link_ind = pdbRecordType->link_ind; devSup *pdevSup; int j; - /* Convert all PV_LINKs to DB_LINKs or CA_LINKs */ /* For all the links in the record type... */ for (j = 0; j < pdbRecordType->no_links; j++) { - dbFldDes *pdbFldDes = - pdbRecordType->papFldDes[pdbRecordType->link_ind[j]]; + dbFldDes *pdbFldDes = papFldDes[link_ind[j]]; DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset); - if (plink->type == PV_LINK) { - DBADDR dbaddr; - - if (plink == &precord->tsel) recGblTSELwasModified(plink); - if (!(plink->value.pv_link.pvlMask&(pvlOptCA|pvlOptCP|pvlOptCPP)) - && (dbNameToAddr(plink->value.pv_link.pvname,&dbaddr)==0)) { - DBADDR *pdbAddr; - - plink->type = DB_LINK; - pdbAddr = dbCalloc(1,sizeof(struct dbAddr)); - *pdbAddr = dbaddr; /*structure copy*/; - plink->value.pv_link.pvt = pdbAddr; - } else {/*It is a CA link*/ - - if (pdbFldDes->field_type == DBF_INLINK) { - plink->value.pv_link.pvlMask |= pvlOptInpNative; - } - dbCaAddLink(plink); - if (pdbFldDes->field_type == DBF_FWDLINK) { - char *pperiod = - strrchr(plink->value.pv_link.pvname,'.'); - - if (pperiod && strstr(pperiod,"PROC")) { - plink->value.pv_link.pvlMask |= pvlOptFWD; - } else { - errlogPrintf("%s.FLNK is a Channel Access Link " - " but does not link to a PROC field\n", - precord->name); - } - } - } - } + if (plink->type == PV_LINK) + dbInitLink(precord, plink, pdbFldDes->field_type); } pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp); if (pdevSup) { @@ -491,6 +461,7 @@ static void doInitRecord1(dbRecordType *pdbRecordType, dbCommon *precord, static void initDatabase(void) { + dbChannelInit(); iterateRecords(doInitRecord0, NULL); iterateRecords(doResolveLinks, NULL); iterateRecords(doInitRecord1, NULL); diff --git a/src/ioc/registry/registerRecordDeviceDriver.pl b/src/ioc/registry/registerRecordDeviceDriver.pl index 3adda373a..5ab5c14d3 100755 --- a/src/ioc/registry/registerRecordDeviceDriver.pl +++ b/src/ioc/registry/registerRecordDeviceDriver.pl @@ -18,12 +18,18 @@ use DBD; use DBD::Parser; use EPICS::Readfile; use EPICS::Path; +use EPICS::Getopts; use Text::Wrap; +getopts('I@') or + die "Usage: registerRecordDeviceDriver [-I dir] in.dbd subroutinename [TOP]"; + +my @path = map { split /[:;]/ } @EPICS::Getopts::opt_I; # FIXME: Broken on Win32? + my ($file, $subname, $bldTop) = @ARGV; my $dbd = DBD->new(); -&ParseDBD($dbd, &Readfile($file)); +&ParseDBD($dbd, &Readfile($file, "", \@path)); $Text::Wrap::columns = 75; diff --git a/src/ioc/rsrv/camessage.c b/src/ioc/rsrv/camessage.c index 09015f667..c52d18347 100644 --- a/src/ioc/rsrv/camessage.c +++ b/src/ioc/rsrv/camessage.c @@ -1,15 +1,19 @@ /*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. * Copyright (c) 2002 The University of Chicago, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos 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. \*************************************************************************/ /* - * Author: Jeffrey O. Hill + * Author: Jeffrey O. Hill * + * Ralph Lange */ #include @@ -45,7 +49,7 @@ #include "rsrv.h" #include "server.h" -#define RECORD_NAME(PADDR) ((PADDR)->precord->name) +#define RECORD_NAME(CHAN) (dbChannelRecord(CHAN)->name) static EVENTFUNC read_reply; @@ -60,9 +64,9 @@ typedef struct rsrv_put_notify { putNotify dbPutNotify; caHdrLargeArray msg; /* - * Include a union of all scalar types + * Include a union of all scalar types * including fixed length strings so - * that in many cases we can avoid + * that in many cases we can avoid * allocating another buffer and only * use an rsrv_put_notify from its * free list. @@ -173,8 +177,8 @@ va_list args /* * allocate plenty of space for a sprintf() buffer */ - localStatus = cas_copy_in_header ( client, - CA_PROTO_ERROR, maxDiagLen, 0, 0, cid, status, + localStatus = cas_copy_in_header ( client, + CA_PROTO_ERROR, maxDiagLen, 0, 0, cid, status, ( void * ) &pReqOut ); if ( localStatus != ECA_NORMAL ) { errlogPrintf ( "caserver: Unable to deliver err msg \"%s\" to client because \"%s\"\n", @@ -187,7 +191,7 @@ va_list args * copy back the request protocol * (in network byte order) */ - if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) && + if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) && CA_V49( client->minor_version_number ) ) { ca_uint32_t *pLW = ( ca_uint32_t * ) ( pReqOut + 1 ); pReqOut->m_cmmd = htons ( curp->m_cmmd ); @@ -222,7 +226,7 @@ va_list args size += (ca_uint32_t) (diagLen + 1u); } else { - errlogPrintf ( + errlogPrintf ( "caserver: vsend_err: epicsVsnprintf detected " "error message truncation, pFormat = \"%s\"\n", pformat ); @@ -282,9 +286,9 @@ static void log_header ( "CAS: Request from %s => cmmd=%d cid=0x%x type=%d count=%d postsize=%u\n", hostName, mp->m_cmmd, mp->m_cid, mp->m_dataType, mp->m_count, mp->m_postsize); - epicsPrintf ( -"CAS: Request from %s => available=0x%x \tN=%u paddr=%p\n", - hostName, mp->m_available, mnum, (pciu?(void *)&pciu->addr:NULL)); + epicsPrintf ( +"CAS: Request from %s => available=0x%x \tN=%u dbch=%p\n", + hostName, mp->m_available, mnum, (pciu?(void *)&pciu->dbch:NULL)); if (mp->m_cmmd==CA_PROTO_WRITE && mp->m_dataType==DBF_STRING && pPayLoad ) { epicsPrintf ( @@ -297,7 +301,7 @@ static void log_header ( * logBadIdWithFileAndLineno() */ static void logBadIdWithFileAndLineno( -struct client *client, +struct client *client, caHdrLargeArray *mp, const void *pPayload, char *pFileName, @@ -314,10 +318,10 @@ unsigned lineno /* * bad_udp_cmd_action() */ -static int bad_udp_cmd_action ( caHdrLargeArray *mp, +static int bad_udp_cmd_action ( caHdrLargeArray *mp, void *pPayload, struct client *pClient ) { - log_header ("invalid (damaged?) request code from UDP", + log_header ("invalid (damaged?) request code from UDP", pClient, mp, pPayload, 0); return RSRV_ERROR; } @@ -325,13 +329,13 @@ static int bad_udp_cmd_action ( caHdrLargeArray *mp, /* * udp_echo_action() */ -static int udp_echo_action ( caHdrLargeArray *mp, +static int udp_echo_action ( caHdrLargeArray *mp, void *pPayload, struct client *pClient ) { char *pPayloadOut; int status; SEND_LOCK ( pClient ); - status = cas_copy_in_header ( pClient, mp->m_cmmd, mp->m_postsize, + status = cas_copy_in_header ( pClient, mp->m_cmmd, mp->m_postsize, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, ( void * ) &pPayloadOut ); if ( status == ECA_NORMAL ) { @@ -345,13 +349,13 @@ static int udp_echo_action ( caHdrLargeArray *mp, /* * bad_tcp_cmd_action() */ -static int bad_tcp_cmd_action ( caHdrLargeArray *mp, void *pPayload, +static int bad_tcp_cmd_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { const char *pCtx = "invalid (damaged?) request code from TCP"; log_header ( pCtx, client, mp, pPayload, 0 ); - /* + /* * by default, clients dont recover * from this */ @@ -365,7 +369,7 @@ static int bad_tcp_cmd_action ( caHdrLargeArray *mp, void *pPayload, /* * tcp_version_action() */ -static int tcp_version_action ( caHdrLargeArray *mp, void *pPayload, +static int tcp_version_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { double tmp; @@ -406,13 +410,13 @@ static int tcp_version_action ( caHdrLargeArray *mp, void *pPayload, /* * tcp_echo_action() */ -static int tcp_echo_action ( caHdrLargeArray *mp, +static int tcp_echo_action ( caHdrLargeArray *mp, void *pPayload, struct client *pClient ) { char *pPayloadOut; int status; SEND_LOCK ( pClient ); - status = cas_copy_in_header ( pClient, mp->m_cmmd, mp->m_postsize, + status = cas_copy_in_header ( pClient, mp->m_cmmd, mp->m_postsize, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, ( void * ) &pPayloadOut ); if ( status == ECA_NORMAL ) { @@ -426,7 +430,7 @@ static int tcp_echo_action ( caHdrLargeArray *mp, /* * events_on_action () */ -static int events_on_action ( caHdrLargeArray *mp, +static int events_on_action ( caHdrLargeArray *mp, void *pPayload, struct client *pClient ) { db_event_flow_ctrl_mode_off ( pClient->evuser ); @@ -436,7 +440,7 @@ static int events_on_action ( caHdrLargeArray *mp, /* * events_off_action () */ -static int events_off_action ( caHdrLargeArray *mp, +static int events_off_action ( caHdrLargeArray *mp, void *pPayload, struct client *pClient ) { db_event_flow_ctrl_mode_on ( pClient->evuser ); @@ -462,8 +466,8 @@ static void no_read_access_event ( struct client *pClient, * on failure to pre v41 clients */ if ( ! CA_V41 ( pClient->minor_version_number ) ) { - send_err ( &pevext->msg, ECA_GETFAIL, pClient, - RECORD_NAME ( &pevext->pciu->addr ) ); + send_err ( &pevext->msg, ECA_GETFAIL, pClient, + RECORD_NAME ( pevext->pciu->dbch ) ); return; } @@ -473,30 +477,30 @@ static void no_read_access_event ( struct client *pClient, * event/put/get callback. * * Fetched value is zerod in case they - * use it even when the status indicates + * use it even when the status indicates * failure. * * The m_cid field in the protocol * header is abused to carry the status */ - status = cas_copy_in_header ( pClient, pevext->msg.m_cmmd, pevext->size, - pevext->msg.m_dataType, pevext->msg.m_count, ECA_NORDACCESS, + status = cas_copy_in_header ( pClient, pevext->msg.m_cmmd, pevext->size, + pevext->msg.m_dataType, pevext->msg.m_count, ECA_NORDACCESS, pevext->msg.m_available, ( void * ) &pPayloadOut ); if ( status == ECA_NORMAL ) { memset ( pPayloadOut, 0, pevext->size ); cas_commit_msg ( pClient, pevext->size ); } else { - send_err ( &pevext->msg, status, pClient, + send_err ( &pevext->msg, status, pClient, "server unable to load read access denied response into protocol buffer PV=\"%s max bytes=%u\"", - RECORD_NAME ( &pevext->pciu->addr ), rsrvSizeofLargeBufTCP ); + RECORD_NAME ( pevext->pciu->dbch ), rsrvSizeofLargeBufTCP ); } } /* * read_reply() */ -static void read_reply ( void *pArg, struct dbAddr *paddr, +static void read_reply ( void *pArg, struct dbChannel *dbch, int eventsRemaining, db_field_log *pfl ) { ca_uint32_t cid; @@ -508,12 +512,14 @@ static void read_reply ( void *pArg, struct dbAddr *paddr, int status; int v41; int autosize; + int local_fl = 0; long item_count; ca_uint32_t payload_size; + dbAddr *paddr=&dbch->addr; SEND_LOCK ( pClient ); - /* + /* * New clients recv the status of the * operation directly to the * event/put/get callback. @@ -543,10 +549,10 @@ static void read_reply ( void *pArg, struct dbAddr *paddr, pevext->msg.m_dataType, item_count, cid, pevext->msg.m_available, &pPayload ); if ( status != ECA_NORMAL ) { - send_err ( &pevext->msg, status, pClient, + send_err ( &pevext->msg, status, pClient, "server unable to load read (or subscription update) response " "into protocol buffer PV=\"%s\" max bytes=%u", - RECORD_NAME ( paddr ), rsrvSizeofLargeBufTCP ); + RECORD_NAME ( dbch ), rsrvSizeofLargeBufTCP ); if ( ! eventsRemaining ) cas_send_bs_msg ( pClient, FALSE ); SEND_UNLOCK ( pClient ); @@ -564,19 +570,32 @@ static void read_reply ( void *pArg, struct dbAddr *paddr, return; } - status = db_get_field_and_count( - paddr, pevext->msg.m_dataType, pPayload, &item_count, pfl); + /* If filters are involved in a read, create field log and run filters */ + if (!pfl && (ellCount(&dbch->pre_chain) || ellCount(&dbch->post_chain))) { + pfl = db_create_read_log(dbch); + if (pfl) { + local_fl = 1; + pfl = dbChannelRunPreChain(dbch, pfl); + pfl = dbChannelRunPostChain(dbch, pfl); + } + } + + status = dbChannel_get_count ( dbch, pevext->msg.m_dataType, + pPayload, &item_count, pfl); + + if (local_fl) db_delete_field_log(pfl); + if ( status < 0 ) { /* * I cant wait to redesign this protocol from scratch! */ if ( ! v41 ) { /* - * old client & plain get + * old client & plain get * continue to return an exception * on failure */ - send_err ( &pevext->msg, ECA_GETFAIL, pClient, RECORD_NAME ( paddr ) ); + send_err ( &pevext->msg, ECA_GETFAIL, pClient, RECORD_NAME ( dbch ) ); } else { /* New clients recv the status of the operation directly to the @@ -598,8 +617,8 @@ static void read_reply ( void *pArg, struct dbAddr *paddr, } } else { - int cacStatus = caNetConvert ( - pevext->msg.m_dataType, pPayload, pPayload, + int cacStatus = caNetConvert ( + pevext->msg.m_dataType, pPayload, pPayload, TRUE /* host -> net format */, item_count ); if ( cacStatus == ECA_NORMAL ) { ca_uint32_t data_size = @@ -646,6 +665,8 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p void *pPayload; int status; int v41; + int local_fl = 0; + db_field_log *pfl = NULL; if ( ! pciu ) { logBadId ( pClient, mp, 0 ); @@ -655,18 +676,18 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p SEND_LOCK ( pClient ); if ( INVALID_DB_REQ ( mp->m_dataType ) ) { - send_err ( mp, ECA_BADTYPE, pClient, RECORD_NAME ( &pciu->addr ) ); + send_err ( mp, ECA_BADTYPE, pClient, RECORD_NAME ( pciu->dbch ) ); SEND_UNLOCK ( pClient ); return RSRV_ERROR; } payloadSize = dbr_size_n ( mp->m_dataType, mp->m_count ); - status = cas_copy_in_header ( pClient, mp->m_cmmd, payloadSize, + status = cas_copy_in_header ( pClient, mp->m_cmmd, payloadSize, mp->m_dataType, mp->m_count, pciu->cid, mp->m_available, &pPayload ); if ( status != ECA_NORMAL ) { - send_err ( mp, status, pClient, + send_err ( mp, status, pClient, "server unable to load read response into protocol buffer PV=\"%s\" max bytes=%u", - RECORD_NAME ( &pciu->addr ), rsrvSizeofLargeBufTCP ); + RECORD_NAME ( pciu->dbch ), rsrvSizeofLargeBufTCP ); SEND_UNLOCK ( pClient ); return RSRV_OK; } @@ -682,25 +703,38 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p else{ status = ECA_GETFAIL; } - send_err ( mp, status, - pClient, RECORD_NAME ( &pciu->addr ) ); + send_err ( mp, status, + pClient, RECORD_NAME ( pciu->dbch ) ); SEND_UNLOCK ( pClient ); return RSRV_OK; } - status = db_get_field ( &pciu->addr, mp->m_dataType, - pPayload, mp->m_count, 0 ); + /* If filters are involved in a read, create field log and run filters */ + if (ellCount(&pciu->dbch->pre_chain) || ellCount(&pciu->dbch->post_chain)) { + pfl = db_create_read_log(pciu->dbch); + if (pfl) { + local_fl = 1; + pfl = dbChannelRunPreChain(pciu->dbch, pfl); + pfl = dbChannelRunPostChain(pciu->dbch, pfl); + } + } + + status = dbChannel_get ( pciu->dbch, mp->m_dataType, + pPayload, mp->m_count, pfl ); + + if (local_fl) db_delete_field_log(pfl); + if ( status < 0 ) { - send_err ( mp, ECA_GETFAIL, pClient, RECORD_NAME ( &pciu->addr ) ); + send_err ( mp, ECA_GETFAIL, pClient, RECORD_NAME ( pciu->dbch ) ); SEND_UNLOCK ( pClient ); return RSRV_OK; } - status = caNetConvert ( - mp->m_dataType, pPayload, pPayload, + status = caNetConvert ( + mp->m_dataType, pPayload, pPayload, TRUE /* host -> net format */, mp->m_count ); if ( status != ECA_NORMAL ) { - send_err ( mp, status, pClient, RECORD_NAME ( &pciu->addr ) ); + send_err ( mp, status, pClient, RECORD_NAME ( pciu->dbch ) ); SEND_UNLOCK ( pClient ); return RSRV_OK; } @@ -717,7 +751,7 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p } else { pStr[payloadSize-1] = '\0'; - errlogPrintf ( + errlogPrintf ( "caserver: read_action: detected DBR_STRING w/o nill termination " "in response from db_get_field, pPayload = \"%s\"\n", pStr ); @@ -754,11 +788,11 @@ static int read_notify_action ( caHdrLargeArray *mp, void *pPayload, struct clie * favor of the standard db event calling * mechanism- routine(userarg, paddr). See * events added above. - * + * * Hold argument set true so the send message * buffer is not flushed once each call. */ - read_reply ( &evext, &pciu->addr, TRUE, NULL ); + read_reply ( &evext, pciu->dbch, TRUE, NULL ); return RSRV_OK; } @@ -766,7 +800,7 @@ static int read_notify_action ( caHdrLargeArray *mp, void *pPayload, struct clie /* * write_action() */ -static int write_action ( caHdrLargeArray *mp, +static int write_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { struct channel_in_use *pciu; @@ -791,25 +825,25 @@ static int write_action ( caHdrLargeArray *mp, } SEND_LOCK(client); send_err( - mp, - status, - client, - RECORD_NAME(&pciu->addr)); + mp, + status, + client, + RECORD_NAME ( pciu->dbch )); SEND_UNLOCK(client); return RSRV_OK; } - status = caNetConvert ( - mp->m_dataType, pPayload, pPayload, + status = caNetConvert ( + mp->m_dataType, pPayload, pPayload, FALSE /* net -> host format */, mp->m_count ); if ( status != ECA_NORMAL ) { log_header ("invalid data type", client, mp, pPayload, 0); SEND_LOCK(client); send_err( - mp, - status, - client, - RECORD_NAME(&pciu->addr)); + mp, + status, + client, + RECORD_NAME ( pciu->dbch )); SEND_UNLOCK(client); return RSRV_ERROR; } @@ -817,10 +851,10 @@ static int write_action ( caHdrLargeArray *mp, asWritePvt = asTrapWriteBefore ( pciu->asClientPVT, pciu->client->pUserName ? pciu->client->pUserName : "", pciu->client->pHostName ? pciu->client->pHostName : "", - (void *) &pciu->addr ); + pciu->dbch ); - dbStatus = db_put_field( - &pciu->addr, + dbStatus = dbChannel_put( + pciu->dbch, mp->m_dataType, pPayload, mp->m_count); @@ -830,10 +864,10 @@ static int write_action ( caHdrLargeArray *mp, if (dbStatus < 0) { SEND_LOCK(client); send_err( - mp, - ECA_PUTFAIL, - client, - RECORD_NAME(&pciu->addr)); + mp, + ECA_PUTFAIL, + client, + RECORD_NAME ( pciu->dbch )); SEND_UNLOCK(client); } @@ -852,8 +886,8 @@ static int host_name_action ( caHdrLargeArray *mp, void *pPayload, int chanCount; epicsMutexMustLock ( client->chanListLock ); - chanCount = - ellCount ( &client->chanList ) + + chanCount = + ellCount ( &client->chanList ) + ellCount ( &client->chanPendingUpdateARList ); epicsMutexUnlock( client->chanListLock ); @@ -872,13 +906,13 @@ static int host_name_action ( caHdrLargeArray *mp, void *pPayload, pName = (char *) pPayload; size = strlen(pName)+1; if (size > 512) { - log_header ( "bad (very long) host name", + log_header ( "bad (very long) host name", client, mp, pPayload, 0 ); SEND_LOCK(client); send_err( - mp, - ECA_INTERNAL, - client, + mp, + ECA_INTERNAL, + client, "bad (very long) host name"); SEND_UNLOCK(client); return RSRV_ERROR; @@ -889,7 +923,7 @@ static int host_name_action ( caHdrLargeArray *mp, void *pPayload, */ pMalloc = malloc(size); if(!pMalloc){ - log_header ( "no space in pool for new host name", + log_header ( "no space in pool for new host name", client, mp, pPayload, 0 ); SEND_LOCK(client); send_err( @@ -901,8 +935,8 @@ static int host_name_action ( caHdrLargeArray *mp, void *pPayload, return RSRV_ERROR; } strncpy( - pMalloc, - pName, + pMalloc, + pName, size-1); pMalloc[size-1]='\0'; @@ -912,13 +946,13 @@ static int host_name_action ( caHdrLargeArray *mp, void *pPayload, free ( pName ); } - DLOG (2, ( "CAS: host_name_action for \"%s\"\n", + DLOG (2, ( "CAS: host_name_action for \"%s\"\n", client->pHostName ? client->pHostName : "" ) ); return RSRV_OK; } - + /* * client_name_action() */ @@ -931,8 +965,8 @@ static int client_name_action ( caHdrLargeArray *mp, void *pPayload, int chanCount; epicsMutexMustLock ( client->chanListLock ); - chanCount = - ellCount ( &client->chanList ) + + chanCount = + ellCount ( &client->chanList ) + ellCount ( &client->chanPendingUpdateARList ); epicsMutexUnlock( client->chanListLock ); @@ -951,13 +985,13 @@ static int client_name_action ( caHdrLargeArray *mp, void *pPayload, pName = (char *) pPayload; size = strlen(pName)+1; if (size > 512) { - log_header ("a very long user name was specified", + log_header ("a very long user name was specified", client, mp, pPayload, 0); SEND_LOCK(client); send_err( - mp, - ECA_INTERNAL, - client, + mp, + ECA_INTERNAL, + client, "a very long user name was specified"); SEND_UNLOCK(client); return RSRV_ERROR; @@ -968,7 +1002,7 @@ static int client_name_action ( caHdrLargeArray *mp, void *pPayload, */ pMalloc = malloc(size); if(!pMalloc){ - log_header ("no memory for new user name", + log_header ("no memory for new user name", client, mp, pPayload, 0); SEND_LOCK(client); send_err( @@ -980,8 +1014,8 @@ static int client_name_action ( caHdrLargeArray *mp, void *pPayload, return RSRV_ERROR; } strncpy( - pMalloc, - pName, + pMalloc, + pName, size-1); pMalloc[size-1]='\0'; @@ -999,7 +1033,7 @@ static int client_name_action ( caHdrLargeArray *mp, void *pPayload, */ static struct channel_in_use *casCreateChannel ( struct client *client, -struct dbAddr *pAddr, +struct dbChannel *dbch, unsigned cid ) { @@ -1009,14 +1043,14 @@ unsigned cid int status; /* get block off free list if possible */ - pchannel = (struct channel_in_use *) + pchannel = (struct channel_in_use *) freeListCalloc(rsrvChanFreeList); if (!pchannel) { return NULL; } ellInit(&pchannel->eventq); epicsTimeGetCurrent(&pchannel->time_at_creation); - pchannel->addr = *pAddr; + pchannel->dbch = dbch; pchannel->client = client; /* * bypass read only warning @@ -1046,8 +1080,8 @@ unsigned cid * Verify that this id is not in use */ status = bucketAddItemUnsignedId ( - pCaBucket, - &pchannel->sid, + pCaBucket, + &pchannel->sid, pchannel); } while (status == S_bucket_idInUse); @@ -1116,7 +1150,7 @@ static void casAccessRightsCB(ASCLIENTPVT ascpvt, asClientStatus type) epicsMutexUnlock ( pclient->chanListLock ); /* - * Update all event call backs + * Update all event call backs */ epicsMutexMustLock(pclient->eventqLock); for (pevext = (struct event_ext *) ellFirst(&pciu->eventq); @@ -1175,8 +1209,8 @@ static void access_rights_reply ( struct channel_in_use * pciu ) } SEND_LOCK ( pciu->client ); - status = cas_copy_in_header ( - pciu->client, CA_PROTO_ACCESS_RIGHTS, 0, + status = cas_copy_in_header ( + pciu->client, CA_PROTO_ACCESS_RIGHTS, 0, 0, 0, pciu->cid, ar, 0 ); /* * OK to just ignore the request if the connection drops @@ -1197,26 +1231,28 @@ static void claim_ciu_reply ( struct channel_in_use * pciu ) if ( v42 ) { int status; ca_uint32_t nElem; + long dbElem; SEND_LOCK ( pciu->client ); - if ( pciu->addr.no_elements < 0 ) { + dbElem = dbChannelFinalElements(pciu->dbch); + if ( dbElem < 0 ) { nElem = 0; } else { if ( ! CA_V49 ( pciu->client->minor_version_number ) ) { - if ( pciu->addr.no_elements >= 0xffff ) { + if ( dbElem >= 0xffff ) { nElem = 0xfffe; } else { - nElem = (ca_uint32_t) pciu->addr.no_elements; + nElem = (ca_uint32_t) dbElem; } } else { - nElem = (ca_uint32_t) pciu->addr.no_elements; + nElem = (ca_uint32_t) dbElem; } } - status = cas_copy_in_header ( + status = cas_copy_in_header ( pciu->client, CA_PROTO_CREATE_CHAN, 0u, - pciu->addr.dbr_field_type, nElem, pciu->cid, + dbChannelFinalExportType(pciu->dbch), nElem, pciu->cid, pciu->sid, NULL ); if ( status == ECA_NORMAL ) { cas_commit_msg ( pciu->client, 0u ); @@ -1228,7 +1264,7 @@ static void claim_ciu_reply ( struct channel_in_use * pciu ) /* * claim_ciu_action() */ -static int claim_ciu_action ( caHdrLargeArray *mp, +static int claim_ciu_action ( caHdrLargeArray *mp, void *pPayload, client *client ) { int status; @@ -1243,47 +1279,48 @@ static int claim_ciu_action ( caHdrLargeArray *mp, client->minor_version_number = mp->m_available; if (CA_V44(client->minor_version_number)) { - struct dbAddr tmp_addr; + struct dbChannel *dbch; char *pName = (char *) pPayload; /* * check the sanity of the message */ if (mp->m_postsize<=1) { - log_header ( "empty PV name in UDP search request?", + log_header ( "empty PV name in UDP search request?", client, mp, pPayload, 0 ); return RSRV_OK; } pName[mp->m_postsize-1] = '\0'; - status = db_name_to_addr (pName, &tmp_addr); - if (status) { + dbch = dbChannel_create (pName); + if (!dbch) { return RSRV_OK; } - DLOG ( 2, ("CAS: claim_ciu_action found '%s', type %d, count %d\n", - pName, tmp_addr.dbr_field_type, tmp_addr.no_elements) ); - + DLOG ( 2, ("CAS: claim_ciu_action found '%s', type %d, count %d\n", + pName, dbChannelExportType(dbch), dbChannelElements(dbch)) ); + pciu = casCreateChannel ( - client, - &tmp_addr, + client, + dbch, mp->m_cid); if (!pciu) { - log_header ("no memory to create new channel", + log_header ("no memory to create new channel", client, mp, pPayload, 0); SEND_LOCK(client); - send_err(mp, - ECA_ALLOCMEM, - client, - RECORD_NAME(&tmp_addr)); + send_err(mp, + ECA_ALLOCMEM, + client, + RECORD_NAME(dbch)); SEND_UNLOCK(client); + dbChannelDelete(dbch); return RSRV_ERROR; } } else { epicsMutexMustLock(prsrv_cast_client->chanListLock); /* - * clients which dont claim their + * clients which dont claim their * channel in use block prior to * timeout must reconnect */ @@ -1302,8 +1339,8 @@ static int claim_ciu_action ( caHdrLargeArray *mp, return RSRV_ERROR; } - /* - * duplicate claim message are unacceptable + /* + * duplicate claim message are unacceptable * (so we disconnect the client) */ if (pciu->client!=prsrv_cast_client) { @@ -1327,7 +1364,7 @@ static int claim_ciu_action ( caHdrLargeArray *mp, * who is claiming it */ ellDelete( - &prsrv_cast_client->chanList, + &prsrv_cast_client->chanList, &pciu->node); epicsMutexUnlock(prsrv_cast_client->chanListLock); @@ -1343,12 +1380,12 @@ static int claim_ciu_action ( caHdrLargeArray *mp, */ status = asAddClient( &pciu->asClientPVT, - asDbGetMemberPvt(&pciu->addr), - asDbGetAsl(&pciu->addr), + asDbGetMemberPvt(pciu->dbch), + asDbGetAsl(pciu->dbch), client->pUserName ? client->pUserName : "", - client->pHostName ? client->pHostName : ""); + client->pHostName ? client->pHostName : ""); if(status != 0 && status != S_asLib_asNotActive){ - log_header ("No room for security table", + log_header ("No room for security table", client, mp, pPayload, 0); SEND_LOCK(client); send_err(mp, ECA_ALLOCMEM, client, "No room for security table"); @@ -1357,7 +1394,7 @@ static int claim_ciu_action ( caHdrLargeArray *mp, } /* - * store ptr to channel in use block + * store ptr to channel in use block * in access security private */ asPutClientPvt(pciu->asClientPVT, pciu); @@ -1366,7 +1403,7 @@ static int claim_ciu_action ( caHdrLargeArray *mp, * register for asynch updates of access rights changes */ status = asRegisterClientCallback( - pciu->asClientPVT, + pciu->asClientPVT, casAccessRightsCB); if ( status == S_asLib_asNotActive ) { epicsMutexMustLock ( client->chanListLock ); @@ -1378,10 +1415,10 @@ static int claim_ciu_action ( caHdrLargeArray *mp, claim_ciu_reply ( pciu ); } else if (status!=0) { - log_header ("No room for access security state change subscription", + log_header ("No room for access security state change subscription", client, mp, pPayload, 0); SEND_LOCK(client); - send_err(mp, ECA_ALLOCMEM, client, + send_err(mp, ECA_ALLOCMEM, client, "No room for access security state change subscription"); SEND_UNLOCK(client); return RSRV_ERROR; @@ -1429,7 +1466,7 @@ static void write_notify_call_back(putNotify *ppn) db_post_extra_labor(pClient->evuser); } -/* +/* * write_notify_reply() * (called by the CA server event task via the extra labor interface) */ @@ -1477,18 +1514,18 @@ static void write_notify_reply ( struct client * pClient ) asTrapWriteAfter ( asWritePvtTmp ); /* - * the channel id field is being abused to carry + * the channel id field is being abused to carry * status here */ SEND_LOCK(pClient); - localStatus = cas_copy_in_header ( - pClient, CA_PROTO_WRITE_NOTIFY, - 0u, msgtmp.m_dataType, msgtmp.m_count, status, + localStatus = cas_copy_in_header ( + pClient, CA_PROTO_WRITE_NOTIFY, + 0u, msgtmp.m_dataType, msgtmp.m_count, status, msgtmp.m_available, 0 ); if ( localStatus != ECA_NORMAL ) { /* * inability to aquire buffer space - * Indicates corruption + * Indicates corruption */ errlogPrintf("CA server corrupted - put call back(s) discarded\n"); SEND_UNLOCK ( pClient ); @@ -1511,11 +1548,11 @@ static void write_notify_reply ( struct client * pClient ) */ static void sendAllUpdateAS ( struct client *client ) { - struct channel_in_use *pciu; + struct channel_in_use *pciu; epicsMutexMustLock ( client->chanListLock ); - pciu = ( struct channel_in_use * ) + pciu = ( struct channel_in_use * ) ellGet ( & client->chanPendingUpdateARList ); while ( pciu ) { if ( pciu->state == rsrvCS_pendConnectRespUpdatePendAR ) { @@ -1531,14 +1568,14 @@ static void sendAllUpdateAS ( struct client *client ) } pciu->state = rsrvCS_inService; ellAdd ( & client->chanList, & pciu->node ); - pciu = ( struct channel_in_use * ) + pciu = ( struct channel_in_use * ) ellGet ( & client->chanPendingUpdateARList ); } epicsMutexUnlock( client->chanListLock ); } -/* +/* * rsrv_extra_labor() * (called by the CA server event task via the extra labor interface) */ @@ -1562,8 +1599,8 @@ static void putNotifyErrorReply ( struct client *client, caHdrLargeArray *mp, in * the cid field abused to contain status * during put cb replies */ - status = cas_copy_in_header ( client, CA_PROTO_WRITE_NOTIFY, - 0u, mp->m_dataType, mp->m_count, statusCA, + status = cas_copy_in_header ( client, CA_PROTO_WRITE_NOTIFY, + 0u, mp->m_dataType, mp->m_count, statusCA, mp->m_available, 0 ); if ( status != ECA_NORMAL ) { SEND_UNLOCK ( client ); @@ -1578,28 +1615,28 @@ static void putNotifyErrorReply ( struct client *client, caHdrLargeArray *mp, in void initializePutNotifyFreeList (void) { if ( ! rsrvPutNotifyFreeList ) { - freeListInitPvt ( &rsrvPutNotifyFreeList, + freeListInitPvt ( &rsrvPutNotifyFreeList, sizeof(struct rsrv_put_notify), 512 ); assert ( rsrvPutNotifyFreeList ); } } -static struct rsrv_put_notify * +static struct rsrv_put_notify * rsrvAllocPutNotify ( struct channel_in_use * pciu ) { struct rsrv_put_notify *pNotify; - + if ( rsrvPutNotifyFreeList ) { pNotify = (RSRVPUTNOTIFY *) freeListCalloc ( rsrvPutNotifyFreeList ); if ( pNotify ) { - pNotify->dbPutNotify.pbuffer = + pNotify->dbPutNotify.pbuffer = &pNotify->dbrScalarValue; - pNotify->valueSize = + pNotify->valueSize = sizeof (pNotify->dbrScalarValue); pNotify->dbPutNotify.usrPvt = pciu; - pNotify->dbPutNotify.paddr = &pciu->addr; - pNotify->dbPutNotify.userCallback = + pNotify->dbPutNotify.chan = pciu->dbch; + pNotify->dbPutNotify.userCallback = write_notify_call_back; } } @@ -1609,18 +1646,18 @@ static struct rsrv_put_notify * return pNotify; } -static int rsrvExpandPutNotify ( +static int rsrvExpandPutNotify ( struct rsrv_put_notify * pNotify, unsigned sizeNeeded ) { int booleanStatus; - + if ( sizeNeeded > pNotify->valueSize ) { /* - * try to use the union embeded in the free list - * item, but allocate a random sized block if they + * try to use the union embeded in the free list + * item, but allocate a random sized block if they * writing a vector. */ - if ( pNotify->valueSize > + if ( pNotify->valueSize > sizeof (pNotify->dbrScalarValue) ) { free ( pNotify->dbPutNotify.pbuffer ); } @@ -1633,9 +1670,9 @@ static int rsrvExpandPutNotify ( /* * revert back to the embedded union */ - pNotify->dbPutNotify.pbuffer = + pNotify->dbPutNotify.pbuffer = &pNotify->dbrScalarValue; - pNotify->valueSize = + pNotify->valueSize = sizeof (pNotify->dbrScalarValue); booleanStatus = FALSE; } @@ -1643,7 +1680,7 @@ static int rsrvExpandPutNotify ( else { booleanStatus = TRUE; } - + return booleanStatus; } @@ -1651,7 +1688,7 @@ unsigned rsrvSizeOfPutNotify ( struct rsrv_put_notify *pNotify ) { unsigned size = sizeof ( *pNotify ); if ( pNotify ) { - if ( pNotify->valueSize > + if ( pNotify->valueSize > sizeof ( pNotify->dbrScalarValue ) ) { size += pNotify->valueSize; } @@ -1659,7 +1696,7 @@ unsigned rsrvSizeOfPutNotify ( struct rsrv_put_notify *pNotify ) return size; } -void rsrvFreePutNotify ( client *pClient, +void rsrvFreePutNotify ( client *pClient, struct rsrv_put_notify *pNotify ) { if ( pNotify ) { @@ -1671,16 +1708,16 @@ void rsrvFreePutNotify ( client *pClient, epicsMutexUnlock ( pClient->putNotifyLock ); /* - * if any possiblity that the put notify is + * if any possiblity that the put notify is * outstanding then cancel it */ if ( busyTmp ) { dbNotifyCancel ( &pNotify->dbPutNotify ); - } + } epicsMutexMustLock ( pClient->putNotifyLock ); if ( pNotify->onExtraLaborQueue ) { - ellDelete ( &pClient->putNotifyQue, + ellDelete ( &pClient->putNotifyQue, &pNotify->node ); } busyTmp = pNotify->busy; @@ -1692,7 +1729,7 @@ void rsrvFreePutNotify ( client *pClient, asTrapWriteAfter ( asWritePvtTmp ); } - if ( pNotify->valueSize > + if ( pNotify->valueSize > sizeof(pNotify->dbrScalarValue) ) { free ( pNotify->dbPutNotify.pbuffer ); } @@ -1703,7 +1740,7 @@ void rsrvFreePutNotify ( client *pClient, /* * write_notify_action() */ -static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, +static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { unsigned size; @@ -1732,7 +1769,7 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, if ( pciu->pPutNotify ) { /* - * serialize concurrent put notifies + * serialize concurrent put notifies */ epicsMutexMustLock(client->putNotifyLock); while(pciu->pPutNotify->busy){ @@ -1757,7 +1794,7 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, busyTmp = pciu->pPutNotify->busy; if ( busyTmp ) { if ( pciu->pPutNotify->onExtraLaborQueue ) { - ellDelete ( &client->putNotifyQue, + ellDelete ( &client->putNotifyQue, &pciu->pPutNotify->node ); } pciu->pPutNotify->busy = FALSE; @@ -1765,9 +1802,9 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, pciu->pPutNotify->asWritePvt = 0; } epicsMutexUnlock(client->putNotifyLock); - + if ( busyTmp ) { - log_header("put call back time out", client, + log_header("put call back time out", client, &pciu->pPutNotify->msg, pciu->pPutNotify->dbPutNotify.pbuffer, 0); asTrapWriteAfter ( asWritePvtTmp ); putNotifyErrorReply (client, &pciu->pPutNotify->msg, ECA_PUTCBINPROG); @@ -1784,15 +1821,15 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, * send error and go to next request * if there isnt enough memory left */ - log_header ( "no memory to initiate put notify", + log_header ( "no memory to initiate put notify", client, mp, pPayload, 0 ); putNotifyErrorReply (client, mp, ECA_ALLOCMEM); return RSRV_ERROR; } } - + if ( ! rsrvExpandPutNotify ( pciu->pPutNotify, size ) ) { - log_header ( "no memory to initiate vector put notify", + log_header ( "no memory to initiate vector put notify", client, mp, pPayload, 0 ); putNotifyErrorReply ( client, mp, ECA_ALLOCMEM ); return RSRV_ERROR; @@ -1803,8 +1840,8 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, pciu->pPutNotify->msg = *mp; pciu->pPutNotify->dbPutNotify.nRequest = mp->m_count; - status = caNetConvert ( - mp->m_dataType, pPayload, pciu->pPutNotify->dbPutNotify.pbuffer, + status = caNetConvert ( + mp->m_dataType, pPayload, pciu->pPutNotify->dbPutNotify.pbuffer, FALSE /* net -> host format */, mp->m_count ); if ( status != ECA_NORMAL ) { log_header ("invalid data type", client, mp, pPayload, 0); @@ -1819,11 +1856,11 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, return RSRV_OK; } - pciu->pPutNotify->asWritePvt = asTrapWriteBefore ( + pciu->pPutNotify->asWritePvt = asTrapWriteBefore ( pciu->asClientPVT, pciu->client->pUserName ? pciu->client->pUserName : "", pciu->client->pHostName ? pciu->client->pHostName : "", - (void *) &pciu->addr ); + pciu->dbch ); dbPutNotify(&pciu->pPutNotify->dbPutNotify); @@ -1849,10 +1886,10 @@ static int event_add_action (caHdrLargeArray *mp, void *pPayload, struct client } /* - * stop further use of server if memory becomes scarse + * stop further use of server if memory becomes scarce */ spaceAvailOnFreeList = freeListItemsAvail ( rsrvEventFreeList ) > 0; - if ( osiSufficentSpaceInPool(sizeof(*pevext)) || spaceAvailOnFreeList ) { + if ( osiSufficentSpaceInPool(sizeof(*pevext)) || spaceAvailOnFreeList ) { pevext = (struct event_ext *) freeListCalloc (rsrvEventFreeList); } else { @@ -1860,14 +1897,14 @@ static int event_add_action (caHdrLargeArray *mp, void *pPayload, struct client } if (!pevext) { - log_header ("no memory to add subscription", + log_header ("no memory to add subscription", client, mp, pPayload, 0); SEND_LOCK(client); send_err( mp, - ECA_ALLOCMEM, - client, - RECORD_NAME(&pciu->addr)); + ECA_ALLOCMEM, + client, + RECORD_NAME(pciu->dbch)); SEND_UNLOCK(client); return RSRV_ERROR; } @@ -1881,15 +1918,15 @@ static int event_add_action (caHdrLargeArray *mp, void *pPayload, struct client ellAdd( &pciu->eventq, &pevext->node); epicsMutexUnlock(client->eventqLock); - pevext->pdbev = db_add_event (client->evuser, &pciu->addr, + pevext->pdbev = db_add_event (client->evuser, pciu->dbch, read_reply, pevext, pevext->mask); if (pevext->pdbev == NULL) { - log_header ("no memory to add subscription to db", + log_header ("no memory to add subscription to db", client, mp, pPayload, 0); SEND_LOCK(client); - send_err (mp, ECA_ALLOCMEM, client, - "subscription install into record %s failed", - RECORD_NAME(&pciu->addr)); + send_err (mp, ECA_ALLOCMEM, client, + "subscription install into record %s failed", + RECORD_NAME(pciu->dbch)); SEND_UNLOCK(client); return RSRV_ERROR; } @@ -1903,7 +1940,7 @@ static int event_add_action (caHdrLargeArray *mp, void *pPayload, struct client * of the socket would block. This prevents * a application program initiated deadlock. * - * However when I am reconnecting I reissue + * However when I am reconnecting I reissue * the monitors and I could get deadlocked. * The client is blocked sending and the server * task for the client is blocked sending in @@ -1914,7 +1951,7 @@ static int event_add_action (caHdrLargeArray *mp, void *pPayload, struct client * post_single_event() below instead of calling * read_reply() in this module. This is a complete * fix since a monitor setup is the only request - * soliciting a reply in the client which is + * soliciting a reply in the client which is * issued from inside of service.c (from inside * of the part of the ca client which services * messages sent by the server). @@ -1940,13 +1977,13 @@ static int event_add_action (caHdrLargeArray *mp, void *pPayload, struct client /* * clear_channel_reply() */ -static int clear_channel_reply ( caHdrLargeArray *mp, +static int clear_channel_reply ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { struct event_ext *pevext; struct channel_in_use *pciu; int status; - + /* * * Verify the channel @@ -1957,43 +1994,43 @@ static int clear_channel_reply ( caHdrLargeArray *mp, logBadId ( client, mp, pPayload ); return RSRV_ERROR; } - + rsrvFreePutNotify ( client, pciu->pPutNotify ); - + while (TRUE){ epicsMutexMustLock(client->eventqLock); pevext = (struct event_ext *) ellGet(&pciu->eventq); epicsMutexUnlock(client->eventqLock); - + if(!pevext){ break; } - + if (pevext->pdbev) { db_cancel_event (pevext->pdbev); } freeListFree(rsrvEventFreeList, pevext); } - + db_flush_extra_labor_event ( client->evuser ); - + /* * send delete confirmed message */ SEND_LOCK(client); - status = cas_copy_in_header ( client, CA_PROTO_CLEAR_CHANNEL, - 0u, mp->m_dataType, mp->m_count, mp->m_cid, + status = cas_copy_in_header ( client, CA_PROTO_CLEAR_CHANNEL, + 0u, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, NULL ); if ( status != ECA_NORMAL ) { SEND_UNLOCK(client); return RSRV_ERROR; } - + cas_commit_msg ( client, 0u ); SEND_UNLOCK(client); - + epicsMutexMustLock ( client->chanListLock ); - if ( pciu->state == rsrvCS_inService || + if ( pciu->state == rsrvCS_inService || pciu->state == rsrvCS_pendConnectResp ) { ellDelete ( &client->chanList, &pciu->node ); } @@ -2004,22 +2041,22 @@ static int clear_channel_reply ( caHdrLargeArray *mp, else { epicsMutexUnlock( client->chanListLock ); SEND_LOCK(client); - send_err(mp, ECA_INTERNAL, client, + send_err(mp, ECA_INTERNAL, client, "channel was in strange state or corrupted during cleanup"); SEND_UNLOCK(client); return RSRV_ERROR; } epicsMutexUnlock( client->chanListLock ); - + /* * remove from access control list */ status = asRemoveClient(&pciu->asClientPVT); if(status != 0 && status != S_asLib_asNotActive){ - errMessage(status, RECORD_NAME(&pciu->addr)); + errMessage(status, RECORD_NAME(pciu->dbch)); return RSRV_ERROR; } - + LOCK_CLIENTQ; status = bucketRemoveItemUnsignedId (pCaBucket, &pciu->sid); if(status != S_bucket_success){ @@ -2031,11 +2068,12 @@ static int clear_channel_reply ( caHdrLargeArray *mp, rsrvChannelCount--; UNLOCK_CLIENTQ; + dbChannelDelete(pciu->dbch); freeListFree(rsrvChanFreeList, pciu); - + return RSRV_OK; } - + /* * * event_cancel_reply() @@ -2049,7 +2087,7 @@ static int event_cancel_reply ( caHdrLargeArray *mp, void *pPayload, struct clie struct channel_in_use *pciu; struct event_ext *pevext; int status; - + /* * * Verify the channel @@ -2060,7 +2098,7 @@ static int event_cancel_reply ( caHdrLargeArray *mp, void *pPayload, struct clie logBadId ( client, mp, pPayload ); return RSRV_ERROR; } - + /* * search events on this channel for a match * (there are usually very few monitors per channel) @@ -2068,38 +2106,38 @@ static int event_cancel_reply ( caHdrLargeArray *mp, void *pPayload, struct clie epicsMutexMustLock(client->eventqLock); for (pevext = (struct event_ext *) ellFirst(&pciu->eventq); pevext; pevext = (struct event_ext *) ellNext(&pevext->node)){ - + if (pevext->msg.m_available == mp->m_available) { ellDelete(&pciu->eventq, &pevext->node); break; } } epicsMutexUnlock(client->eventqLock); - + /* - * Not Found- return an exception event + * Not Found- return an exception event */ if(!pevext){ SEND_LOCK(client); - send_err(mp, ECA_BADMONID, client, RECORD_NAME(&pciu->addr)); + send_err(mp, ECA_BADMONID, client, RECORD_NAME(pciu->dbch)); SEND_UNLOCK(client); return RSRV_ERROR; } - + /* * cancel monitor activity in progress */ if (pevext->pdbev) { db_cancel_event (pevext->pdbev); } - + /* * send delete confirmed message */ SEND_LOCK(client); - status = cas_copy_in_header ( client, pevext->msg.m_cmmd, - 0u, pevext->msg.m_dataType, pevext->msg.m_count, pevext->msg.m_cid, + status = cas_copy_in_header ( client, pevext->msg.m_cmmd, + 0u, pevext->msg.m_dataType, pevext->msg.m_count, pevext->msg.m_cid, pevext->msg.m_available, NULL ); if ( status != ECA_NORMAL ) { SEND_UNLOCK(client); @@ -2107,7 +2145,7 @@ static int event_cancel_reply ( caHdrLargeArray *mp, void *pPayload, struct clie } cas_commit_msg ( client, 0 ); SEND_UNLOCK(client); - + freeListFree (rsrvEventFreeList, pevext); return RSRV_OK; @@ -2120,8 +2158,8 @@ static int read_sync_reply ( caHdrLargeArray *mp, void *pPayload, struct client { int status; SEND_LOCK(client); - status = cas_copy_in_header ( client, mp->m_cmmd, - 0u, mp->m_dataType, mp->m_count, mp->m_cid, + status = cas_copy_in_header ( client, mp->m_cmmd, + 0u, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, NULL ); if ( status != ECA_NORMAL ) { SEND_UNLOCK(client); @@ -2132,17 +2170,17 @@ static int read_sync_reply ( caHdrLargeArray *mp, void *pPayload, struct client return RSRV_OK; } -/* +/* * search_fail_reply() * - * Only when requested by the client + * Only when requested by the client * send search failed reply */ static void search_fail_reply ( caHdrLargeArray *mp, void *pPayload, struct client *client) { int status; SEND_LOCK ( client ); - status = cas_copy_in_header ( client, CA_PROTO_NOT_FOUND, + status = cas_copy_in_header ( client, CA_PROTO_NOT_FOUND, 0u, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, NULL ); if ( status != ECA_NORMAL ) { SEND_UNLOCK ( client ); @@ -2180,16 +2218,16 @@ int rsrv_version_reply ( struct client *client ) SEND_LOCK ( client ); /* * sequence number is specified zero when we copy in the - * header because we dont know it until we receive a datagram + * header because we dont know it until we receive a datagram * from the client */ - status = cas_copy_in_header ( client, CA_PROTO_VERSION, - 0, 0, CA_MINOR_PROTOCOL_REVISION, + status = cas_copy_in_header ( client, CA_PROTO_VERSION, + 0, 0, CA_MINOR_PROTOCOL_REVISION, 0, 0, 0 ); if ( status != ECA_NORMAL ) { SEND_UNLOCK ( client ); return RSRV_ERROR; - } + } cas_commit_msg ( client, 0 ); SEND_UNLOCK ( client ); return RSRV_OK; @@ -2200,7 +2238,6 @@ int rsrv_version_reply ( struct client *client ) */ static int search_reply_udp ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { - struct dbAddr tmp_addr; ca_uint16_t *pMinorVersion; char *pName = (char *) pPayload; int status; @@ -2215,41 +2252,41 @@ static int search_reply_udp ( caHdrLargeArray *mp, void *pPayload, struct client * check the sanity of the message */ if (mp->m_postsize<=1) { - log_header ("empty PV name in UDP search request?", + log_header ("empty PV name in UDP search request?", client, mp, pPayload, 0); return RSRV_OK; } pName[mp->m_postsize-1] = '\0'; - + /* Exit quickly if channel not on this node */ - status = db_name_to_addr (pName, &tmp_addr); - if (status) { + if (dbChannelTest(pName)) { DLOG ( 2, ( "CAS: Lookup for channel \"%s\" failed\n", pPayLoad ) ); if (mp->m_dataType == DOREPLY) search_fail_reply ( mp, pPayload, client ); return RSRV_OK; } - + /* - * stop further use of server if memory becomes scarse + * stop further use of server if memory becomes scarce */ spaceAvailOnFreeList = freeListItemsAvail ( rsrvChanFreeList ) > 0 && freeListItemsAvail ( rsrvEventFreeList ) > reasonableMonitorSpace; - spaceNeeded = sizeof (struct channel_in_use) + + spaceNeeded = sizeof (struct channel_in_use) + reasonableMonitorSpace * sizeof (struct event_ext); - if ( ! ( osiSufficentSpaceInPool(spaceNeeded) || spaceAvailOnFreeList ) ) { + if ( ! ( osiSufficentSpaceInPool(spaceNeeded) || spaceAvailOnFreeList ) ) { SEND_LOCK(client); send_err ( mp, ECA_ALLOCMEM, client, "Server memory exhausted" ); SEND_UNLOCK(client); return RSRV_OK; } - + /* * starting with V4.4 the count field is used (abused) * to store the minor version number of the client. * * New versions dont alloc the channel in response * to a search request. + * For these, allocation has been moved to claim_ciu_action(). * * m_count, m_cid are already in host format... */ @@ -2259,49 +2296,58 @@ static int search_reply_udp ( caHdrLargeArray *mp, void *pPayload, struct client type = ca_server_port; } else { + struct dbChannel *dbch; struct channel_in_use *pchannel; - - pchannel = casCreateChannel ( client, &tmp_addr, mp->m_cid ); + + dbch = dbChannel_create(pName); + if (!dbch) { + DLOG ( 2, ( "CAS: dbChannel Test of \"%s\" OK but Create failed\n", pName ) ); + if (mp->m_dataType == DOREPLY) + search_fail_reply ( mp, pPayload, client ); + return RSRV_OK; + } + pchannel = casCreateChannel ( client, dbch, mp->m_cid ); if (!pchannel) { SEND_LOCK(client); - send_err ( mp, ECA_ALLOCMEM, client, - RECORD_NAME ( &tmp_addr ) ); + send_err ( mp, ECA_ALLOCMEM, client, + RECORD_NAME ( dbch ) ); SEND_UNLOCK ( client ); + dbChannelDelete(dbch); return RSRV_OK; } sid = pchannel->sid; - if ( tmp_addr.no_elements < 0 ) { + if ( dbChannelFinalElements(dbch) < 0 ) { count = 0; } - else if ( tmp_addr.no_elements > 0xffff ) { + else if ( dbChannelFinalElements(dbch) > 0xffff ) { count = 0xfffe; } else { - count = (ca_uint16_t) tmp_addr.no_elements; + count = (ca_uint16_t) dbChannelFinalElements(dbch); } - type = (ca_uint16_t) tmp_addr.dbr_field_type; + type = (ca_uint16_t) dbChannelFinalExportType(dbch); } - + SEND_LOCK ( client ); - status = cas_copy_in_header ( client, CA_PROTO_SEARCH, - sizeof(*pMinorVersion), type, count, - sid, mp->m_available, + status = cas_copy_in_header ( client, CA_PROTO_SEARCH, + sizeof(*pMinorVersion), type, count, + sid, mp->m_available, ( void * ) &pMinorVersion ); if ( status != ECA_NORMAL ) { SEND_UNLOCK ( client ); return RSRV_ERROR; } - + /* * Starting with CA V4.1 the minor version number * is appended to the end of each search reply. - * This value is ignored by earlier clients. + * This value is ignored by earlier clients. */ *pMinorVersion = htons ( CA_MINOR_PROTOCOL_REVISION ); - + cas_commit_msg ( client, sizeof ( *pMinorVersion ) ); SEND_UNLOCK ( client ); - + return RSRV_OK; } @@ -2311,7 +2357,6 @@ static int search_reply_udp ( caHdrLargeArray *mp, void *pPayload, struct client static int search_reply_tcp ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { - struct dbAddr tmp_addr; char *pName = (char *) pPayload; int status; int spaceAvailOnFreeList; @@ -2329,8 +2374,7 @@ static int search_reply_tcp ( pName[mp->m_postsize-1] = '\0'; /* Exit quickly if channel not on this node */ - status = db_name_to_addr (pName, &tmp_addr); - if (status) { + if (dbChannelTest(pName)) { DLOG ( 2, ( "CAS: Lookup for channel \"%s\" failed\n", pPayLoad ) ); if (mp->m_dataType == DOREPLY) search_fail_reply ( mp, pPayload, client ); @@ -2370,7 +2414,7 @@ typedef int (*pProtoStubTCP) (caHdrLargeArray *mp, void *pPayload, struct client /* * TCP protocol jump table */ -static const pProtoStubTCP tcpJumpTable[] = +static const pProtoStubTCP tcpJumpTable[] = { tcp_version_action, event_add_action, @@ -2406,7 +2450,7 @@ static const pProtoStubTCP tcpJumpTable[] = * UDP protocol jump table */ typedef int (*pProtoStubUDP) (caHdrLargeArray *mp, void *pPayload, struct client *client); -static const pProtoStubUDP udpJumpTable[] = +static const pProtoStubUDP udpJumpTable[] = { udp_version_action, bad_udp_cmd_action, @@ -2447,7 +2491,7 @@ int camessage ( struct client *client ) unsigned msgsize; unsigned bytes_left; int status = RSRV_ERROR; - + if ( ! pCaBucket ) { pCaBucket = bucketCreate(CAS_HASH_TABLE_SIZE); if(!pCaBucket){ @@ -2467,11 +2511,11 @@ int camessage ( struct client *client ) client->recvBytesToDrain = 0u; } } - + DLOG ( 2, ( "CAS: Parsing %d(decimal) bytes\n", recv->cnt ) ); - + while ( 1 ) - { + { caHdrLargeArray msg; caHdr *mp; void *pBody; @@ -2482,7 +2526,7 @@ int camessage ( struct client *client ) status = RSRV_OK; break; } - + mp = (caHdr *) &client->recv.buf[client->recv.stk]; msg.m_cmmd = ntohs ( mp->m_cmmd ); msg.m_postsize = ntohs ( mp->m_postsize ); @@ -2508,13 +2552,13 @@ int camessage ( struct client *client ) } /* - * disconnect clients that dont send 8 byte + * disconnect clients that dont send 8 byte * aligned payloads */ if ( msgsize & 0x7 ) { - send_err ( &msg, ECA_INTERNAL, client, + send_err ( &msg, ECA_INTERNAL, client, "CAS: Missaligned protocol rejected" ); - log_header ( "CAS: Missaligned protocol rejected", + log_header ( "CAS: Missaligned protocol rejected", client, &msg, 0, nmsg ); status = RSRV_ERROR; break; @@ -2529,10 +2573,10 @@ int camessage ( struct client *client ) if ( msgsize > client->recv.maxstk ) { casExpandRecvBuffer ( client, msgsize ); if ( msgsize > client->recv.maxstk ) { - send_err ( &msg, ECA_TOLARGE, client, + send_err ( &msg, ECA_TOLARGE, client, "CAS: Server unable to load large request message. Max bytes=%lu", rsrvSizeofLargeBufTCP ); - log_header ( "CAS: server unable to load large request message", + log_header ( "CAS: server unable to load large request message", client, &msg, 0, nmsg ); assert ( client->recv.cnt <= client->recv.maxstk ); assert ( msgsize >= bytes_left ); @@ -2550,12 +2594,12 @@ int camessage ( struct client *client ) status = RSRV_OK; break; } - + nmsg++; - + if ( CASDEBUG > 2 ) log_header (NULL, client, &msg, pBody, nmsg); - + if ( client == prsrv_cast_client ) { if ( msg.m_cmmd < NELEMENTS ( udpJumpTable ) ) { status = ( *udpJumpTable[msg.m_cmmd] )( &msg, pBody, client ); @@ -2581,10 +2625,10 @@ int camessage ( struct client *client ) return bad_tcp_cmd_action ( &msg, pBody, client ); } } - + client->recv.stk += msgsize; } - + return status; } @@ -2595,8 +2639,8 @@ int rsrvCheckPut (const struct channel_in_use *pciu) { /* * SPC_NOMOD fields are always unwritable - */ - if (pciu->addr.special==SPC_NOMOD) { + */ + if (dbChannelSpecial(pciu->dbch) == SPC_NOMOD) { return 0; } else { diff --git a/src/ioc/rsrv/caservertask.c b/src/ioc/rsrv/caservertask.c index e86277306..7c82afe81 100644 --- a/src/ioc/rsrv/caservertask.c +++ b/src/ioc/rsrv/caservertask.c @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos 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. \*************************************************************************/ /* @@ -33,7 +33,6 @@ #include "freeList.h" #include "errlog.h" #include "db_field_log.h" -#include "dbAddr.h" #include "dbEvent.h" #include "dbCommon.h" #include "epicsStdio.h" @@ -73,22 +72,22 @@ static void req_server (void *pParm) epicsSignalInstallSigPipeIgnore (); taskwdInsert ( epicsThreadGetIdSelf (), NULL, NULL ); - + rsrvCurrentClient = epicsThreadPrivateCreate (); if ( envGetConfigParamPtr ( &EPICS_CAS_SERVER_PORT ) ) { - ca_server_port = envGetInetPortConfigParam ( &EPICS_CAS_SERVER_PORT, + ca_server_port = envGetInetPortConfigParam ( &EPICS_CAS_SERVER_PORT, (unsigned short) CA_SERVER_PORT ); } else { - ca_server_port = envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT, + ca_server_port = envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT, (unsigned short) CA_SERVER_PORT ); } if (IOC_sock != 0 && IOC_sock != INVALID_SOCKET) { epicsSocketDestroy ( IOC_sock ); } - + /* * Open the socket. Use ARPA Internet address format and stream * sockets. Format described in . @@ -103,7 +102,7 @@ static void req_server (void *pParm) /* Zero the sock_addr structure */ memset ( (void *) &serverAddr, 0, sizeof ( serverAddr ) ); serverAddr.sin_family = AF_INET; - serverAddr.sin_addr.s_addr = htonl (INADDR_ANY); + serverAddr.sin_addr.s_addr = htonl (INADDR_ANY); serverAddr.sin_port = htons ( ca_server_port ); /* get server's Internet address */ @@ -116,12 +115,12 @@ static void req_server (void *pParm) * work correctly) */ serverAddr.sin_port = ntohs (0); - status = bind ( IOC_sock, + status = bind ( IOC_sock, (struct sockaddr *) &serverAddr, sizeof ( serverAddr ) ); } if ( status < 0 ) { char sockErrBuf[64]; - epicsSocketConvertErrnoToString ( + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAS: Socket bind error was \"%s\"\n", sockErrBuf ); @@ -134,13 +133,13 @@ static void req_server (void *pParm) } addrSize = ( osiSocklen_t ) sizeof ( serverAddr ); - status = getsockname ( IOC_sock, + status = getsockname ( IOC_sock, (struct sockaddr *)&serverAddr, &addrSize); if ( status ) { char sockErrBuf[64]; - epicsSocketConvertErrnoToString ( + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - errlogPrintf ( "CAS: getsockname() error %s\n", + errlogPrintf ( "CAS: getsockname() error %s\n", sockErrBuf ); epicsThreadSuspendSelf (); } @@ -149,7 +148,7 @@ static void req_server (void *pParm) if ( portChange ) { errlogPrintf ( "cas warning: Configured TCP port was unavailable.\n"); - errlogPrintf ( "cas warning: Using dynamically assigned TCP port %hu,\n", + errlogPrintf ( "cas warning: Using dynamically assigned TCP port %hu,\n", ca_server_port ); errlogPrintf ( "cas warning: but now two or more servers share the same UDP port.\n"); errlogPrintf ( "cas warning: Depending on your IP kernel this server may not be\n" ); @@ -192,13 +191,13 @@ static void req_server (void *pParm) clientSock = epicsSocketAccept ( IOC_sock, &sockAddr, &addLen ); if ( clientSock == INVALID_SOCKET ) { char sockErrBuf[64]; - epicsSocketConvertErrnoToString ( + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf("CAS: Client accept error was \"%s\"\n", sockErrBuf ); epicsThreadSleep(15.0); continue; - } + } else { epicsThreadId id; struct client *pClient; @@ -282,14 +281,14 @@ int rsrv_init (void) castcp_ctl = ctlPause; /* - * go down two levels so that we are below + * go down two levels so that we are below * the TCP and event threads started on behalf * of individual clients */ - tbs = epicsThreadHighestPriorityLevelBelow ( + tbs = epicsThreadHighestPriorityLevelBelow ( epicsThreadPriorityCAServerLow, &priorityOfConnectDaemon ); if ( tbs == epicsThreadBooleanStatusSuccess ) { - tbs = epicsThreadHighestPriorityLevelBelow ( + tbs = epicsThreadHighestPriorityLevelBelow ( priorityOfConnectDaemon, &priorityOfConnectDaemon ); if ( tbs != epicsThreadBooleanStatusSuccess ) { priorityOfConnectDaemon = epicsThreadPriorityCAServerLow; @@ -330,7 +329,7 @@ int rsrv_pause (void) return RSRV_OK; } -static unsigned countChanListBytes ( +static unsigned countChanListBytes ( struct client *client, ELLLIST * pList ) { struct channel_in_use * pciu; @@ -349,23 +348,19 @@ static unsigned countChanListBytes ( return bytes_reserved; } -static void showChanList ( - struct client * client, ELLLIST * pList ) +static void showChanList ( + struct client * client, unsigned level, ELLLIST * pList ) { - unsigned i = 0u; struct channel_in_use * pciu; epicsMutexMustLock ( client->chanListLock ); pciu = (struct channel_in_use *) pList->node.next; while ( pciu ){ - printf( "\t%s(%d%c%c)", - pciu->addr.precord->name, + dbChannelShow ( pciu->dbch, level, 8 ); + printf( " # on eventq=%d, access=%c%c\n", ellCount ( &pciu->eventq ), asCheckGet ( pciu->asClientPVT ) ? 'r': '-', rsrvCheckPut ( pciu ) ? 'w': '-' ); pciu = ( struct channel_in_use * ) ellNext ( &pciu->node ); - if( ++i % 3u == 0u ) { - printf ( "\n" ); - } } epicsMutexUnlock ( client->chanListLock ); } @@ -398,28 +393,28 @@ static void log_one_client (struct client *client, unsigned level) send_delay = epicsTimeDiffInSeconds(¤t,&client->time_at_last_send); recv_delay = epicsTimeDiffInSeconds(¤t,&client->time_at_last_recv); - printf ( "%s %s(%s): User=\"%s\", V%u.%u, %d Channels, Priority=%u\n", + printf ( "%s %s(%s): User=\"%s\", V%u.%u, %d Channels, Priority=%u\n", pproto, clientHostName, client->pHostName ? client->pHostName : "", client->pUserName ? client->pUserName : "", CA_MAJOR_PROTOCOL_REVISION, client->minor_version_number, - ellCount(&client->chanList) + + ellCount(&client->chanList) + ellCount(&client->chanPendingUpdateARList), client->priority ); if ( level >= 1 ) { - printf ("\tTask Id=%p, Socket FD=%d\n", - (void *) client->tid, client->sock); - printf( - "\tSecs since last send %6.2f, Secs since last receive %6.2f\n", + printf ("\tTask Id=%p, Socket FD=%d\n", + (void *) client->tid, client->sock); + printf( + "\tSecs since last send %6.2f, Secs since last receive %6.2f\n", send_delay, recv_delay); - printf( - "\tUnprocessed request bytes=%u, Undelivered response bytes=%u\n", + printf( + "\tUnprocessed request bytes=%u, Undelivered response bytes=%u\n", client->recv.cnt - client->recv.stk, client->send.stk ); - printf( - "\tState=%s%s%s\n", + printf( + "\tState=%s%s%s\n", state[client->disconnect?1:0], client->send.type == mbtLargeTCP ? " jumbo-send-buf" : "", client->recv.type == mbtLargeTCP ? " jumbo-recv-buf" : ""); @@ -428,14 +423,13 @@ static void log_one_client (struct client *client, unsigned level) if ( level >= 2u ) { unsigned bytes_reserved = 0; bytes_reserved += sizeof(struct client); - bytes_reserved += countChanListBytes ( + bytes_reserved += countChanListBytes ( client, & client->chanList ); - bytes_reserved += countChanListBytes ( + bytes_reserved += countChanListBytes ( client, & client->chanPendingUpdateARList ); printf( "\t%d bytes allocated\n", bytes_reserved); - showChanList ( client, & client->chanList ); - showChanList ( client, & client->chanPendingUpdateARList ); - printf("\n"); + showChanList ( client, level - 2u, & client->chanList ); + showChanList ( client, level - 2u, & client->chanPendingUpdateARList ); } if ( level >= 3u ) { @@ -485,20 +479,20 @@ void epicsShareAPI casr (unsigned level) printf( "UDP Server:\n" ); log_one_client(prsrv_cast_client, level); } - + if (level>=2u) { bytes_reserved = 0u; - bytes_reserved += sizeof (struct client) * + bytes_reserved += sizeof (struct client) * freeListItemsAvail (rsrvClientFreeList); bytes_reserved += sizeof (struct channel_in_use) * freeListItemsAvail (rsrvChanFreeList); bytes_reserved += sizeof(struct event_ext) * freeListItemsAvail (rsrvEventFreeList); - bytes_reserved += MAX_TCP * + bytes_reserved += MAX_TCP * freeListItemsAvail ( rsrvSmallBufFreeListTCP ); - bytes_reserved += rsrvSizeofLargeBufTCP * + bytes_reserved += rsrvSizeofLargeBufTCP * freeListItemsAvail ( rsrvLargeBufFreeListTCP ); - bytes_reserved += rsrvSizeOfPutNotify ( 0 ) * + bytes_reserved += rsrvSizeOfPutNotify ( 0 ) * freeListItemsAvail ( rsrvPutNotifyFreeList ); printf( "There are currently %u bytes on the server's free list\n", (unsigned int) bytes_reserved); @@ -525,7 +519,7 @@ void epicsShareAPI casr (unsigned level) } } -/* +/* * destroy_client () */ void destroy_client ( struct client *client ) @@ -533,7 +527,7 @@ void destroy_client ( struct client *client ) if ( ! client ) { return; } - + if ( client->tid != 0 ) { taskwdRemove ( client->tid ); } @@ -603,12 +597,12 @@ void destroy_client ( struct client *client ) if ( client->pHostName ) { free ( client->pHostName ); - } + } freeListFree ( rsrvClientFreeList, client ); } -static void destroyAllChannels ( +static void destroyAllChannels ( struct client * client, ELLLIST * pList ) { if ( !client->chanListLock || !client->eventqLock ) { @@ -651,7 +645,7 @@ static void destroyAllChannels ( rsrvChannelCount--; UNLOCK_CLIENTQ; if ( status != S_bucket_success ) { - errPrintf ( status, __FILE__, __LINE__, + errPrintf ( status, __FILE__, __LINE__, "Bad id=%d at close", pciu->sid); } status = asRemoveClient(&pciu->asClientPVT); @@ -660,6 +654,7 @@ static void destroyAllChannels ( errPrintf ( status, __FILE__, __LINE__, "asRemoveClient" ); } + dbChannelDelete(pciu->dbch); freeListFree ( rsrvChanFreeList, pciu ); } } @@ -691,7 +686,7 @@ void destroy_tcp_client ( struct client *client ) if ( client->evuser ) { db_close_events (client->evuser); } - + destroy_client ( client ); } @@ -710,7 +705,7 @@ struct client * create_client ( SOCKET sock, int proto ) spaceAvailOnFreeList = freeListItemsAvail ( rsrvClientFreeList ) > 0 && freeListItemsAvail ( rsrvSmallBufFreeListTCP ) > 0; spaceNeeded = sizeof (struct client) + MAX_TCP; - if ( ! ( osiSufficentSpaceInPool(spaceNeeded) || spaceAvailOnFreeList ) ) { + if ( ! ( osiSufficentSpaceInPool(spaceNeeded) || spaceAvailOnFreeList ) ) { epicsSocketDestroy ( sock ); epicsPrintf ("CAS: no space in pool for a new client (below max block thresh)\n"); return NULL; @@ -721,7 +716,7 @@ struct client * create_client ( SOCKET sock, int proto ) epicsSocketDestroy ( sock ); epicsPrintf ("CAS: no space in pool for a new client (alloc failed)\n"); return NULL; - } + } client->sock = sock; client->proto = proto; @@ -737,8 +732,8 @@ struct client * create_client ( SOCKET sock, int proto ) return NULL; } - client->pUserName = NULL; - client->pHostName = NULL; + client->pUserName = NULL; + client->pHostName = NULL; ellInit ( & client->chanList ); ellInit ( & client->chanPendingUpdateARList ); ellInit ( & client->putNotifyQue ); @@ -776,7 +771,7 @@ struct client * create_client ( SOCKET sock, int proto ) epicsTimeGetCurrent ( &client->time_at_last_recv ); client->minor_version_number = CA_UKN_MINOR_VERSION; client->recvBytesToDrain = 0u; - + return client; } @@ -791,10 +786,10 @@ void casAttachThreadToClient ( struct client *pClient ) void casExpandSendBuffer ( struct client *pClient, ca_uint32_t size ) { - if ( pClient->send.type == mbtSmallTCP && rsrvSizeofLargeBufTCP > MAX_TCP + if ( pClient->send.type == mbtSmallTCP && rsrvSizeofLargeBufTCP > MAX_TCP && size <= rsrvSizeofLargeBufTCP ) { int spaceAvailOnFreeList = freeListItemsAvail ( rsrvLargeBufFreeListTCP ) > 0; - if ( osiSufficentSpaceInPool(rsrvSizeofLargeBufTCP) || spaceAvailOnFreeList ) { + if ( osiSufficentSpaceInPool(rsrvSizeofLargeBufTCP) || spaceAvailOnFreeList ) { char *pNewBuf = ( char * ) freeListCalloc ( rsrvLargeBufFreeListTCP ); if ( pNewBuf ) { memcpy ( pNewBuf, pClient->send.buf, pClient->send.stk ); @@ -812,7 +807,7 @@ void casExpandRecvBuffer ( struct client *pClient, ca_uint32_t size ) if ( pClient->recv.type == mbtSmallTCP && rsrvSizeofLargeBufTCP > MAX_TCP && size <= rsrvSizeofLargeBufTCP) { int spaceAvailOnFreeList = freeListItemsAvail ( rsrvLargeBufFreeListTCP ) > 0; - if ( osiSufficentSpaceInPool(rsrvSizeofLargeBufTCP) || spaceAvailOnFreeList ) { + if ( osiSufficentSpaceInPool(rsrvSizeofLargeBufTCP) || spaceAvailOnFreeList ) { char *pNewBuf = ( char * ) freeListCalloc ( rsrvLargeBufFreeListTCP ); if ( pNewBuf ) { assert ( pClient->recv.cnt >= pClient->recv.stk ); @@ -849,26 +844,26 @@ struct client *create_tcp_client ( SOCKET sock ) * see TCP(4P) this seems to make unsolicited single events much * faster. I take care of queue up as load increases. */ - status = setsockopt ( sock, IPPROTO_TCP, TCP_NODELAY, + status = setsockopt ( sock, IPPROTO_TCP, TCP_NODELAY, (char *) &intTrue, sizeof (intTrue) ); if (status < 0) { errlogPrintf ( "CAS: TCP_NODELAY option set failed\n" ); destroy_client ( client ); return NULL; } - - /* + + /* * turn on KEEPALIVE so if the client crashes * this task will find out and exit */ - status = setsockopt ( sock, SOL_SOCKET, SO_KEEPALIVE, + status = setsockopt ( sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &intTrue, sizeof (intTrue) ); if ( status < 0 ) { errlogPrintf ( "CAS: SO_KEEPALIVE option set failed\n" ); destroy_client ( client ); return NULL; } - + /* * some concern that vxWorks will run out of mBuf's * if this change is made @@ -876,8 +871,8 @@ struct client *create_tcp_client ( SOCKET sock ) * joh 11-10-98 */ #if 0 - /* - * set TCP buffer sizes to be synergistic + /* + * set TCP buffer sizes to be synergistic * with CA internal buffering */ i = MAX_MSG_SIZE; @@ -895,7 +890,7 @@ struct client *create_tcp_client ( SOCKET sock ) return NULL; } #endif - + addrSize = sizeof ( client->addr ); status = getpeername ( sock, (struct sockaddr *)&client->addr, &addrSize ); @@ -928,8 +923,8 @@ struct client *create_tcp_client ( SOCKET sock ) } } - status = db_start_events ( client->evuser, "CAS-event", - NULL, NULL, priorityOfEvents ); + status = db_start_events ( client->evuser, "CAS-event", + NULL, NULL, priorityOfEvents ); if ( status != DB_EVENT_OK ) { errlogPrintf ( "CAS: unable to start the event facility\n" ); destroy_tcp_client ( client ); diff --git a/src/ioc/rsrv/server.h b/src/ioc/rsrv/server.h index 9c222d09b..9651ca601 100644 --- a/src/ioc/rsrv/server.h +++ b/src/ioc/rsrv/server.h @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos 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. \*************************************************************************/ /* @@ -25,7 +25,7 @@ #include "epicsEvent.h" #include "bucketLib.h" #include "asLib.h" -#include "dbAddr.h" +#include "dbChannel.h" #include "dbNotify.h" #define CA_MINOR_PROTOCOL_REVISION 13 #include "caProto.h" @@ -64,7 +64,7 @@ struct message_buffer { char *buf; unsigned stk; unsigned maxstk; - unsigned cnt; + unsigned cnt; enum messageBufferType type; }; @@ -98,16 +98,16 @@ typedef struct client { char disconnect; /* disconnect detected */ } client; -enum rsrvChanState { +enum rsrvChanState { rsrvCS_invalid, rsrvCS_pendConnectResp, rsrvCS_inService, rsrvCS_pendConnectRespUpdatePendAR, - rsrvCS_inServiceUpdatePendAR + rsrvCS_inServiceUpdatePendAR }; /* - * per channel structure + * per channel structure * (stored in chanList or chanPendingUpdateARList off of a client block) */ struct channel_in_use { @@ -118,7 +118,7 @@ struct channel_in_use { const unsigned cid; /* client id */ const unsigned sid; /* server id */ epicsTimeStamp time_at_creation; /* for UDP timeout */ - struct dbAddr addr; + struct dbChannel *dbch; ASCLIENTPVT asClientPVT; enum rsrvChanState state; }; @@ -156,7 +156,7 @@ enum ctl {ctlInit, ctlRun, ctlPause, ctlExit}; # define DLOG(LEVEL,ARGSINPAREN) \ if (CASDEBUG > LEVEL) errlogPrintf ARGSINPAREN #else -# define DLOG(LEVEL,ARGSINPAREN) +# define DLOG(LEVEL,ARGSINPAREN) #endif GLBLTYPE int CASDEBUG; @@ -168,13 +168,13 @@ GLBLTYPE ELLLIST beaconAddrList; GLBLTYPE epicsMutexId clientQlock; GLBLTYPE struct client *prsrv_cast_client; GLBLTYPE BUCKET *pCaBucket; -GLBLTYPE void *rsrvClientFreeList; +GLBLTYPE void *rsrvClientFreeList; GLBLTYPE void *rsrvChanFreeList; GLBLTYPE void *rsrvEventFreeList; -GLBLTYPE void *rsrvSmallBufFreeListTCP; -GLBLTYPE void *rsrvLargeBufFreeListTCP; +GLBLTYPE void *rsrvSmallBufFreeListTCP; +GLBLTYPE void *rsrvLargeBufFreeListTCP; GLBLTYPE unsigned rsrvSizeofLargeBufTCP; -GLBLTYPE void *rsrvPutNotifyFreeList; +GLBLTYPE void *rsrvPutNotifyFreeList; GLBLTYPE unsigned rsrvChannelCount; GLBLTYPE epicsEventId casudp_startStopEvent; @@ -207,7 +207,7 @@ int camessage ( struct client *client ); void rsrv_extra_labor ( void * pArg ); int rsrvCheckPut ( const struct channel_in_use *pciu ); int rsrv_version_reply ( struct client *client ); -void rsrvFreePutNotify ( struct client *pClient, +void rsrvFreePutNotify ( struct client *pClient, struct rsrv_put_notify *pNotify ); void initializePutNotifyFreeList (void); unsigned rsrvSizeOfPutNotify ( struct rsrv_put_notify *pNotify ); @@ -221,9 +221,9 @@ void casExpandRecvBuffer ( struct client *pClient, ca_uint32_t size ); * outgoing protocol maintenance */ void casExpandSendBuffer ( struct client *pClient, ca_uint32_t size ); -int cas_copy_in_header ( +int cas_copy_in_header ( struct client *pClient, ca_uint16_t response, ca_uint32_t payloadSize, - ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, + ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, ca_uint32_t responseSpecific, void **pPayload ); void cas_set_header_cid ( struct client *pClient, ca_uint32_t ); void cas_set_header_count (struct client *pClient, ca_uint32_t count); diff --git a/src/libCom/Makefile b/src/libCom/Makefile index 349c8dd26..c8f41b330 100644 --- a/src/libCom/Makefile +++ b/src/libCom/Makefile @@ -35,6 +35,7 @@ include $(LIBCOM)/ring/Makefile include $(LIBCOM)/taskwd/Makefile include $(LIBCOM)/timer/Makefile include $(LIBCOM)/yacc/Makefile +include $(LIBCOM)/yajl/Makefile # Library to build: LIBRARY=Com diff --git a/src/libCom/ellLib/ellLib.c b/src/libCom/ellLib/ellLib.c index 12a12ed4c..be1632079 100644 --- a/src/libCom/ellLib/ellLib.c +++ b/src/libCom/ellLib/ellLib.c @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos 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. \*************************************************************************/ /* $Revision-Id$ * @@ -155,6 +155,21 @@ ELLNODE * ellGet (ELLLIST *pList) return pnode; } +/**************************************************************************** +* +* This function returns the last node in the specified list. The node is +* removed from the list. If the list is empty, NULL will be returned. +* +*****************************************************************************/ +ELLNODE * ellPop (ELLLIST *pList) +{ + ELLNODE *pnode = pList->node.previous; + + if (pnode != NULL) + ellDelete(pList, pnode); + + return pnode; +} /**************************************************************************** * * This function inserts the specified node pNode after pPrev in the list @@ -185,7 +200,7 @@ void ellInsert (ELLLIST *plist, ELLNODE *pPrev, ELLNODE *pNode) } /**************************************************************************** * - * This function returns the nodeNum'th element in pList. If there is no + * This function returns the nodeNum'th element in pList. If there is no * nodeNum'th node in the list, NULL will be returned. * *****************************************************************************/ diff --git a/src/libCom/ellLib/ellLib.h b/src/libCom/ellLib/ellLib.h index d855a552f..ea90774cf 100644 --- a/src/libCom/ellLib/ellLib.h +++ b/src/libCom/ellLib/ellLib.h @@ -1,15 +1,16 @@ /*************************************************************************\ -* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos 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. \*************************************************************************/ -/* $Revision-Id$ + +/* * * Author: John Winans (ANL) - * Date: 07-02-92 + * Andrew Johnson */ #ifndef INC_ellLib_H #define INC_ellLib_H @@ -52,6 +53,7 @@ epicsShareFunc void ellConcat (ELLLIST *pDstList, ELLLIST *pAddList); epicsShareFunc void ellDelete (ELLLIST *pList, ELLNODE *pNode); epicsShareFunc void ellExtract (ELLLIST *pSrcList, ELLNODE *pStartNode, ELLNODE *pEndNode, ELLLIST *pDstList); epicsShareFunc ELLNODE * ellGet (ELLLIST *pList); +epicsShareFunc ELLNODE * ellPop (ELLLIST *pList); epicsShareFunc void ellInsert (ELLLIST *plist, ELLNODE *pPrev, ELLNODE *pNode); epicsShareFunc ELLNODE * ellNth (ELLLIST *pList, int nodeNum); epicsShareFunc ELLNODE * ellNStep (ELLNODE *pNode, int nStep); diff --git a/src/libCom/gpHash/gpHash.h b/src/libCom/gpHash/gpHash.h index 1a2783b64..ce6ec5f4a 100644 --- a/src/libCom/gpHash/gpHash.h +++ b/src/libCom/gpHash/gpHash.h @@ -5,7 +5,7 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are 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. \*************************************************************************/ /* $Revision-Id$ */ /* Author: Marty Kraimer Date: 04-07-94 */ @@ -37,6 +37,8 @@ epicsShareFunc void epicsShareAPI gphInitPvt(struct gphPvt **ppvt, int tableSize); epicsShareFunc GPHENTRY * epicsShareAPI gphFind(struct gphPvt *pvt, const char *name, void *pvtid); +epicsShareFunc GPHENTRY * epicsShareAPI + gphFindParse(struct gphPvt *pvt, const char *name, size_t len, void *pvtid); epicsShareFunc GPHENTRY * epicsShareAPI gphAdd(struct gphPvt *pvt, const char *name, void *pvtid); epicsShareFunc void epicsShareAPI diff --git a/src/libCom/gpHash/gpHashLib.c b/src/libCom/gpHash/gpHashLib.c index 3cea5cb3a..c37371fae 100644 --- a/src/libCom/gpHash/gpHashLib.c +++ b/src/libCom/gpHash/gpHashLib.c @@ -4,12 +4,12 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos 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. \*************************************************************************/ /* $Revision-Id$ */ /* Author: Marty Kraimer Date: 04-07-94 */ - + #include #include #include @@ -34,7 +34,7 @@ typedef struct gphPvt { #define MIN_SIZE 256 #define DEFAULT_SIZE 512 #define MAX_SIZE 65536 - + void epicsShareAPI gphInitPvt(gphPvt **ppvt, int size) { gphPvt *pgphPvt; @@ -59,7 +59,7 @@ void epicsShareAPI gphInitPvt(gphPvt **ppvt, int size) return; } -GPHENTRY * epicsShareAPI gphFind(gphPvt *pgphPvt, const char *name, void *pvtid) +GPHENTRY * epicsShareAPI gphFindParse(gphPvt *pgphPvt, const char *name, size_t len, void *pvtid) { ELLLIST **paplist; ELLLIST *gphlist; @@ -69,7 +69,7 @@ GPHENTRY * epicsShareAPI gphFind(gphPvt *pgphPvt, const char *name, void *pvtid) if (pgphPvt == NULL) return NULL; paplist = pgphPvt->paplist; hash = epicsMemHash((char *)&pvtid, sizeof(void *), 0); - hash = epicsStrHash(name, hash) & pgphPvt->mask; + hash = epicsMemHash(name, len, hash) & pgphPvt->mask; epicsMutexMustLock(pgphPvt->lock); gphlist = paplist[hash]; @@ -81,14 +81,20 @@ GPHENTRY * epicsShareAPI gphFind(gphPvt *pgphPvt, const char *name, void *pvtid) while (pgphNode) { if (pvtid == pgphNode->pvtid && - strcmp(name, pgphNode->name) == 0) break; + strlen(pgphNode->name) == len && + strncmp(name, pgphNode->name, len) == 0) break; pgphNode = (GPHENTRY *) ellNext((ELLNODE *)pgphNode); } epicsMutexUnlock(pgphPvt->lock); return pgphNode; } - + +GPHENTRY * epicsShareAPI gphFind(gphPvt *pgphPvt, const char *name, void *pvtid) +{ + return gphFindParse(pgphPvt, name, strlen(name), pvtid); +} + GPHENTRY * epicsShareAPI gphAdd(gphPvt *pgphPvt, const char *name, void *pvtid) { ELLLIST **paplist; @@ -133,7 +139,7 @@ GPHENTRY * epicsShareAPI gphAdd(gphPvt *pgphPvt, const char *name, void *pvtid) epicsMutexUnlock(pgphPvt->lock); return (pgphNode); } - + void epicsShareAPI gphDelete(gphPvt *pgphPvt, const char *name, void *pvtid) { ELLLIST **paplist; @@ -167,7 +173,7 @@ void epicsShareAPI gphDelete(gphPvt *pgphPvt, const char *name, void *pvtid) epicsMutexUnlock(pgphPvt->lock); return; } - + void epicsShareAPI gphFreeMem(gphPvt *pgphPvt) { ELLLIST **paplist; diff --git a/src/libCom/yajl/Makefile b/src/libCom/yajl/Makefile new file mode 100644 index 000000000..f6b58efe6 --- /dev/null +++ b/src/libCom/yajl/Makefile @@ -0,0 +1,26 @@ +#************************************************************************* +# Copyright (c) 2002 The University of Chicago, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/yacc + +# Yet Another JSON Library +SRC_DIRS += $(LIBCOM)/yajl +INC += yajl_common.h +INC += yajl_gen.h +INC += yajl_parse.h +# The other yajl_*.h files are for internal use only +SRCS += yajl.c +SRCS += yajl_alloc.c +SRCS += yajl_buf.c +SRCS += yajl_encode.c +SRCS += yajl_gen.c +SRCS += yajl_lex.c +SRCS += yajl_parser.c diff --git a/src/libCom/yajl/yajl.c b/src/libCom/yajl/yajl.c new file mode 100644 index 000000000..f353598d8 --- /dev/null +++ b/src/libCom/yajl/yajl.c @@ -0,0 +1,160 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "yajl_parse.h" +#include "yajl_lex.h" +#include "yajl_parser.h" +#include "yajl_alloc.h" + +const char * +yajl_status_to_string(yajl_status stat) +{ + const char * statStr = "unknown"; + switch (stat) { + case yajl_status_ok: + statStr = "ok, no error"; + break; + case yajl_status_client_canceled: + statStr = "client canceled parse"; + break; + case yajl_status_insufficient_data: + statStr = "eof was met before the parse could complete"; + break; + case yajl_status_error: + statStr = "parse error"; + break; + } + return statStr; +} + +yajl_handle +yajl_alloc(const yajl_callbacks * callbacks, + const yajl_parser_config * config, + const yajl_alloc_funcs * afs, + void * ctx) +{ + unsigned int allowComments = 0; + unsigned int validateUTF8 = 0; + yajl_handle hand = NULL; + yajl_alloc_funcs afsBuffer; + + /* first order of business is to set up memory allocation routines */ + if (afs != NULL) { + if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL) + { + return NULL; + } + } else { + yajl_set_default_alloc_funcs(&afsBuffer); + afs = &afsBuffer; + } + + hand = (yajl_handle) YA_MALLOC(afs, sizeof(struct yajl_handle_t)); + + /* copy in pointers to allocation routines */ + memcpy((void *) &(hand->alloc), (void *) afs, sizeof(yajl_alloc_funcs)); + + if (config != NULL) { + allowComments = config->allowComments; + validateUTF8 = config->checkUTF8; + } + + hand->callbacks = callbacks; + hand->ctx = ctx; + hand->lexer = yajl_lex_alloc(&(hand->alloc), allowComments, validateUTF8); + hand->bytesConsumed = 0; + hand->decodeBuf = yajl_buf_alloc(&(hand->alloc)); + yajl_bs_init(hand->stateStack, &(hand->alloc)); + + yajl_bs_push(hand->stateStack, yajl_state_start); + + return hand; +} + +void +yajl_free(yajl_handle handle) +{ + yajl_bs_free(handle->stateStack); + yajl_buf_free(handle->decodeBuf); + yajl_lex_free(handle->lexer); + YA_FREE(&(handle->alloc), handle); +} + +yajl_status +yajl_parse(yajl_handle hand, const unsigned char * jsonText, + unsigned int jsonTextLen) +{ + yajl_status status; + status = yajl_do_parse(hand, jsonText, jsonTextLen); + return status; +} + +yajl_status +yajl_parse_complete(yajl_handle hand) +{ + /* The particular case we want to handle is a trailing number. + * Further input consisting of digits could cause our interpretation + * of the number to change (buffered "1" but "2" comes in). + * A very simple approach to this is to inject whitespace to terminate + * any number in the lex buffer. + */ + return yajl_parse(hand, (const unsigned char *)" ", 1); +} + +unsigned char * +yajl_get_error(yajl_handle hand, int verbose, + const unsigned char * jsonText, unsigned int jsonTextLen) +{ + return yajl_render_error_string(hand, jsonText, jsonTextLen, verbose); +} + +unsigned int +yajl_get_bytes_consumed(yajl_handle hand) +{ + if (!hand) return 0; + else return hand->bytesConsumed; +} + + +void +yajl_free_error(yajl_handle hand, unsigned char * str) +{ + /* use memory allocation functions if set */ + YA_FREE(&(hand->alloc), str); +} + +/* XXX: add utility routines to parse from file */ diff --git a/src/libCom/yajl/yajl_alloc.c b/src/libCom/yajl/yajl_alloc.c new file mode 100644 index 000000000..5b2544ecd --- /dev/null +++ b/src/libCom/yajl/yajl_alloc.c @@ -0,0 +1,67 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file yajl_alloc.h + * default memory allocation routines for yajl which use malloc/realloc and + * free + */ + +#include + +#define epicsExportSharedSymbols +#include "yajl_alloc.h" + +static void * yajl_internal_malloc(void *ctx, unsigned int sz) +{ + return malloc(sz); +} + +static void * yajl_internal_realloc(void *ctx, void * previous, + unsigned int sz) +{ + return realloc(previous, sz); +} + +static void yajl_internal_free(void *ctx, void * ptr) +{ + free(ptr); +} + +void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf) +{ + yaf->malloc = yajl_internal_malloc; + yaf->free = yajl_internal_free; + yaf->realloc = yajl_internal_realloc; + yaf->ctx = NULL; +} + diff --git a/src/libCom/yajl/yajl_alloc.h b/src/libCom/yajl/yajl_alloc.h new file mode 100644 index 000000000..988a6c50a --- /dev/null +++ b/src/libCom/yajl/yajl_alloc.h @@ -0,0 +1,50 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file yajl_alloc.h + * default memory allocation routines for yajl which use malloc/realloc and + * free + */ + +#ifndef __YAJL_ALLOC_H__ +#define __YAJL_ALLOC_H__ + +#include "yajl_common.h" + +#define YA_MALLOC(afs, sz) (afs)->malloc((afs)->ctx, (sz)) +#define YA_FREE(afs, ptr) (afs)->free((afs)->ctx, (ptr)) +#define YA_REALLOC(afs, ptr, sz) (afs)->realloc((afs)->ctx, (ptr), (sz)) + +void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf); + +#endif diff --git a/src/libCom/yajl/yajl_buf.c b/src/libCom/yajl/yajl_buf.c new file mode 100644 index 000000000..3bd7ad67b --- /dev/null +++ b/src/libCom/yajl/yajl_buf.c @@ -0,0 +1,120 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "yajl_buf.h" + +#define YAJL_BUF_INIT_SIZE 2048 + +struct yajl_buf_t { + unsigned int len; + unsigned int used; + unsigned char * data; + yajl_alloc_funcs * alloc; +}; + +static +void yajl_buf_ensure_available(yajl_buf buf, unsigned int want) +{ + unsigned int need; + + assert(buf != NULL); + + /* first call */ + if (buf->data == NULL) { + buf->len = YAJL_BUF_INIT_SIZE; + buf->data = (unsigned char *) YA_MALLOC(buf->alloc, buf->len); + buf->data[0] = 0; + } + + need = buf->len; + + while (want >= (need - buf->used)) need <<= 1; + + if (need != buf->len) { + buf->data = (unsigned char *) YA_REALLOC(buf->alloc, buf->data, need); + buf->len = need; + } +} + +yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc) +{ + yajl_buf b = YA_MALLOC(alloc, sizeof(struct yajl_buf_t)); + memset((void *) b, 0, sizeof(struct yajl_buf_t)); + b->alloc = alloc; + return b; +} + +void yajl_buf_free(yajl_buf buf) +{ + assert(buf != NULL); + if (buf->data) YA_FREE(buf->alloc, buf->data); + YA_FREE(buf->alloc, buf); +} + +void yajl_buf_append(yajl_buf buf, const void * data, unsigned int len) +{ + yajl_buf_ensure_available(buf, len); + if (len > 0) { + assert(data != NULL); + memcpy(buf->data + buf->used, data, len); + buf->used += len; + buf->data[buf->used] = 0; + } +} + +void yajl_buf_clear(yajl_buf buf) +{ + buf->used = 0; + if (buf->data) buf->data[buf->used] = 0; +} + +const unsigned char * yajl_buf_data(yajl_buf buf) +{ + return buf->data; +} + +unsigned int yajl_buf_len(yajl_buf buf) +{ + return buf->used; +} + +void +yajl_buf_truncate(yajl_buf buf, unsigned int len) +{ + assert(len <= buf->used); + buf->used = len; +} diff --git a/src/libCom/yajl/yajl_buf.h b/src/libCom/yajl/yajl_buf.h new file mode 100644 index 000000000..e5b2c8231 --- /dev/null +++ b/src/libCom/yajl/yajl_buf.h @@ -0,0 +1,73 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __YAJL_BUF_H__ +#define __YAJL_BUF_H__ + +#include "yajl_common.h" +#include "yajl_alloc.h" + +/* + * Implementation/performance notes. If this were moved to a header + * only implementation using #define's where possible we might be + * able to sqeeze a little performance out of the guy by killing function + * call overhead. YMMV. + */ + +/** + * yajl_buf is a buffer with exponential growth. the buffer ensures that + * you are always null padded. + */ +typedef struct yajl_buf_t * yajl_buf; + +/* allocate a new buffer */ +yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc); + +/* free the buffer */ +void yajl_buf_free(yajl_buf buf); + +/* append a number of bytes to the buffer */ +void yajl_buf_append(yajl_buf buf, const void * data, unsigned int len); + +/* empty the buffer */ +void yajl_buf_clear(yajl_buf buf); + +/* get a pointer to the beginning of the buffer */ +const unsigned char * yajl_buf_data(yajl_buf buf); + +/* get the length of the buffer */ +unsigned int yajl_buf_len(yajl_buf buf); + +/* truncate the buffer */ +void yajl_buf_truncate(yajl_buf buf, unsigned int len); + +#endif diff --git a/src/libCom/yajl/yajl_bytestack.h b/src/libCom/yajl/yajl_bytestack.h new file mode 100644 index 000000000..9ce192fcc --- /dev/null +++ b/src/libCom/yajl/yajl_bytestack.h @@ -0,0 +1,85 @@ +/* + * Copyright 2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * A header only implementation of a simple stack of bytes, used in YAJL + * to maintain parse state. + */ + +#ifndef __YAJL_BYTESTACK_H__ +#define __YAJL_BYTESTACK_H__ + +#include "yajl_common.h" + +#define YAJL_BS_INC 128 + +typedef struct yajl_bytestack_t +{ + unsigned char * stack; + unsigned int size; + unsigned int used; + yajl_alloc_funcs * yaf; +} yajl_bytestack; + +/* initialize a bytestack */ +#define yajl_bs_init(obs, _yaf) { \ + (obs).stack = NULL; \ + (obs).size = 0; \ + (obs).used = 0; \ + (obs).yaf = (_yaf); \ + } \ + + +/* initialize a bytestack */ +#define yajl_bs_free(obs) \ + if ((obs).stack) (obs).yaf->free((obs).yaf->ctx, (obs).stack); + +#define yajl_bs_current(obs) \ + (assert((obs).used > 0), (obs).stack[(obs).used - 1]) + +#define yajl_bs_push(obs, byte) { \ + if (((obs).size - (obs).used) == 0) { \ + (obs).size += YAJL_BS_INC; \ + (obs).stack = (obs).yaf->realloc((obs).yaf->ctx,\ + (void *) (obs).stack, (obs).size);\ + } \ + (obs).stack[((obs).used)++] = (byte); \ +} + +/* removes the top item of the stack, returns nothing */ +#define yajl_bs_pop(obs) { ((obs).used)--; } + +#define yajl_bs_set(obs, byte) \ + (obs).stack[((obs).used) - 1] = (byte); + + +#endif diff --git a/src/libCom/yajl/yajl_common.h b/src/libCom/yajl/yajl_common.h new file mode 100644 index 000000000..7cb657470 --- /dev/null +++ b/src/libCom/yajl/yajl_common.h @@ -0,0 +1,76 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __YAJL_COMMON_H__ +#define __YAJL_COMMON_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define YAJL_MAX_DEPTH 128 + +#define YAJL_API epicsShareFunc + +/** pointer to a malloc function, supporting client overriding memory + * allocation routines */ +typedef void * (*yajl_malloc_func)(void *ctx, unsigned int sz); + +/** pointer to a free function, supporting client overriding memory + * allocation routines */ +typedef void (*yajl_free_func)(void *ctx, void * ptr); + +/** pointer to a realloc function which can resize an allocation. */ +typedef void * (*yajl_realloc_func)(void *ctx, void * ptr, unsigned int sz); + +/** A structure which can be passed to yajl_*_alloc routines to allow the + * client to specify memory allocation functions to be used. */ +typedef struct +{ + /** pointer to a function that can allocate uninitialized memory */ + yajl_malloc_func malloc; + /** pointer to a function that can resize memory allocations */ + yajl_realloc_func realloc; + /** pointer to a function that can free memory allocated using + * reallocFunction or mallocFunction */ + yajl_free_func free; + /** a context pointer that will be passed to above allocation routines */ + void * ctx; +} yajl_alloc_funcs; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libCom/yajl/yajl_encode.c b/src/libCom/yajl/yajl_encode.c new file mode 100644 index 000000000..0d7007da0 --- /dev/null +++ b/src/libCom/yajl/yajl_encode.c @@ -0,0 +1,189 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "yajl_encode.h" + +static void CharToHex(unsigned char c, char * hexBuf) +{ + const char * hexchar = "0123456789ABCDEF"; + hexBuf[0] = hexchar[c >> 4]; + hexBuf[1] = hexchar[c & 0x0F]; +} + +void +yajl_string_encode(yajl_buf buf, const unsigned char * str, + unsigned int len) +{ + yajl_string_encode2((const yajl_print_t) &yajl_buf_append, buf, str, len); +} + +void +yajl_string_encode2(const yajl_print_t print, + void * ctx, + const unsigned char * str, + unsigned int len) +{ + unsigned int beg = 0; + unsigned int end = 0; + char hexBuf[7]; + hexBuf[0] = '\\'; hexBuf[1] = 'u'; hexBuf[2] = '0'; hexBuf[3] = '0'; + hexBuf[6] = 0; + + while (end < len) { + const char * escaped = NULL; + switch (str[end]) { + case '\r': escaped = "\\r"; break; + case '\n': escaped = "\\n"; break; + case '\\': escaped = "\\\\"; break; + /* case '/': escaped = "\\/"; break; */ + case '"': escaped = "\\\""; break; + case '\f': escaped = "\\f"; break; + case '\b': escaped = "\\b"; break; + case '\t': escaped = "\\t"; break; + default: + if ((unsigned char) str[end] < 32) { + CharToHex(str[end], hexBuf + 4); + escaped = hexBuf; + } + break; + } + if (escaped != NULL) { + print(ctx, (const char *) (str + beg), end - beg); + print(ctx, escaped, strlen(escaped)); + beg = ++end; + } else { + ++end; + } + } + print(ctx, (const char *) (str + beg), end - beg); +} + +static void hexToDigit(unsigned int * val, const unsigned char * hex) +{ + unsigned int i; + for (i=0;i<4;i++) { + unsigned char c = hex[i]; + if (c >= 'A') c = (c & ~0x20) - 7; + c -= '0'; + assert(!(c & 0xF0)); + *val = (*val << 4) | c; + } +} + +static void Utf32toUtf8(unsigned int codepoint, char * utf8Buf) +{ + if (codepoint < 0x80) { + utf8Buf[0] = (char) codepoint; + utf8Buf[1] = 0; + } else if (codepoint < 0x0800) { + utf8Buf[0] = (char) ((codepoint >> 6) | 0xC0); + utf8Buf[1] = (char) ((codepoint & 0x3F) | 0x80); + utf8Buf[2] = 0; + } else if (codepoint < 0x10000) { + utf8Buf[0] = (char) ((codepoint >> 12) | 0xE0); + utf8Buf[1] = (char) (((codepoint >> 6) & 0x3F) | 0x80); + utf8Buf[2] = (char) ((codepoint & 0x3F) | 0x80); + utf8Buf[3] = 0; + } else if (codepoint < 0x200000) { + utf8Buf[0] =(char)((codepoint >> 18) | 0xF0); + utf8Buf[1] =(char)(((codepoint >> 12) & 0x3F) | 0x80); + utf8Buf[2] =(char)(((codepoint >> 6) & 0x3F) | 0x80); + utf8Buf[3] =(char)((codepoint & 0x3F) | 0x80); + utf8Buf[4] = 0; + } else { + utf8Buf[0] = '?'; + utf8Buf[1] = 0; + } +} + +void yajl_string_decode(yajl_buf buf, const unsigned char * str, + unsigned int len) +{ + unsigned int beg = 0; + unsigned int end = 0; + + while (end < len) { + if (str[end] == '\\') { + char utf8Buf[5]; + const char * unescaped = "?"; + yajl_buf_append(buf, str + beg, end - beg); + switch (str[++end]) { + case 'r': unescaped = "\r"; break; + case 'n': unescaped = "\n"; break; + case '\\': unescaped = "\\"; break; + case '/': unescaped = "/"; break; + case '"': unescaped = "\""; break; + case 'f': unescaped = "\f"; break; + case 'b': unescaped = "\b"; break; + case 't': unescaped = "\t"; break; + case 'u': { + unsigned int codepoint = 0; + hexToDigit(&codepoint, str + ++end); + end+=3; + /* check if this is a surrogate */ + if ((codepoint & 0xFC00) == 0xD800) { + end++; + if (str[end] == '\\' && str[end + 1] == 'u') { + unsigned int surrogate = 0; + hexToDigit(&surrogate, str + end + 2); + codepoint = + (((codepoint & 0x3F) << 10) | + ((((codepoint >> 6) & 0xF) + 1) << 16) | + (surrogate & 0x3FF)); + end += 5; + } else { + unescaped = "?"; + break; + } + } + + Utf32toUtf8(codepoint, utf8Buf); + unescaped = utf8Buf; + break; + } + default: + assert("this should never happen" == NULL); + } + yajl_buf_append(buf, unescaped, strlen(unescaped)); + beg = ++end; + } else { + end++; + } + } + yajl_buf_append(buf, str + beg, end - beg); +} diff --git a/src/libCom/yajl/yajl_encode.h b/src/libCom/yajl/yajl_encode.h new file mode 100644 index 000000000..d478baa19 --- /dev/null +++ b/src/libCom/yajl/yajl_encode.h @@ -0,0 +1,50 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __YAJL_ENCODE_H__ +#define __YAJL_ENCODE_H__ + +#include "yajl_buf.h" +#include "yajl_gen.h" + +void yajl_string_encode2(const yajl_print_t printer, + void * ctx, + const unsigned char * str, + unsigned int length); + +void yajl_string_encode(yajl_buf buf, const unsigned char * str, + unsigned int length); + +void yajl_string_decode(yajl_buf buf, const unsigned char * str, + unsigned int length); + +#endif diff --git a/src/libCom/yajl/yajl_gen.c b/src/libCom/yajl/yajl_gen.c new file mode 100644 index 000000000..a10d08297 --- /dev/null +++ b/src/libCom/yajl/yajl_gen.c @@ -0,0 +1,317 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "epicsMath.h" +#include "yajl_gen.h" +#include "yajl_buf.h" +#include "yajl_encode.h" + +typedef enum { + yajl_gen_start, + yajl_gen_map_start, + yajl_gen_map_key, + yajl_gen_map_val, + yajl_gen_array_start, + yajl_gen_in_array, + yajl_gen_complete, + yajl_gen_error +} yajl_gen_state; + +struct yajl_gen_t +{ + unsigned int depth; + unsigned int pretty; + const char * indentString; + yajl_gen_state state[YAJL_MAX_DEPTH]; + yajl_print_t print; + void * ctx; /* yajl_buf */ + /* memory allocation routines */ + yajl_alloc_funcs alloc; +}; + +yajl_gen +yajl_gen_alloc(const yajl_gen_config * config, + const yajl_alloc_funcs * afs) +{ + return yajl_gen_alloc2(NULL, config, afs, NULL); +} + +yajl_gen +yajl_gen_alloc2(const yajl_print_t callback, + const yajl_gen_config * config, + const yajl_alloc_funcs * afs, + void * ctx) +{ + yajl_gen g = NULL; + yajl_alloc_funcs afsBuffer; + + /* first order of business is to set up memory allocation routines */ + if (afs != NULL) { + if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL) + { + return NULL; + } + } else { + yajl_set_default_alloc_funcs(&afsBuffer); + afs = &afsBuffer; + } + + g = (yajl_gen) YA_MALLOC(afs, sizeof(struct yajl_gen_t)); + memset((void *) g, 0, sizeof(struct yajl_gen_t)); + /* copy in pointers to allocation routines */ + memcpy((void *) &(g->alloc), (void *) afs, sizeof(yajl_alloc_funcs)); + + if (config) { + g->pretty = config->beautify; + g->indentString = config->indentString ? config->indentString : " "; + } + + if (callback) { + g->print = callback; + g->ctx = ctx; + } else { + g->print = (yajl_print_t)&yajl_buf_append; + g->ctx = yajl_buf_alloc(&(g->alloc)); + } + + return g; +} + +void +yajl_gen_free(yajl_gen g) +{ + if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_free((yajl_buf)g->ctx); + YA_FREE(&(g->alloc), g); +} + +#define INSERT_SEP \ + if (g->state[g->depth] == yajl_gen_map_key || \ + g->state[g->depth] == yajl_gen_in_array) { \ + g->print(g->ctx, ",", 1); \ + if (g->pretty) g->print(g->ctx, "\n", 1); \ + } else if (g->state[g->depth] == yajl_gen_map_val) { \ + g->print(g->ctx, ":", 1); \ + if (g->pretty) g->print(g->ctx, " ", 1); \ + } + +#define INSERT_WHITESPACE \ + if (g->pretty) { \ + if (g->state[g->depth] != yajl_gen_map_val) { \ + unsigned int _i; \ + for (_i=0;_idepth;_i++) \ + g->print(g->ctx, g->indentString, \ + strlen(g->indentString)); \ + } \ + } + +#define ENSURE_NOT_KEY \ + if (g->state[g->depth] == yajl_gen_map_key) { \ + return yajl_gen_keys_must_be_strings; \ + } \ + +/* check that we're not complete, or in error state. in a valid state + * to be generating */ +#define ENSURE_VALID_STATE \ + if (g->state[g->depth] == yajl_gen_error) { \ + return yajl_gen_in_error_state;\ + } else if (g->state[g->depth] == yajl_gen_complete) { \ + return yajl_gen_generation_complete; \ + } + +#define INCREMENT_DEPTH \ + if (++(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded; + +#define APPENDED_ATOM \ + switch (g->state[g->depth]) { \ + case yajl_gen_start: \ + g->state[g->depth] = yajl_gen_complete; \ + break; \ + case yajl_gen_map_start: \ + case yajl_gen_map_key: \ + g->state[g->depth] = yajl_gen_map_val; \ + break; \ + case yajl_gen_array_start: \ + g->state[g->depth] = yajl_gen_in_array; \ + break; \ + case yajl_gen_map_val: \ + g->state[g->depth] = yajl_gen_map_key; \ + break; \ + default: \ + break; \ + } \ + +#define FINAL_NEWLINE \ + if (g->pretty && g->state[g->depth] == yajl_gen_complete) \ + g->print(g->ctx, "\n", 1); + +yajl_gen_status +yajl_gen_integer(yajl_gen g, long int number) +{ + char i[32]; + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + sprintf(i, "%ld", number); + g->print(g->ctx, i, strlen(i)); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_double(yajl_gen g, double number) +{ + char i[32]; + ENSURE_VALID_STATE; ENSURE_NOT_KEY; + if (isnan(number) || isinf(number)) return yajl_gen_invalid_number; + INSERT_SEP; INSERT_WHITESPACE; + sprintf(i, "%g", number); + g->print(g->ctx, i, strlen(i)); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_number(yajl_gen g, const char * s, unsigned int l) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + g->print(g->ctx, s, l); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_string(yajl_gen g, const unsigned char * str, + unsigned int len) +{ + ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE; + g->print(g->ctx, "\"", 1); + yajl_string_encode2(g->print, g->ctx, str, len); + g->print(g->ctx, "\"", 1); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_null(yajl_gen g) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + g->print(g->ctx, "null", strlen("null")); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_bool(yajl_gen g, int boolean) +{ + const char * val = boolean ? "true" : "false"; + + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + g->print(g->ctx, val, strlen(val)); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_map_open(yajl_gen g) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + INCREMENT_DEPTH; + + g->state[g->depth] = yajl_gen_map_start; + g->print(g->ctx, "{", 1); + if (g->pretty) g->print(g->ctx, "\n", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_map_close(yajl_gen g) +{ + ENSURE_VALID_STATE; + (g->depth)--; + if (g->pretty) g->print(g->ctx, "\n", 1); + APPENDED_ATOM; + INSERT_WHITESPACE; + g->print(g->ctx, "}", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_array_open(yajl_gen g) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + INCREMENT_DEPTH; + g->state[g->depth] = yajl_gen_array_start; + g->print(g->ctx, "[", 1); + if (g->pretty) g->print(g->ctx, "\n", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_array_close(yajl_gen g) +{ + ENSURE_VALID_STATE; + if (g->pretty) g->print(g->ctx, "\n", 1); + (g->depth)--; + APPENDED_ATOM; + INSERT_WHITESPACE; + g->print(g->ctx, "]", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_get_buf(yajl_gen g, const unsigned char ** buf, + unsigned int * len) +{ + if (g->print != (yajl_print_t)&yajl_buf_append) return yajl_gen_no_buf; + *buf = yajl_buf_data((yajl_buf)g->ctx); + *len = yajl_buf_len((yajl_buf)g->ctx); + return yajl_gen_status_ok; +} + +void +yajl_gen_clear(yajl_gen g) +{ + if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_clear((yajl_buf)g->ctx); +} diff --git a/src/libCom/yajl/yajl_gen.h b/src/libCom/yajl/yajl_gen.h new file mode 100644 index 000000000..8efabd073 --- /dev/null +++ b/src/libCom/yajl/yajl_gen.h @@ -0,0 +1,159 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file yajl_gen.h + * Interface to YAJL's JSON generation facilities. + */ + +#ifndef __YAJL_GEN_H__ +#define __YAJL_GEN_H__ + +#include "yajl_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + /** generator status codes */ + typedef enum { + /** no error */ + yajl_gen_status_ok = 0, + /** at a point where a map key is generated, a function other than + * yajl_gen_string was called */ + yajl_gen_keys_must_be_strings, + /** YAJL's maximum generation depth was exceeded. see + * YAJL_MAX_DEPTH */ + yajl_max_depth_exceeded, + /** A generator function (yajl_gen_XXX) was called while in an error + * state */ + yajl_gen_in_error_state, + /** A complete JSON document has been generated */ + yajl_gen_generation_complete, + /** yajl_gen_double was passed an invalid floating point value + * (infinity or NaN). */ + yajl_gen_invalid_number, + /** A print callback was passed in, so there is no internal + * buffer to get from */ + yajl_gen_no_buf + } yajl_gen_status; + + /** an opaque handle to a generator */ + typedef struct yajl_gen_t * yajl_gen; + + /** a callback used for "printing" the results. */ + typedef void (*yajl_print_t)(void * ctx, + const char * str, + unsigned int len); + + /** configuration structure for the generator */ + typedef struct { + /** generate indented (beautiful) output */ + unsigned int beautify; + /** an opportunity to define an indent string. such as \\t or + * some number of spaces. default is four spaces ' '. This + * member is only relevant when beautify is true */ + const char * indentString; + } yajl_gen_config; + + /** allocate a generator handle + * \param config a pointer to a structure containing parameters which + * configure the behavior of the json generator + * \param allocFuncs an optional pointer to a structure which allows + * the client to overide the memory allocation + * used by yajl. May be NULL, in which case + * malloc/free/realloc will be used. + * + * \returns an allocated handle on success, NULL on failure (bad params) + */ + YAJL_API yajl_gen yajl_gen_alloc(const yajl_gen_config * config, + const yajl_alloc_funcs * allocFuncs); + + /** allocate a generator handle that will print to the specified + * callback rather than storing the results in an internal buffer. + * \param callback a pointer to a printer function. May be NULL + * in which case, the results will be store in an + * internal buffer. + * \param config a pointer to a structure containing parameters + * which configure the behavior of the json + * generator. + * \param allocFuncs an optional pointer to a structure which allows + * the client to overide the memory allocation + * used by yajl. May be NULL, in which case + * malloc/free/realloc will be used. + * \param ctx a context pointer that will be passed to the + * printer callback. + * + * \returns an allocated handle on success, NULL on failure (bad params) + */ + YAJL_API yajl_gen yajl_gen_alloc2(const yajl_print_t callback, + const yajl_gen_config * config, + const yajl_alloc_funcs * allocFuncs, + void * ctx); + + /** free a generator handle */ + YAJL_API void yajl_gen_free(yajl_gen handle); + + YAJL_API yajl_gen_status yajl_gen_integer(yajl_gen hand, long int number); + /** generate a floating point number. number may not be infinity or + * NaN, as these have no representation in JSON. In these cases the + * generator will return 'yajl_gen_invalid_number' */ + YAJL_API yajl_gen_status yajl_gen_double(yajl_gen hand, double number); + YAJL_API yajl_gen_status yajl_gen_number(yajl_gen hand, + const char * num, + unsigned int len); + YAJL_API yajl_gen_status yajl_gen_string(yajl_gen hand, + const unsigned char * str, + unsigned int len); + YAJL_API yajl_gen_status yajl_gen_null(yajl_gen hand); + YAJL_API yajl_gen_status yajl_gen_bool(yajl_gen hand, int boolean); + YAJL_API yajl_gen_status yajl_gen_map_open(yajl_gen hand); + YAJL_API yajl_gen_status yajl_gen_map_close(yajl_gen hand); + YAJL_API yajl_gen_status yajl_gen_array_open(yajl_gen hand); + YAJL_API yajl_gen_status yajl_gen_array_close(yajl_gen hand); + + /** access the null terminated generator buffer. If incrementally + * outputing JSON, one should call yajl_gen_clear to clear the + * buffer. This allows stream generation. */ + YAJL_API yajl_gen_status yajl_gen_get_buf(yajl_gen hand, + const unsigned char ** buf, + unsigned int * len); + + /** clear yajl's output buffer, but maintain all internal generation + * state. This function will not "reset" the generator state, and is + * intended to enable incremental JSON outputing. */ + YAJL_API void yajl_gen_clear(yajl_gen hand); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libCom/yajl/yajl_lex.c b/src/libCom/yajl/yajl_lex.c new file mode 100644 index 000000000..be83887ff --- /dev/null +++ b/src/libCom/yajl/yajl_lex.c @@ -0,0 +1,738 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "yajl_lex.h" +#include "yajl_buf.h" + +#ifdef YAJL_LEXER_DEBUG +static const char * +tokToStr(yajl_tok tok) +{ + switch (tok) { + case yajl_tok_bool: return "bool"; + case yajl_tok_colon: return "colon"; + case yajl_tok_comma: return "comma"; + case yajl_tok_eof: return "eof"; + case yajl_tok_error: return "error"; + case yajl_tok_left_brace: return "brace"; + case yajl_tok_left_bracket: return "bracket"; + case yajl_tok_null: return "null"; + case yajl_tok_integer: return "integer"; + case yajl_tok_double: return "double"; + case yajl_tok_right_brace: return "brace"; + case yajl_tok_right_bracket: return "bracket"; + case yajl_tok_string: return "string"; + case yajl_tok_string_with_escapes: return "string_with_escapes"; + } + return "unknown"; +} +#endif + +/* Impact of the stream parsing feature on the lexer: + * + * YAJL support stream parsing. That is, the ability to parse the first + * bits of a chunk of JSON before the last bits are available (still on + * the network or disk). This makes the lexer more complex. The + * responsibility of the lexer is to handle transparently the case where + * a chunk boundary falls in the middle of a token. This is + * accomplished is via a buffer and a character reading abstraction. + * + * Overview of implementation + * + * When we lex to end of input string before end of token is hit, we + * copy all of the input text composing the token into our lexBuf. + * + * Every time we read a character, we do so through the readChar function. + * readChar's responsibility is to handle pulling all chars from the buffer + * before pulling chars from input text + */ + +struct yajl_lexer_t { + /* the overal line and char offset into the data */ + unsigned int lineOff; + unsigned int charOff; + + /* error */ + yajl_lex_error error; + + /* a input buffer to handle the case where a token is spread over + * multiple chunks */ + yajl_buf buf; + + /* in the case where we have data in the lexBuf, bufOff holds + * the current offset into the lexBuf. */ + unsigned int bufOff; + + /* are we using the lex buf? */ + unsigned int bufInUse; + + /* shall we allow comments? */ + unsigned int allowComments; + + /* shall we validate utf8 inside strings? */ + unsigned int validateUTF8; + + yajl_alloc_funcs * alloc; +}; + +#define readChar(lxr, txt, off) \ + (((lxr)->bufInUse && yajl_buf_len((lxr)->buf) && lxr->bufOff < yajl_buf_len((lxr)->buf)) ? \ + (*((const unsigned char *) yajl_buf_data((lxr)->buf) + ((lxr)->bufOff)++)) : \ + ((txt)[(*(off))++])) + +#define unreadChar(lxr, off) ((*(off) > 0) ? (*(off))-- : ((lxr)->bufOff--)) + +yajl_lexer +yajl_lex_alloc(yajl_alloc_funcs * alloc, + unsigned int allowComments, unsigned int validateUTF8) +{ + yajl_lexer lxr = (yajl_lexer) YA_MALLOC(alloc, sizeof(struct yajl_lexer_t)); + memset((void *) lxr, 0, sizeof(struct yajl_lexer_t)); + lxr->buf = yajl_buf_alloc(alloc); + lxr->allowComments = allowComments; + lxr->validateUTF8 = validateUTF8; + lxr->alloc = alloc; + return lxr; +} + +void +yajl_lex_free(yajl_lexer lxr) +{ + yajl_buf_free(lxr->buf); + YA_FREE(lxr->alloc, lxr); + return; +} + +/* a lookup table which lets us quickly determine three things: + * VEC - valid escaped conrol char + * IJC - invalid json char + * VHC - valid hex char + * note. the solidus '/' may be escaped or not. + * note. the + */ +#define VEC 1 +#define IJC 2 +#define VHC 4 +static const char charLookupTable[256] = +{ +/*00*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , +/*08*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , +/*10*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , +/*18*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , + +/*20*/ 0 , 0 , VEC|IJC, 0 , 0 , 0 , 0 , 0 , +/*28*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , VEC , +/*30*/ VHC , VHC , VHC , VHC , VHC , VHC , VHC , VHC , +/*38*/ VHC , VHC , 0 , 0 , 0 , 0 , 0 , 0 , + +/*40*/ 0 , VHC , VHC , VHC , VHC , VHC , VHC , 0 , +/*48*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , +/*50*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , +/*58*/ 0 , 0 , 0 , 0 , VEC|IJC, 0 , 0 , 0 , + +/*60*/ 0 , VHC , VEC|VHC, VHC , VHC , VHC , VEC|VHC, 0 , +/*68*/ 0 , 0 , 0 , 0 , 0 , 0 , VEC , 0 , +/*70*/ 0 , 0 , VEC , 0 , VEC , 0 , 0 , 0 , +/*78*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + +/* include these so we don't have to always check the range of the char */ + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 +}; + +/** process a variable length utf8 encoded codepoint. + * + * returns: + * yajl_tok_string - if valid utf8 char was parsed and offset was + * advanced + * yajl_tok_eof - if end of input was hit before validation could + * complete + * yajl_tok_error - if invalid utf8 was encountered + * + * NOTE: on error the offset will point to the first char of the + * invalid utf8 */ +#define UTF8_CHECK_EOF if (*offset >= jsonTextLen) { return yajl_tok_eof; } + +static yajl_tok +yajl_lex_utf8_char(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * offset, + unsigned char curChar) +{ + if (curChar <= 0x7f) { + /* single byte */ + return yajl_tok_string; + } else if ((curChar >> 5) == 0x6) { + /* two byte */ + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) return yajl_tok_string; + } else if ((curChar >> 4) == 0x0e) { + /* three byte */ + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) { + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) return yajl_tok_string; + } + } else if ((curChar >> 3) == 0x1e) { + /* four byte */ + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) { + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) { + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) return yajl_tok_string; + } + } + } + + return yajl_tok_error; +} + +/* lex a string. input is the lexer, pointer to beginning of + * json text, and start of string (offset). + * a token is returned which has the following meanings: + * yajl_tok_string: lex of string was successful. offset points to + * terminating '"'. + * yajl_tok_eof: end of text was encountered before we could complete + * the lex. + * yajl_tok_error: embedded in the string were unallowable chars. offset + * points to the offending char + */ +#define STR_CHECK_EOF \ +if (*offset >= jsonTextLen) { \ + tok = yajl_tok_eof; \ + goto finish_string_lex; \ +} + +static yajl_tok +yajl_lex_string(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * offset) +{ + yajl_tok tok = yajl_tok_error; + int hasEscapes = 0; + + for (;;) { + unsigned char curChar; + + STR_CHECK_EOF; + + curChar = readChar(lexer, jsonText, offset); + + /* quote terminates */ + if (curChar == '"') { + tok = yajl_tok_string; + break; + } + /* backslash escapes a set of control chars, */ + else if (curChar == '\\') { + hasEscapes = 1; + STR_CHECK_EOF; + + /* special case \u */ + curChar = readChar(lexer, jsonText, offset); + if (curChar == 'u') { + unsigned int i = 0; + + for (i=0;i<4;i++) { + STR_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if (!(charLookupTable[curChar] & VHC)) { + /* back up to offending char */ + unreadChar(lexer, offset); + lexer->error = yajl_lex_string_invalid_hex_char; + goto finish_string_lex; + } + } + } else if (!(charLookupTable[curChar] & VEC)) { + /* back up to offending char */ + unreadChar(lexer, offset); + lexer->error = yajl_lex_string_invalid_escaped_char; + goto finish_string_lex; + } + } + /* when not validating UTF8 it's a simple table lookup to determine + * if the present character is invalid */ + else if(charLookupTable[curChar] & IJC) { + /* back up to offending char */ + unreadChar(lexer, offset); + lexer->error = yajl_lex_string_invalid_json_char; + goto finish_string_lex; + } + /* when in validate UTF8 mode we need to do some extra work */ + else if (lexer->validateUTF8) { + yajl_tok t = yajl_lex_utf8_char(lexer, jsonText, jsonTextLen, + offset, curChar); + + if (t == yajl_tok_eof) { + tok = yajl_tok_eof; + goto finish_string_lex; + } else if (t == yajl_tok_error) { + lexer->error = yajl_lex_string_invalid_utf8; + goto finish_string_lex; + } + } + /* accept it, and move on */ + } + finish_string_lex: + /* tell our buddy, the parser, wether he needs to process this string + * again */ + if (hasEscapes && tok == yajl_tok_string) { + tok = yajl_tok_string_with_escapes; + } + + return tok; +} + +#define RETURN_IF_EOF if (*offset >= jsonTextLen) return yajl_tok_eof; + +static yajl_tok +yajl_lex_number(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * offset) +{ + /** XXX: numbers are the only entities in json that we must lex + * _beyond_ in order to know that they are complete. There + * is an ambiguous case for integers at EOF. */ + + unsigned char c; + + yajl_tok tok = yajl_tok_integer; + + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + /* optional leading minus */ + if (c == '-') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } + + /* a single zero, or a series of integers */ + if (c == '0') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } else if (c >= '1' && c <= '9') { + do { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } while (c >= '0' && c <= '9'); + } else { + unreadChar(lexer, offset); + lexer->error = yajl_lex_missing_integer_after_minus; + return yajl_tok_error; + } + + /* optional fraction (indicates this is floating point) */ + if (c == '.') { + int numRd = 0; + + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + while (c >= '0' && c <= '9') { + numRd++; + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } + + if (!numRd) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_missing_integer_after_decimal; + return yajl_tok_error; + } + tok = yajl_tok_double; + } + + /* optional exponent (indicates this is floating point) */ + if (c == 'e' || c == 'E') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + /* optional sign */ + if (c == '+' || c == '-') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } + + if (c >= '0' && c <= '9') { + do { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } while (c >= '0' && c <= '9'); + } else { + unreadChar(lexer, offset); + lexer->error = yajl_lex_missing_integer_after_exponent; + return yajl_tok_error; + } + tok = yajl_tok_double; + } + + /* we always go "one too far" */ + unreadChar(lexer, offset); + + return tok; +} + +static yajl_tok +yajl_lex_comment(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * offset) +{ + unsigned char c; + + yajl_tok tok = yajl_tok_comment; + + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + /* either slash or star expected */ + if (c == '/') { + /* now we throw away until end of line */ + do { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } while (c != '\n'); + } else if (c == '*') { + /* now we throw away until end of comment */ + for (;;) { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + if (c == '*') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + if (c == '/') { + break; + } else { + unreadChar(lexer, offset); + } + } + } + } else { + lexer->error = yajl_lex_invalid_char; + tok = yajl_tok_error; + } + + return tok; +} + +yajl_tok +yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * offset, + const unsigned char ** outBuf, unsigned int * outLen) +{ + yajl_tok tok = yajl_tok_error; + unsigned char c; + unsigned int startOffset = *offset; + + *outBuf = NULL; + *outLen = 0; + + for (;;) { + assert(*offset <= jsonTextLen); + + if (*offset >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + + c = readChar(lexer, jsonText, offset); + + switch (c) { + case '{': + tok = yajl_tok_left_bracket; + goto lexed; + case '}': + tok = yajl_tok_right_bracket; + goto lexed; + case '[': + tok = yajl_tok_left_brace; + goto lexed; + case ']': + tok = yajl_tok_right_brace; + goto lexed; + case ',': + tok = yajl_tok_comma; + goto lexed; + case ':': + tok = yajl_tok_colon; + goto lexed; + case '\t': case '\n': case '\v': case '\f': case '\r': case ' ': + startOffset++; + break; + case 't': { + const char * want = "rue"; + do { + if (*offset >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + c = readChar(lexer, jsonText, offset); + if (c != *want) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_invalid_string; + tok = yajl_tok_error; + goto lexed; + } + } while (*(++want)); + tok = yajl_tok_bool; + goto lexed; + } + case 'f': { + const char * want = "alse"; + do { + if (*offset >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + c = readChar(lexer, jsonText, offset); + if (c != *want) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_invalid_string; + tok = yajl_tok_error; + goto lexed; + } + } while (*(++want)); + tok = yajl_tok_bool; + goto lexed; + } + case 'n': { + const char * want = "ull"; + do { + if (*offset >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + c = readChar(lexer, jsonText, offset); + if (c != *want) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_invalid_string; + tok = yajl_tok_error; + goto lexed; + } + } while (*(++want)); + tok = yajl_tok_null; + goto lexed; + } + case '"': { + tok = yajl_lex_string(lexer, (const unsigned char *) jsonText, + jsonTextLen, offset); + goto lexed; + } + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + /* integer parsing wants to start from the beginning */ + unreadChar(lexer, offset); + tok = yajl_lex_number(lexer, (const unsigned char *) jsonText, + jsonTextLen, offset); + goto lexed; + } + case '/': + /* hey, look, a probable comment! If comments are disabled + * it's an error. */ + if (!lexer->allowComments) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_unallowed_comment; + tok = yajl_tok_error; + goto lexed; + } + /* if comments are enabled, then we should try to lex + * the thing. possible outcomes are + * - successful lex (tok_comment, which means continue), + * - malformed comment opening (slash not followed by + * '*' or '/') (tok_error) + * - eof hit. (tok_eof) */ + tok = yajl_lex_comment(lexer, (const unsigned char *) jsonText, + jsonTextLen, offset); + if (tok == yajl_tok_comment) { + /* "error" is silly, but that's the initial + * state of tok. guilty until proven innocent. */ + tok = yajl_tok_error; + yajl_buf_clear(lexer->buf); + lexer->bufInUse = 0; + startOffset = *offset; + break; + } + /* hit error or eof, bail */ + goto lexed; + default: + lexer->error = yajl_lex_invalid_char; + tok = yajl_tok_error; + goto lexed; + } + } + + + lexed: + /* need to append to buffer if the buffer is in use or + * if it's an EOF token */ + if (tok == yajl_tok_eof || lexer->bufInUse) { + if (!lexer->bufInUse) yajl_buf_clear(lexer->buf); + lexer->bufInUse = 1; + yajl_buf_append(lexer->buf, jsonText + startOffset, *offset - startOffset); + lexer->bufOff = 0; + + if (tok != yajl_tok_eof) { + *outBuf = yajl_buf_data(lexer->buf); + *outLen = yajl_buf_len(lexer->buf); + lexer->bufInUse = 0; + } + } else if (tok != yajl_tok_error) { + *outBuf = jsonText + startOffset; + *outLen = *offset - startOffset; + } + + /* special case for strings. skip the quotes. */ + if (tok == yajl_tok_string || tok == yajl_tok_string_with_escapes) + { + assert(*outLen >= 2); + (*outBuf)++; + *outLen -= 2; + } + + +#ifdef YAJL_LEXER_DEBUG + if (tok == yajl_tok_error) { + printf("lexical error: %s\n", + yajl_lex_error_to_string(yajl_lex_get_error(lexer))); + } else if (tok == yajl_tok_eof) { + printf("EOF hit\n"); + } else { + printf("lexed %s: '", tokToStr(tok)); + fwrite(*outBuf, 1, *outLen, stdout); + printf("'\n"); + } +#endif + + return tok; +} + +const char * +yajl_lex_error_to_string(yajl_lex_error error) +{ + switch (error) { + case yajl_lex_e_ok: + return "ok, no error"; + case yajl_lex_string_invalid_utf8: + return "invalid bytes in UTF8 string."; + case yajl_lex_string_invalid_escaped_char: + return "inside a string, '\\' occurs before a character " + "which it may not."; + case yajl_lex_string_invalid_json_char: + return "invalid character inside string."; + case yajl_lex_string_invalid_hex_char: + return "invalid (non-hex) character occurs after '\\u' inside " + "string."; + case yajl_lex_invalid_char: + return "invalid char in json text."; + case yajl_lex_invalid_string: + return "invalid string in json text."; + case yajl_lex_missing_integer_after_exponent: + return "malformed number, a digit is required after the exponent."; + case yajl_lex_missing_integer_after_decimal: + return "malformed number, a digit is required after the " + "decimal point."; + case yajl_lex_missing_integer_after_minus: + return "malformed number, a digit is required after the " + "minus sign."; + case yajl_lex_unallowed_comment: + return "probable comment found in input text, comments are " + "not enabled."; + } + return "unknown error code"; +} + + +/** allows access to more specific information about the lexical + * error when yajl_lex_lex returns yajl_tok_error. */ +yajl_lex_error +yajl_lex_get_error(yajl_lexer lexer) +{ + if (lexer == NULL) return (yajl_lex_error) -1; + return lexer->error; +} + +unsigned int yajl_lex_current_line(yajl_lexer lexer) +{ + return lexer->lineOff; +} + +unsigned int yajl_lex_current_char(yajl_lexer lexer) +{ + return lexer->charOff; +} + +yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int offset) +{ + const unsigned char * outBuf; + unsigned int outLen; + unsigned int bufLen = yajl_buf_len(lexer->buf); + unsigned int bufOff = lexer->bufOff; + unsigned int bufInUse = lexer->bufInUse; + yajl_tok tok; + + tok = yajl_lex_lex(lexer, jsonText, jsonTextLen, &offset, + &outBuf, &outLen); + + lexer->bufOff = bufOff; + lexer->bufInUse = bufInUse; + yajl_buf_truncate(lexer->buf, bufLen); + + return tok; +} diff --git a/src/libCom/yajl/yajl_lex.h b/src/libCom/yajl/yajl_lex.h new file mode 100644 index 000000000..658885241 --- /dev/null +++ b/src/libCom/yajl/yajl_lex.h @@ -0,0 +1,133 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __YAJL_LEX_H__ +#define __YAJL_LEX_H__ + +#include "yajl_common.h" + +typedef enum { + yajl_tok_bool, + yajl_tok_colon, + yajl_tok_comma, + yajl_tok_eof, + yajl_tok_error, + yajl_tok_left_brace, + yajl_tok_left_bracket, + yajl_tok_null, + yajl_tok_right_brace, + yajl_tok_right_bracket, + + /* we differentiate between integers and doubles to allow the + * parser to interpret the number without re-scanning */ + yajl_tok_integer, + yajl_tok_double, + + /* we differentiate between strings which require further processing, + * and strings that do not */ + yajl_tok_string, + yajl_tok_string_with_escapes, + + /* comment tokens are not currently returned to the parser, ever */ + yajl_tok_comment +} yajl_tok; + +typedef struct yajl_lexer_t * yajl_lexer; + +yajl_lexer yajl_lex_alloc(yajl_alloc_funcs * alloc, + unsigned int allowComments, + unsigned int validateUTF8); + +void yajl_lex_free(yajl_lexer lexer); + +/** + * run/continue a lex. "offset" is an input/output parameter. + * It should be initialized to zero for a + * new chunk of target text, and upon subsetquent calls with the same + * target text should passed with the value of the previous invocation. + * + * the client may be interested in the value of offset when an error is + * returned from the lexer. This allows the client to render useful +n * error messages. + * + * When you pass the next chunk of data, context should be reinitialized + * to zero. + * + * Finally, the output buffer is usually just a pointer into the jsonText, + * however in cases where the entity being lexed spans multiple chunks, + * the lexer will buffer the entity and the data returned will be + * a pointer into that buffer. + * + * This behavior is abstracted from client code except for the performance + * implications which require that the client choose a reasonable chunk + * size to get adequate performance. + */ +yajl_tok yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * offset, + const unsigned char ** outBuf, unsigned int * outLen); + +/** have a peek at the next token, but don't move the lexer forward */ +yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int offset); + + +typedef enum { + yajl_lex_e_ok = 0, + yajl_lex_string_invalid_utf8, + yajl_lex_string_invalid_escaped_char, + yajl_lex_string_invalid_json_char, + yajl_lex_string_invalid_hex_char, + yajl_lex_invalid_char, + yajl_lex_invalid_string, + yajl_lex_missing_integer_after_decimal, + yajl_lex_missing_integer_after_exponent, + yajl_lex_missing_integer_after_minus, + yajl_lex_unallowed_comment +} yajl_lex_error; + +const char * yajl_lex_error_to_string(yajl_lex_error error); + +/** allows access to more specific information about the lexical + * error when yajl_lex_lex returns yajl_tok_error. */ +yajl_lex_error yajl_lex_get_error(yajl_lexer lexer); + +/** get the current offset into the most recently lexed json string. */ +unsigned int yajl_lex_current_offset(yajl_lexer lexer); + +/** get the number of lines lexed by this lexer instance */ +unsigned int yajl_lex_current_line(yajl_lexer lexer); + +/** get the number of chars lexed by this lexer instance since the last + * \n or \r */ +unsigned int yajl_lex_current_char(yajl_lexer lexer); + +#endif diff --git a/src/libCom/yajl/yajl_parse.h b/src/libCom/yajl/yajl_parse.h new file mode 100644 index 000000000..b4de64bbd --- /dev/null +++ b/src/libCom/yajl/yajl_parse.h @@ -0,0 +1,193 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file yajl_parse.h + * Interface to YAJL's JSON parsing facilities. + */ + +#ifndef __YAJL_PARSE_H__ +#define __YAJL_PARSE_H__ + +#include "yajl_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + /** error codes returned from this interface */ + typedef enum { + /** no error was encountered */ + yajl_status_ok, + /** a client callback returned zero, stopping the parse */ + yajl_status_client_canceled, + /** The parse cannot yet complete because more json input text + * is required, call yajl_parse with the next buffer of input text. + * (pertinent only when stream parsing) */ + yajl_status_insufficient_data, + /** An error occured during the parse. Call yajl_get_error for + * more information about the encountered error */ + yajl_status_error + } yajl_status; + + /** attain a human readable, english, string for an error */ + YAJL_API const char * yajl_status_to_string(yajl_status code); + + /** an opaque handle to a parser */ + typedef struct yajl_handle_t * yajl_handle; + + /** yajl is an event driven parser. this means as json elements are + * parsed, you are called back to do something with the data. The + * functions in this table indicate the various events for which + * you will be called back. Each callback accepts a "context" + * pointer, this is a void * that is passed into the yajl_parse + * function which the client code may use to pass around context. + * + * All callbacks return an integer. If non-zero, the parse will + * continue. If zero, the parse will be canceled and + * yajl_status_client_canceled will be returned from the parse. + * + * Note about handling of numbers: + * yajl will only convert numbers that can be represented in a double + * or a long int. All other numbers will be passed to the client + * in string form using the yajl_number callback. Furthermore, if + * yajl_number is not NULL, it will always be used to return numbers, + * that is yajl_integer and yajl_double will be ignored. If + * yajl_number is NULL but one of yajl_integer or yajl_double are + * defined, parsing of a number larger than is representable + * in a double or long int will result in a parse error. + */ + typedef struct { + int (* yajl_null)(void * ctx); + int (* yajl_boolean)(void * ctx, int boolVal); + int (* yajl_integer)(void * ctx, long integerVal); + int (* yajl_double)(void * ctx, double doubleVal); + /** A callback which passes the string representation of the number + * back to the client. Will be used for all numbers when present */ + int (* yajl_number)(void * ctx, const char * numberVal, + unsigned int numberLen); + + /** strings are returned as pointers into the JSON text when, + * possible, as a result, they are _not_ null padded */ + int (* yajl_string)(void * ctx, const unsigned char * stringVal, + unsigned int stringLen); + + int (* yajl_start_map)(void * ctx); + int (* yajl_map_key)(void * ctx, const unsigned char * key, + unsigned int stringLen); + int (* yajl_end_map)(void * ctx); + + int (* yajl_start_array)(void * ctx); + int (* yajl_end_array)(void * ctx); + } yajl_callbacks; + + /** configuration structure for the generator */ + typedef struct { + /** if nonzero, javascript style comments will be allowed in + * the json input, both slash star and slash slash */ + unsigned int allowComments; + /** if nonzero, invalid UTF8 strings will cause a parse + * error */ + unsigned int checkUTF8; + } yajl_parser_config; + + /** allocate a parser handle + * \param callbacks a yajl callbacks structure specifying the + * functions to call when different JSON entities + * are encountered in the input text. May be NULL, + * which is only useful for validation. + * \param config configuration parameters for the parse. + * \param ctx a context pointer that will be passed to callbacks. + */ + YAJL_API yajl_handle yajl_alloc(const yajl_callbacks * callbacks, + const yajl_parser_config * config, + const yajl_alloc_funcs * allocFuncs, + void * ctx); + + /** free a parser handle */ + YAJL_API void yajl_free(yajl_handle handle); + + /** Parse some json! + * \param hand - a handle to the json parser allocated with yajl_alloc + * \param jsonText - a pointer to the UTF8 json text to be parsed + * \param jsonTextLength - the length, in bytes, of input text + */ + YAJL_API yajl_status yajl_parse(yajl_handle hand, + const unsigned char * jsonText, + unsigned int jsonTextLength); + + /** Parse any remaining buffered json. + * Since yajl is a stream-based parser, without an explicit end of + * input, yajl sometimes can't decide if content at the end of the + * stream is valid or not. For example, if "1" has been fed in, + * yajl can't know whether another digit is next or some character + * that would terminate the integer token. + * + * \param hand - a handle to the json parser allocated with yajl_alloc + */ + YAJL_API yajl_status yajl_parse_complete(yajl_handle hand); + + /** get an error string describing the state of the + * parse. + * + * If verbose is non-zero, the message will include the JSON + * text where the error occured, along with an arrow pointing to + * the specific char. + * + * \returns A dynamically allocated string will be returned which should + * be freed with yajl_free_error + */ + YAJL_API unsigned char * yajl_get_error(yajl_handle hand, int verbose, + const unsigned char * jsonText, + unsigned int jsonTextLength); + + /** + * get the amount of data consumed from the last chunk passed to YAJL. + * + * In the case of a successful parse this can help you understand if + * the entire buffer was consumed (which will allow you to handle + * "junk at end of input". + * + * In the event an error is encountered during parsing, this function + * affords the client a way to get the offset into the most recent + * chunk where the error occured. 0 will be returned if no error + * was encountered. + */ + YAJL_API unsigned int yajl_get_bytes_consumed(yajl_handle hand); + + /** free an error returned from yajl_get_error */ + YAJL_API void yajl_free_error(yajl_handle hand, unsigned char * str); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libCom/yajl/yajl_parser.c b/src/libCom/yajl/yajl_parser.c new file mode 100644 index 000000000..1c15f8e1e --- /dev/null +++ b/src/libCom/yajl/yajl_parser.c @@ -0,0 +1,449 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "yajl_lex.h" +#include "yajl_parser.h" +#include "yajl_encode.h" +#include "yajl_bytestack.h" + +unsigned char * +yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText, + unsigned int jsonTextLen, int verbose) +{ + unsigned int offset = hand->bytesConsumed; + unsigned char * str; + const char * errorType = NULL; + const char * errorText = NULL; + char text[72]; + const char * arrow = " (right here) ------^\n"; + + if (yajl_bs_current(hand->stateStack) == yajl_state_parse_error) { + errorType = "parse"; + errorText = hand->parseError; + } else if (yajl_bs_current(hand->stateStack) == yajl_state_lexical_error) { + errorType = "lexical"; + errorText = yajl_lex_error_to_string(yajl_lex_get_error(hand->lexer)); + } else { + errorType = "unknown"; + } + + { + unsigned int memneeded = 0; + memneeded += strlen(errorType); + memneeded += strlen(" error"); + if (errorText != NULL) { + memneeded += strlen(": "); + memneeded += strlen(errorText); + } + str = (unsigned char *) YA_MALLOC(&(hand->alloc), memneeded + 2); + str[0] = 0; + strcat((char *) str, errorType); + strcat((char *) str, " error"); + if (errorText != NULL) { + strcat((char *) str, ": "); + strcat((char *) str, errorText); + } + strcat((char *) str, "\n"); + } + + /* now we append as many spaces as needed to make sure the error + * falls at char 41, if verbose was specified */ + if (verbose) { + unsigned int start, end, i; + unsigned int spacesNeeded; + + spacesNeeded = (offset < 30 ? 40 - offset : 10); + start = (offset >= 30 ? offset - 30 : 0); + end = (offset + 30 > jsonTextLen ? jsonTextLen : offset + 30); + + for (i=0;ialloc), (strlen((char *) str) + + strlen((char *) text) + + strlen(arrow) + 1)); + newStr[0] = 0; + strcat((char *) newStr, (char *) str); + strcat((char *) newStr, text); + strcat((char *) newStr, arrow); + YA_FREE(&(hand->alloc), str); + str = (unsigned char *) newStr; + } + } + return str; +} + +/* check for client cancelation */ +#define _CC_CHK(x) \ + if (!(x)) { \ + yajl_bs_set(hand->stateStack, yajl_state_parse_error); \ + hand->parseError = \ + "client cancelled parse via callback return value"; \ + return yajl_status_client_canceled; \ + } + + +yajl_status +yajl_do_parse(yajl_handle hand, const unsigned char * jsonText, + unsigned int jsonTextLen) +{ + yajl_tok tok; + const unsigned char * buf; + unsigned int bufLen; + unsigned int * offset = &(hand->bytesConsumed); + + *offset = 0; + + + around_again: + switch (yajl_bs_current(hand->stateStack)) { + case yajl_state_parse_complete: + return yajl_status_ok; + case yajl_state_lexical_error: + case yajl_state_parse_error: + return yajl_status_error; + case yajl_state_start: + case yajl_state_map_need_val: + case yajl_state_array_need_val: + case yajl_state_array_start: { + /* for arrays and maps, we advance the state for this + * depth, then push the state of the next depth. + * If an error occurs during the parsing of the nesting + * enitity, the state at this level will not matter. + * a state that needs pushing will be anything other + * than state_start */ + yajl_state stateToPush = yajl_state_start; + + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + + switch (tok) { + case yajl_tok_eof: + return yajl_status_insufficient_data; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + case yajl_tok_string: + if (hand->callbacks && hand->callbacks->yajl_string) { + _CC_CHK(hand->callbacks->yajl_string(hand->ctx, + buf, bufLen)); + } + break; + case yajl_tok_string_with_escapes: + if (hand->callbacks && hand->callbacks->yajl_string) { + yajl_buf_clear(hand->decodeBuf); + yajl_string_decode(hand->decodeBuf, buf, bufLen); + _CC_CHK(hand->callbacks->yajl_string( + hand->ctx, yajl_buf_data(hand->decodeBuf), + yajl_buf_len(hand->decodeBuf))); + } + break; + case yajl_tok_bool: + if (hand->callbacks && hand->callbacks->yajl_boolean) { + _CC_CHK(hand->callbacks->yajl_boolean(hand->ctx, + *buf == 't')); + } + break; + case yajl_tok_null: + if (hand->callbacks && hand->callbacks->yajl_null) { + _CC_CHK(hand->callbacks->yajl_null(hand->ctx)); + } + break; + case yajl_tok_left_bracket: + if (hand->callbacks && hand->callbacks->yajl_start_map) { + _CC_CHK(hand->callbacks->yajl_start_map(hand->ctx)); + } + stateToPush = yajl_state_map_start; + break; + case yajl_tok_left_brace: + if (hand->callbacks && hand->callbacks->yajl_start_array) { + _CC_CHK(hand->callbacks->yajl_start_array(hand->ctx)); + } + stateToPush = yajl_state_array_start; + break; + case yajl_tok_integer: + /* + * note. strtol does not respect the length of + * the lexical token. in a corner case where the + * lexed number is a integer with a trailing zero, + * immediately followed by the end of buffer, + * sscanf could run off into oblivion and cause a + * crash. for this reason we copy the integer + * (and doubles), into our parse buffer (the same + * one used for unescaping strings), before + * calling strtol. yajl_buf ensures null padding, + * so we're safe. + */ + if (hand->callbacks) { + if (hand->callbacks->yajl_number) { + _CC_CHK(hand->callbacks->yajl_number( + hand->ctx,(const char *) buf, bufLen)); + } else if (hand->callbacks->yajl_integer) { + long int i = 0; + yajl_buf_clear(hand->decodeBuf); + yajl_buf_append(hand->decodeBuf, buf, bufLen); + buf = yajl_buf_data(hand->decodeBuf); + i = strtol((const char *) buf, NULL, 10); + if ((i == LONG_MIN || i == LONG_MAX) && + errno == ERANGE) + { + yajl_bs_set(hand->stateStack, + yajl_state_parse_error); + hand->parseError = "integer overflow" ; + /* try to restore error offset */ + if (*offset >= bufLen) *offset -= bufLen; + else *offset = 0; + goto around_again; + } + _CC_CHK(hand->callbacks->yajl_integer(hand->ctx, + i)); + } + } + break; + case yajl_tok_double: + if (hand->callbacks) { + if (hand->callbacks->yajl_number) { + _CC_CHK(hand->callbacks->yajl_number( + hand->ctx, (const char *) buf, bufLen)); + } else if (hand->callbacks->yajl_double) { + double d = 0.0; + yajl_buf_clear(hand->decodeBuf); + yajl_buf_append(hand->decodeBuf, buf, bufLen); + buf = yajl_buf_data(hand->decodeBuf); + d = strtod((char *) buf, NULL); + if ((d == HUGE_VAL || d == -HUGE_VAL) && + errno == ERANGE) + { + yajl_bs_set(hand->stateStack, + yajl_state_parse_error); + hand->parseError = "numeric (floating point) " + "overflow"; + /* try to restore error offset */ + if (*offset >= bufLen) *offset -= bufLen; + else *offset = 0; + goto around_again; + } + _CC_CHK(hand->callbacks->yajl_double(hand->ctx, + d)); + } + } + break; + case yajl_tok_right_brace: { + if (yajl_bs_current(hand->stateStack) == + yajl_state_array_start) + { + if (hand->callbacks && + hand->callbacks->yajl_end_array) + { + _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx)); + } + yajl_bs_pop(hand->stateStack); + goto around_again; + } + /* intentional fall-through */ + } + case yajl_tok_colon: + case yajl_tok_comma: + case yajl_tok_right_bracket: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = + "unallowed token at this point in JSON text"; + goto around_again; + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = "invalid token, internal error"; + goto around_again; + } + /* got a value. transition depends on the state we're in. */ + { + yajl_state s = yajl_bs_current(hand->stateStack); + if (s == yajl_state_start) { + yajl_bs_set(hand->stateStack, yajl_state_parse_complete); + } else if (s == yajl_state_map_need_val) { + yajl_bs_set(hand->stateStack, yajl_state_map_got_val); + } else { + yajl_bs_set(hand->stateStack, yajl_state_array_got_val); + } + } + if (stateToPush != yajl_state_start) { + yajl_bs_push(hand->stateStack, stateToPush); + } + + goto around_again; + } + case yajl_state_map_start: + case yajl_state_map_need_key: { + /* only difference between these two states is that in + * start '}' is valid, whereas in need_key, we've parsed + * a comma, and a string key _must_ follow */ + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_eof: + return yajl_status_insufficient_data; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + case yajl_tok_string_with_escapes: + if (hand->callbacks && hand->callbacks->yajl_map_key) { + yajl_buf_clear(hand->decodeBuf); + yajl_string_decode(hand->decodeBuf, buf, bufLen); + buf = yajl_buf_data(hand->decodeBuf); + bufLen = yajl_buf_len(hand->decodeBuf); + } + /* intentional fall-through */ + case yajl_tok_string: + if (hand->callbacks && hand->callbacks->yajl_map_key) { + _CC_CHK(hand->callbacks->yajl_map_key(hand->ctx, buf, + bufLen)); + } + yajl_bs_set(hand->stateStack, yajl_state_map_sep); + goto around_again; + case yajl_tok_right_bracket: + if (yajl_bs_current(hand->stateStack) == + yajl_state_map_start) + { + if (hand->callbacks && hand->callbacks->yajl_end_map) { + _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx)); + } + yajl_bs_pop(hand->stateStack); + goto around_again; + } + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = + "invalid object key (must be a string)"; + goto around_again; + } + } + case yajl_state_map_sep: { + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_colon: + yajl_bs_set(hand->stateStack, yajl_state_map_need_val); + goto around_again; + case yajl_tok_eof: + return yajl_status_insufficient_data; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = "object key and value must " + "be separated by a colon (':')"; + goto around_again; + } + } + case yajl_state_map_got_val: { + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_right_bracket: + if (hand->callbacks && hand->callbacks->yajl_end_map) { + _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx)); + } + yajl_bs_pop(hand->stateStack); + goto around_again; + case yajl_tok_comma: + yajl_bs_set(hand->stateStack, yajl_state_map_need_key); + goto around_again; + case yajl_tok_eof: + return yajl_status_insufficient_data; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = "after key and value, inside map, " + "I expect ',' or '}'"; + /* try to restore error offset */ + if (*offset >= bufLen) *offset -= bufLen; + else *offset = 0; + goto around_again; + } + } + case yajl_state_array_got_val: { + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_right_brace: + if (hand->callbacks && hand->callbacks->yajl_end_array) { + _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx)); + } + yajl_bs_pop(hand->stateStack); + goto around_again; + case yajl_tok_comma: + yajl_bs_set(hand->stateStack, yajl_state_array_need_val); + goto around_again; + case yajl_tok_eof: + return yajl_status_insufficient_data; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = + "after array element, I expect ',' or ']'"; + goto around_again; + } + } + } + + abort(); + return yajl_status_error; +} + diff --git a/src/libCom/yajl/yajl_parser.h b/src/libCom/yajl/yajl_parser.h new file mode 100644 index 000000000..c9d29d436 --- /dev/null +++ b/src/libCom/yajl/yajl_parser.h @@ -0,0 +1,82 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __YAJL_PARSER_H__ +#define __YAJL_PARSER_H__ + +#include "yajl_parse.h" +#include "yajl_bytestack.h" +#include "yajl_buf.h" + + +typedef enum { + yajl_state_start = 0, + yajl_state_parse_complete, + yajl_state_parse_error, + yajl_state_lexical_error, + yajl_state_map_start, + yajl_state_map_sep, + yajl_state_map_need_val, + yajl_state_map_got_val, + yajl_state_map_need_key, + yajl_state_array_start, + yajl_state_array_got_val, + yajl_state_array_need_val +} yajl_state; + +struct yajl_handle_t { + const yajl_callbacks * callbacks; + void * ctx; + yajl_lexer lexer; + const char * parseError; + /* the number of bytes consumed from the last client buffer, + * in the case of an error this will be an error offset, in the + * case of an error this can be used as the error offset */ + unsigned int bytesConsumed; + /* temporary storage for decoded strings */ + yajl_buf decodeBuf; + /* a stack of states. access with yajl_state_XXX routines */ + yajl_bytestack stateStack; + /* memory allocation routines */ + yajl_alloc_funcs alloc; +}; + +yajl_status +yajl_do_parse(yajl_handle handle, const unsigned char * jsonText, + unsigned int jsonTextLen); + +unsigned char * +yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText, + unsigned int jsonTextLen, int verbose); + + +#endif diff --git a/src/std/Makefile b/src/std/Makefile index 72dd2b2b0..6b63cc8f5 100644 --- a/src/std/Makefile +++ b/src/std/Makefile @@ -19,6 +19,7 @@ dbRecStd_RCS += dbRecStd.rc include $(STDDIR)/rec/Makefile include $(STDDIR)/dev/Makefile +include $(STDDIR)/filters/Makefile include $(STDDIR)/test/Makefile include $(STDDIR)/softIoc/Makefile diff --git a/src/std/dev/Makefile b/src/std/dev/Makefile index a118f7057..79eb6cb0c 100644 --- a/src/std/dev/Makefile +++ b/src/std/dev/Makefile @@ -21,8 +21,10 @@ dbRecStd_SRCS += devAoSoft.c dbRecStd_SRCS += devAoSoftRaw.c dbRecStd_SRCS += devBiSoft.c dbRecStd_SRCS += devBiSoftRaw.c +dbRecStd_SRCS += devBiDbState.c dbRecStd_SRCS += devBoSoft.c dbRecStd_SRCS += devBoSoftRaw.c +dbRecStd_SRCS += devBoDbState.c dbRecStd_SRCS += devCalcoutSoft.c dbRecStd_SRCS += devEventSoft.c dbRecStd_SRCS += devHistogramSoft.c diff --git a/src/std/dev/devBiDbState.c b/src/std/dev/devBiDbState.c new file mode 100644 index 000000000..54d929373 --- /dev/null +++ b/src/std/dev/devBiDbState.c @@ -0,0 +1,92 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include "errlog.h" +#include "dbState.h" +#include "devSup.h" +#include "recGbl.h" +#include "dbLink.h" +#include "dbAccessDefs.h" +#include "epicsExport.h" +#include "biRecord.h" + +#define DEVSUPNAME "devBiDbState" + +static long add_record (struct dbCommon *pdbc) +{ + biRecord *prec = (biRecord *) pdbc; + + if (INST_IO != prec->inp.type) { + recGblRecordError(S_db_badField, (void *) prec, DEVSUPNAME ": Illegal INP field"); + return(S_db_badField); + } + + if (!(prec->dpvt = dbStateFind(prec->inp.value.instio.string)) && + prec->inp.value.instio.string && + '\0' != *prec->inp.value.instio.string) { + errlogSevPrintf(errlogInfo, DEVSUPNAME ": Creating new db state '%s'\n", + prec->inp.value.instio.string); + prec->dpvt = dbStateCreate(prec->inp.value.instio.string); + } + return 0; +} + +static long del_record (struct dbCommon *pdbc) +{ + biRecord *prec = (biRecord *) pdbc; + prec->dpvt = NULL; + return 0; +} + +static struct dsxt myDsxt = { + add_record, + del_record +}; + +static long init(int pass) +{ + if (pass == 0) + devExtend(&myDsxt); + return 0; +} + +static long read_bi(biRecord *prec) +{ + if (prec->dpvt) { + prec->val = dbStateGet(prec->dpvt); + prec->udf = FALSE; + } + + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(&prec->inp, &prec->time); + + return 2; +} + +static struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_bi; +} devBiDbState = { + 5, + NULL, + init, + NULL, + NULL, + read_bi +}; + +epicsExportAddress(dset, devBiDbState); diff --git a/src/std/dev/devBoDbState.c b/src/std/dev/devBoDbState.c new file mode 100644 index 000000000..b2e8c493f --- /dev/null +++ b/src/std/dev/devBoDbState.c @@ -0,0 +1,86 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include "errlog.h" +#include "dbState.h" +#include "devSup.h" +#include "recGbl.h" +#include "dbAccessDefs.h" +#include "epicsExport.h" +#include "boRecord.h" + +#define DEVSUPNAME "devBoDbState" + +static long add_record (struct dbCommon *pdbc) +{ + boRecord *prec = (boRecord *) pdbc; + + if (INST_IO != prec->out.type) { + recGblRecordError(S_db_badField, (void *) prec, DEVSUPNAME ": Illegal OUT field"); + return(S_db_badField); + } + + if (!(prec->dpvt = dbStateFind(prec->out.value.instio.string)) && + prec->out.value.instio.string && + '\0' != *prec->out.value.instio.string) { + errlogSevPrintf(errlogInfo, DEVSUPNAME ": Creating new db state '%s'\n", + prec->out.value.instio.string); + prec->dpvt = dbStateCreate(prec->out.value.instio.string); + } + return 0; +} + +static long del_record (struct dbCommon *pdbc) +{ + boRecord *prec = (boRecord *) pdbc; + prec->dpvt = NULL; + return 0; +} + +static struct dsxt myDsxt = { + add_record, + del_record +}; + +static long init(int pass) +{ + if (pass == 0) + devExtend(&myDsxt); + return 0; +} + +static long write_bo(boRecord *prec) +{ + if (prec->val) + dbStateSet(prec->dpvt); + else + dbStateClear(prec->dpvt); + return 0; +} + +static struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_bo; +} devBoDbState = { + 5, + NULL, + init, + NULL, + NULL, + write_bo +}; + +epicsExportAddress(dset, devBoDbState); diff --git a/src/std/dev/devSoft.dbd b/src/std/dev/devSoft.dbd index 5f90aba86..8178bfef1 100644 --- a/src/std/dev/devSoft.dbd +++ b/src/std/dev/devSoft.dbd @@ -44,3 +44,6 @@ device(longin, INST_IO,devLiGeneralTime,"General Time") device(stringin,INST_IO,devSiGeneralTime,"General Time") device(stringout,INST_IO,devSoStdio,"stdio") + +device(bi, INST_IO, devBiDbState, "Db State") +device(bo, INST_IO, devBoDbState, "Db State") diff --git a/src/std/filters/Makefile b/src/std/filters/Makefile new file mode 100644 index 000000000..7e33500d1 --- /dev/null +++ b/src/std/filters/Makefile @@ -0,0 +1,18 @@ +#************************************************************************* +# Copyright (c) 2011 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. +#************************************************************************* + +# This is a Makefile fragment, see src/std/Makefile. + +SRC_DIRS += $(STDDIR)/filters + +DBD_INSTALLS += filters.dbd + +dbRecStd_SRCS += ts.c +dbRecStd_SRCS += dbnd.c +dbRecStd_SRCS += arr.c +dbRecStd_SRCS += sync.c + diff --git a/src/std/filters/arr.c b/src/std/filters/arr.c new file mode 100644 index 000000000..bc0fb3609 --- /dev/null +++ b/src/std/filters/arr.c @@ -0,0 +1,207 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct myStruct { + epicsInt32 start; + epicsInt32 incr; + epicsInt32 end; + void *arrayFreeList; + long no_elements; +} myStruct; + +static void *myStructFreeList; + +static const +chfPluginArgDef opts[] = { + chfInt32 (myStruct, start, "s", 0, 1), + chfInt32 (myStruct, incr, "i", 0, 1), + chfInt32 (myStruct, end, "e", 0, 1), + chfPluginArgEnd +}; + +static void * allocPvt(void) +{ + myStruct *my = (myStruct*) freeListCalloc(myStructFreeList); + my->incr = 1; + my->end = -1; + return (void *) my; +} + +static void freePvt(void *pvt) +{ + myStruct *my = (myStruct*) pvt; + + if (my->arrayFreeList) freeListCleanup(my->arrayFreeList); + freeListFree(myStructFreeList, pvt); +} + +static int parse_ok(void *pvt) +{ + myStruct *my = (myStruct*) pvt; + + if (my->incr <= 0) my->incr = 1; + return 0; +} + +static void freeArray(db_field_log *pfl) { + if (pfl->type == dbfl_type_ref) { + freeListFree(pfl->u.r.pvt, pfl->u.r.field); + } +} + +static long wrapArrayIndices(long *start, const long increment, long *end, const long no_elements) { + long len = 0; + + if (*start < 0) *start = no_elements + *start; + if (*start < 0) *start = 0; + if (*start > no_elements) *start = no_elements; + + if (*end < 0) *end = no_elements + *end; + if (*end < 0) *end = 0; + if (*end >= no_elements) *end = no_elements - 1; + + if (*end - *start >= 0) len = 1 + (*end - *start) / increment; + return len; +} + +static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { + myStruct *my = (myStruct*) pvt; + struct dbCommon *prec; + struct rset *prset; + long start = my->start; + long end = my->end; + long nTarget = 0; + long offset = 0; + long status; + long nSource = chan->addr.no_elements; + + /* Only array data */ + if (pfl->type == dbfl_type_val) { + return pfl; + + /* Extract from record */ + } else if (pfl->type == dbfl_type_rec) { + if (chan->addr.special == SPC_DBADDR && + nSource > 1 && + (prset = dbGetRset(&chan->addr)) && + prset->get_array_info) { + status = prset->get_array_info(&chan->addr, &nSource, &offset); + nTarget = wrapArrayIndices(&start, my->incr, &end, nSource); + prec = dbChannelRecord(chan); + pfl->type = dbfl_type_ref; + pfl->stat = prec->stat; + pfl->sevr = prec->sevr; + pfl->time = prec->time; + pfl->field_type = chan->addr.field_type; + pfl->field_size = chan->addr.field_size; + pfl->no_elements = nTarget; + if (nTarget) { + void *pdst = freeListCalloc(my->arrayFreeList); + + pfl->u.r.dtor = freeArray; + pfl->u.r.pvt = my->arrayFreeList; + offset = (offset + start) % chan->addr.no_elements; + dbExtractArrayFromRec(&chan->addr, pdst, nTarget, nSource, offset, my->incr); + pfl->u.r.field = pdst; + } + } + + /* Extract from buffer */ + } else if (pfl->type == dbfl_type_ref) { + void *psrc = pfl->u.r.field; + void *pdst = NULL; + + nSource = pfl->no_elements; + nTarget = wrapArrayIndices(&start, my->incr, &end, nSource); + pfl->no_elements = nTarget; + if (nTarget) { /* Copy the data out */ + pdst = freeListCalloc(my->arrayFreeList); + offset = start; + dbExtractArrayFromBuf(psrc, pdst, pfl->field_size, pfl->field_type, nTarget, nSource, offset, my->incr); + } + if (pfl->u.r.dtor) pfl->u.r.dtor(pfl); + if (nTarget) { + pfl->u.r.dtor = freeArray; + pfl->u.r.pvt = my->arrayFreeList; + pfl->u.r.field = pdst; + } + } + return pfl; +} + +static void channelRegisterPost(dbChannel *chan, void *pvt, + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) +{ + myStruct *my = (myStruct*) pvt; + long start = my->start; + long end = my->end; + long max = 0; + + if (probe->no_elements <= 1) return; /* array data only */ + + max = wrapArrayIndices(&start, my->incr, &end, probe->no_elements); /* wrap indices into array */ + if (max) { + if (!my->arrayFreeList) + freeListInitPvt(&my->arrayFreeList, max * probe->field_size, 2); + if (!my->arrayFreeList) return; + } + probe->no_elements = my->no_elements = max; + *cb_out = filter; + *arg_out = pvt; +} + +static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent) +{ + myStruct *my = (myStruct*) pvt; + printf("%*s plugin arr, start=%d, incr=%d, end=%d\n", indent, "", + my->start, my->incr, my->end); +} + +static chfPluginIf pif = { + allocPvt, + freePvt, + + NULL, /* parse_error, */ + parse_ok, + + NULL, /* channel_open, */ + NULL, /* channelRegisterPre, */ + channelRegisterPost, + channel_report, + NULL /* channel_close */ +}; + +static void arrInitialize(void) +{ + static int firstTime = 1; + + if (!firstTime) return; + firstTime = 0; + + if (!myStructFreeList) + freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64); + + chfPluginRegister("arr", &pif, opts); +} + +epicsExportRegistrar(arrInitialize); diff --git a/src/std/filters/dbnd.c b/src/std/filters/dbnd.c new file mode 100644 index 000000000..33e79198d --- /dev/null +++ b/src/std/filters/dbnd.c @@ -0,0 +1,142 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#include +#include +#include +#include +#include +#include + +typedef struct myStruct { + int mode; + double cval; + double hyst; + double last; +} myStruct; + +static void *myStructFreeList; + +static const +chfPluginEnumType modeEnum[] = { {"abs", 0}, {"rel", 1}, {NULL,0} }; + +static const +chfPluginArgDef opts[] = { + chfDouble (myStruct, cval, "d", 0, 1), + chfEnum (myStruct, mode, "m", 0, 1, modeEnum), + chfPluginArgEnd +}; + +static void * allocPvt(void) +{ + return freeListCalloc(myStructFreeList); +} + +static void freePvt(void *pvt) +{ + freeListFree(myStructFreeList, pvt); +} + +static int parse_ok(void *pvt) +{ + myStruct *my = (myStruct*) pvt; + my->hyst = my->cval; + my->last = epicsNAN; + return 0; +} + +static void shiftval (myStruct *my, double val) { + my->last = val; + if (my->mode == 1) + my->hyst = val * my->cval/100.; +} + +static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { + myStruct *my = (myStruct*) pvt; + long status; + double val, delta; + short drop = 0; + + /* + * Only scalar values supported - strings, arrays, and conversion errors + * are just passed on + */ + if (pfl->type == dbfl_type_val) { + DBADDR localAddr = chan->addr; /* Structure copy */ + localAddr.field_type = pfl->field_type; + localAddr.field_size = pfl->field_size; + localAddr.no_elements = pfl->no_elements; + localAddr.pfield = (char *) &pfl->u.v.field; + status = dbFastGetConvertRoutine[pfl->field_type][DBR_DOUBLE] + (localAddr.pfield, (void*) &val, &localAddr); + if (!status) { + if (isnan(my->last)) { + shiftval(my, val); + } else { + delta = fabs(my->last - val); + if (delta <= my->hyst) { + drop = 1; + } else { + shiftval(my, val); + } + } + } + } + if (drop) return NULL; + else return pfl; +} + +static void channelRegisterPre(dbChannel *chan, void *pvt, + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) +{ + *cb_out = filter; + *arg_out = pvt; +} + +static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent) +{ + myStruct *my = (myStruct*) pvt; + printf("%*s plugin dbnd, mode=%s, delta=%g%s\n", indent, "", + chfPluginEnumString(modeEnum, my->mode, "n/a"), my->cval, + my->mode == 1 ? "%" : ""); +} + +static chfPluginIf pif = { + allocPvt, + freePvt, + + NULL, /* parse_error, */ + parse_ok, + + NULL, /* channel_open, */ + channelRegisterPre, + NULL, /* channelRegisterPost, */ + channel_report, + NULL /* channel_close */ +}; + +static void dbndInitialize(void) +{ + static int firstTime = 1; + + if (!firstTime) return; + firstTime = 0; + + if (!myStructFreeList) + freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64); + + chfPluginRegister("dbnd", &pif, opts); +} + +epicsExportRegistrar(dbndInitialize); diff --git a/src/std/filters/filters.dbd b/src/std/filters/filters.dbd new file mode 100644 index 000000000..7fb0c5133 --- /dev/null +++ b/src/std/filters/filters.dbd @@ -0,0 +1,4 @@ +registrar(tsInitialize) +registrar(dbndInitialize) +registrar(arrInitialize) +registrar(syncInitialize) diff --git a/src/std/filters/sync.c b/src/std/filters/sync.c new file mode 100644 index 000000000..15c27b267 --- /dev/null +++ b/src/std/filters/sync.c @@ -0,0 +1,172 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#include "epicsExport.h" +#include "freeList.h" +#include "db_field_log.h" +#include "chfPlugin.h" +#include "dbState.h" + +#define STATE_NAME_LENGTH 20 + +static const +chfPluginEnumType modeEnum[] = { {"before", 0}, {"first", 1}, + {"last", 2}, {"after", 3}, + {"while", 4}, {"unless", 5}, + {NULL,0} }; +typedef enum syncMode { + syncModeBefore=0, + syncModeFirst=1, + syncModeLast=2, + syncModeAfter=3, + syncModeWhile=4, + syncModeUnless=5 +} syncMode; + +typedef struct myStruct { + syncMode mode; + char state[STATE_NAME_LENGTH]; + dbStateId id; + db_field_log *lastfl; + int laststate:1; +} myStruct; + +static void *myStructFreeList; + +static const +chfPluginArgDef opts[] = { + chfEnum (myStruct, mode, "m", 1, 1, modeEnum), + chfString (myStruct, state, "s", 1, 0), + chfPluginArgEnd +}; + +static void * allocPvt(void) +{ + myStruct *my = (myStruct*) freeListCalloc(myStructFreeList); + return (void *) my; +} + +static void freePvt(void *pvt) +{ + freeListFree(myStructFreeList, pvt); +} + +static int parse_ok(void *pvt) +{ + myStruct *my = (myStruct*) pvt; + + if (!(my->id = dbStateFind(my->state))) + return -1; + + return 0; +} + +static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { + db_field_log *passfl = NULL; + myStruct *my = (myStruct*) pvt; + int actstate; + + if (pfl->ctx == dbfl_context_read) + return pfl; + + actstate = dbStateGet(my->id); + + switch (my->mode) { + case syncModeBefore: + if (actstate && !my->laststate) { + passfl = my->lastfl; + my->lastfl = NULL; + } + break; + case syncModeFirst: + if (actstate && !my->laststate) { + passfl = pfl; + pfl = NULL; + } + break; + case syncModeLast: + if (!actstate && my->laststate) { + passfl = my->lastfl; + my->lastfl = NULL; + } + break; + case syncModeAfter: + if (!actstate && my->laststate) { + passfl = pfl; + pfl = NULL; + } + break; + case syncModeWhile: + if (actstate) { + passfl = pfl; + } + goto no_shift; + case syncModeUnless: + if (!actstate) { + passfl = pfl; + } + goto no_shift; + } + + if (my->lastfl) + db_delete_field_log(my->lastfl); + my->lastfl = pfl; + my->laststate = actstate; + + no_shift: + return passfl; +} + +static void channelRegisterPre(dbChannel *chan, void *pvt, + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) +{ + *cb_out = filter; + *arg_out = pvt; +} + +static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent) +{ + myStruct *my = (myStruct*) pvt; + printf("%*s plugin sync, mode=%s, state=%s\n", indent, "", + chfPluginEnumString(modeEnum, my->mode, "n/a"), my->state); +} + +static chfPluginIf pif = { + allocPvt, + freePvt, + + NULL, /* parse_error, */ + parse_ok, + + NULL, /* channel_open, */ + channelRegisterPre, + NULL, /* channelRegisterPost, */ + channel_report, + NULL /* channel_close */ +}; + +static void syncInitialize(void) +{ + static int firstTime = 1; + + if (!firstTime) return; + firstTime = 0; + + if (!myStructFreeList) + freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64); + + chfPluginRegister("sync", &pif, opts); +} + +epicsExportRegistrar(syncInitialize); diff --git a/src/std/filters/test/Makefile b/src/std/filters/test/Makefile new file mode 100644 index 000000000..4eb0b1cd6 --- /dev/null +++ b/src/std/filters/test/Makefile @@ -0,0 +1,45 @@ +#************************************************************************* +# Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. +#************************************************************************* +TOP=../../../.. + +include $(TOP)/configure/CONFIG + +PROD_LIBS += dbRecStd dbCore ca Com + +TESTPROD_HOST += tsTest +tsTest_SRCS += tsTest.c xRecord_registerRecordDeviceDriver.cpp +OBJS_IOC_vxWorks += tsTest +TESTS += tsTest + +TESTPROD_HOST += dbndTest +dbndTest_SRCS += dbndTest.c xRecord_registerRecordDeviceDriver.cpp +OBJS_IOC_vxWorks += dbndTest +TESTS += dbndTest + +TARGETS += $(COMMON_DIR)/arrTest.dbd +arrTest_DBD += arrRecord.dbd +TESTPROD_HOST += arrTest +arrTest_SRCS += arrTest.cpp arrRecord.c arrRecord_registerRecordDeviceDriver.cpp +OBJS_IOC_vxWorks += arrTest +TESTS += arrTest + +TESTPROD_HOST += syncTest +syncTest_SRCS += syncTest.c xRecord_registerRecordDeviceDriver.cpp +OBJS_IOC_vxWorks += syncTest +TESTS += syncTest + +TESTSCRIPTS_HOST += $(TESTS:%=%.t) + +include $(TOP)/configure/RULES + +tsTest$(DEP): $(COMMON_DIR)/xRecord.h +dbndTest$(DEP): $(COMMON_DIR)/xRecord.h +syncTest$(DEP): $(COMMON_DIR)/xRecord.h +arrRecord$(DEP): $(COMMON_DIR)/arrRecord.h +arrTest$(DEP): $(COMMON_DIR)/arrRecord.h diff --git a/src/std/filters/test/arrRecord.c b/src/std/filters/test/arrRecord.c new file mode 100644 index 000000000..5e9b2f02c --- /dev/null +++ b/src/std/filters/test/arrRecord.c @@ -0,0 +1,135 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* arrRecord.c - minimal array record for test purposes: no processing */ + +/* + * Author: Ralph Lange + * + * vaguely implemented like parts of recWaveform.c by Bob Dalesio + * + */ + +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "recSup.h" +#include "recGbl.h" +#include "cantProceed.h" +#define GEN_SIZE_OFFSET +#include "arrRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(arrRecord *, int); +static long process(arrRecord *); +#define special NULL +#define get_value NULL +static long cvt_dbaddr(DBADDR *); +static long get_array_info(DBADDR *, long *, long *); +static long put_array_info(DBADDR *, long); +#define get_units NULL +#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 arrRSET = { + 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, arrRSET); + +static long init_record(arrRecord *prec, int pass) +{ + if (pass == 0) { + if (prec->nelm <= 0) + prec->nelm = 1; + if (prec->ftvl > DBF_ENUM) + prec->ftvl = DBF_UCHAR; + prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl), + "arr calloc failed"); + + if (prec->nelm == 1) { + prec->nord = 1; + } else { + prec->nord = 0; + } + return 0; + } + return 0; +} + +static long process(arrRecord *prec) +{ + return 0; +} + +static long cvt_dbaddr(DBADDR *paddr) +{ + arrRecord *prec = (arrRecord *) paddr->precord; + + paddr->pfield = prec->bptr; + paddr->no_elements = prec->nelm; + paddr->field_type = prec->ftvl; + paddr->field_size = dbValueSize(prec->ftvl); + paddr->dbr_field_type = prec->ftvl; + + return 0; +} + +static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) +{ + arrRecord *prec = (arrRecord *) paddr->precord; + + *no_elements = prec->nord; + *offset = prec->off; + + return 0; +} + +static long put_array_info(DBADDR *paddr, long nNew) +{ + arrRecord *prec = (arrRecord *) paddr->precord; + + prec->nord = nNew; + if (prec->nord > prec->nelm) + prec->nord = prec->nelm; + + return 0; +} diff --git a/src/std/filters/test/arrRecord.dbd b/src/std/filters/test/arrRecord.dbd new file mode 100644 index 000000000..782a03e9d --- /dev/null +++ b/src/std/filters/test/arrRecord.dbd @@ -0,0 +1,33 @@ +include "menuGlobal.dbd" +include "menuConvert.dbd" +recordtype(arr) { + include "dbCommon.dbd" + field(VAL, DBF_NOACCESS) { + prompt("Value") + special(SPC_DBADDR) + pp(TRUE) + extra("void *val") + } + field(NELM, DBF_ULONG) { + prompt("Number of Elements") + special(SPC_NOMOD) + initial("1") + } + field(FTVL, DBF_MENU) { + prompt("Field Type of Value") + special(SPC_NOMOD) + menu(menuFtype) + } + field(NORD, DBF_ULONG) { + prompt("Number elements read") + special(SPC_NOMOD) + } + field(OFF, DBF_ULONG) { + prompt("Offset into array") + } + field(BPTR, DBF_NOACCESS) { + prompt("Buffer Pointer") + special(SPC_NOMOD) + extra("void *bptr") + } +} diff --git a/src/std/filters/test/arrTest.cpp b/src/std/filters/test/arrTest.cpp new file mode 100644 index 000000000..389b1fc9a --- /dev/null +++ b/src/std/filters/test/arrTest.cpp @@ -0,0 +1,337 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2003 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to the Software License Agreement +* found in the file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +/* using stuff from softIoc.cpp by Andrew Johnson */ + +#include +#include +#include +#include +#include + +#include "registryFunction.h" +#include "epicsThread.h" +#include "epicsExit.h" +#include "epicsStdio.h" +#include "envDefs.h" +#include "dbStaticLib.h" +#include "subRecord.h" +#include "dbAddr.h" +#include "dbAccess.h" +#include "asDbLib.h" +#include "iocInit.h" +#include "iocsh.h" +#include "dbChannel.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#include "arrRecord.h" + +extern "C" int arrRecord_registerRecordDeviceDriver(struct dbBase *pdbbase); +extern "C" void (*pvar_func_arrInitialize)(void); + +#define CA_SERVER_PORT "65535" + +#define PATTERN 0x55 + +const char *server_port = CA_SERVER_PORT; + +static void exitSubroutine(subRecord *precord) { + epicsExit((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE); +} + +static int fl_equals_array(short type, const db_field_log *pfl1, void *p2) { + for (int i = 0; i < pfl1->no_elements; i++) { + switch (type) { + case DBR_DOUBLE: + if (((epicsFloat64*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) { + testDiag("at index=%d: field log has %g, should be %d", + i, ((epicsFloat64*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]); + return 0; + } + break; + case DBR_LONG: + if (((epicsInt32*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) { + testDiag("at index=%d: field log has %d, should be %d", + i, ((epicsInt32*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]); + return 0; + } + break; + case DBR_STRING: + if (strtol(&((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], NULL, 0) != ((epicsInt32*)p2)[i]) { + testDiag("at index=%d: field log has '%s', should be '%d'", + i, &((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], ((epicsInt32*)p2)[i]); + return 0; + } + break; + default: + return 0; + } + } + return 1; +} + +static void createAndOpen(const char *chan, const char *json, const char *type, dbChannel**pch, short no) { + ELLNODE *node; + chFilter *filter; + char name[80]; + + strncpy(name, chan, sizeof(name)-1); + strncat(name, json, sizeof(name)-strlen(name)-1); + + testOk(!!(*pch = dbChannelCreate(name)), "dbChannel with plugin arr %s created", type); + testOk((ellCount(&(*pch)->filters) == no), "channel has %d filter(s) in filter list", no); + + testOk(!(dbChannelOpen(*pch)), "dbChannel with plugin arr opened"); + + node = ellFirst(&(*pch)->pre_chain); + filter = CONTAINER(node, chFilter, pre_node); + testOk((ellCount(&(*pch)->pre_chain) == 0), "arr has no filter in pre chain"); + + node = ellFirst(&(*pch)->post_chain); + filter = CONTAINER(node, chFilter, post_node); + testOk((ellCount(&(*pch)->post_chain) == no), + "arr has %d filter(s) in post chain", no); +} + +static void testHead (const char *title, const char *typ = "") { + const char *line = "------------------------------------------------------------------------------"; + testDiag(line); + testDiag(title, typ); + testDiag(line); +} + +#define TEST1(Size, Offset, Incr, Text) \ + testDiag("Offset: %d (%s)", Offset, Text); \ + off = Offset; \ + status = dbPutField(&offaddr, DBR_LONG, &off, 1); \ + pfl = db_create_read_log(pch); \ + testOk(pfl->type == dbfl_type_rec, "original field log has type rec"); \ + pfl2 = dbChannelRunPostChain(pch, pfl); \ + testOk(pfl2 == pfl, "call does not drop or replace field_log"); \ + testOk(pfl->type == dbfl_type_ref, "filtered field log has type ref"); \ + testOk(fl_equals_array(dbr_type, pfl2, ar##Size##_##Offset##_##Incr), "array data correct"); \ + db_delete_field_log(pfl); + +static void check(short dbr_type) { + dbChannel *pch; + db_field_log *pfl, *pfl2; + int status; + dbAddr valaddr; + dbAddr offaddr; + const char *offname = NULL, *valname = NULL, *typname = NULL; + epicsInt32 ar[10] = {10,11,12,13,14,15,16,17,18,19}; + epicsInt32 *ar10_0_1 = ar; + epicsInt32 ar10_4_1[10] = {14,15,16,17,18,19,10,11,12,13}; + epicsInt32 ar5_0_1[10] = {12,13,14,15,16}; + epicsInt32 ar5_3_1[10] = {15,16,17,18,19}; + epicsInt32 ar5_5_1[10] = {17,18,19,10,11}; + epicsInt32 ar5_9_1[10] = {11,12,13,14,15}; + epicsInt32 ar5_0_2[10] = {12,14,16}; + epicsInt32 ar5_3_2[10] = {15,17,19}; + epicsInt32 ar5_5_2[10] = {17,19,11}; + epicsInt32 ar5_9_2[10] = {11,13,15}; + epicsInt32 ar5_0_3[10] = {12,15}; + epicsInt32 ar5_3_3[10] = {15,18}; + epicsInt32 ar5_5_3[10] = {17,10}; + epicsInt32 ar5_9_3[10] = {11,14}; + epicsInt32 off = 0; + + switch (dbr_type) { + case DBR_LONG: + offname = "x.OFF"; + valname = "x.VAL"; + typname = "long"; + break; + case DBR_DOUBLE: + offname = "y.OFF"; + valname = "y.VAL"; + typname = "double"; + break; + case DBR_STRING: + offname = "z.OFF"; + valname = "z.VAL"; + typname = "string"; + break; + default: + testDiag("Invalid data type %d", dbr_type); + } + + status = dbNameToAddr(offname, &offaddr); + + status = dbNameToAddr(valname, &valaddr); + status = dbPutField(&valaddr, DBR_LONG, ar, 10); + + /* Default: should not change anything */ + + testHead("Ten %s elements from rec, increment 1, full size (default)", typname); + createAndOpen(valname, "{\"arr\":{}}", "(default)", &pch, 1); + testOk(pch->final_type == valaddr.field_type, + "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); + testOk(pch->final_no_elements == valaddr.no_elements, + "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); + TEST1(10, 0, 1, "no offset"); + TEST1(10, 4, 1, "wrapped"); + dbChannelDelete(pch); + + testHead("Ten %s elements from rec, increment 1, out-of-bound start parameter", typname); + createAndOpen(valname, "{\"arr\":{\"s\":-500}}", "out-of-bound start", &pch, 1); + testOk(pch->final_type == valaddr.field_type, + "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); + testOk(pch->final_no_elements == valaddr.no_elements, + "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); + TEST1(10, 4, 1, "wrapped"); + dbChannelDelete(pch); + + testHead("Ten %s elements from rec, increment 1, out-of-bound end parameter", typname); + createAndOpen(valname, "{\"arr\":{\"e\":500}}", "out-of-bound end", &pch, 1); + testOk(pch->final_type == valaddr.field_type, + "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); + testOk(pch->final_no_elements == valaddr.no_elements, + "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); + TEST1(10, 4, 1, "wrapped"); + dbChannelDelete(pch); + + testHead("Ten %s elements from rec, increment 1, zero increment parameter", typname); + createAndOpen(valname, "{\"arr\":{\"i\":0}}", "zero increment", &pch, 1); + testOk(pch->final_type == valaddr.field_type, + "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); + testOk(pch->final_no_elements == valaddr.no_elements, + "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); + TEST1(10, 4, 1, "wrapped"); + dbChannelDelete(pch); + + testHead("Ten %s elements from rec, increment 1, invalid increment parameter", typname); + createAndOpen(valname, "{\"arr\":{\"i\":-30}}", "invalid increment", &pch, 1); + testOk(pch->final_type == valaddr.field_type, + "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); + testOk(pch->final_no_elements == valaddr.no_elements, + "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); + TEST1(10, 4, 1, "wrapped"); + dbChannelDelete(pch); + +#define TEST5(Incr, Left, Right, Type) \ + testHead("Five %s elements from rec, increment " #Incr ", " Type " addressing", typname); \ + createAndOpen(valname, "{\"arr\":{\"s\":" #Left ",\"e\":" #Right ",\"i\":" #Incr "}}", \ + "(" #Left ":" #Incr ":" #Right ")", &pch, 1); \ + testOk(pch->final_type == valaddr.field_type, \ + "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); \ + testOk(pch->final_no_elements == 4 / Incr + 1, \ + "final no_elements correct (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); \ + TEST1(5, 0, Incr, "no offset"); \ + TEST1(5, 3, Incr, "from upper block"); \ + TEST1(5, 5, Incr, "wrapped"); \ + TEST1(5, 9, Incr, "from lower block"); \ + dbChannelDelete(pch); + + /* Contiguous block of 5 */ + + TEST5(1, 2, 6, "regular"); + TEST5(1, -8, 6, "left side from-end"); + TEST5(1, 2, -4, "right side from-end"); + TEST5(1, -8, -4, "both sides from-end"); + + /* 5 elements with increment 2 */ + + TEST5(2, 2, 6, "regular"); + TEST5(2, -8, 6, "left side from-end"); + TEST5(2, 2, -4, "right side from-end"); + TEST5(2, -8, -4, "both sides from-end"); + + /* 5 elements with increment 3 */ + + TEST5(3, 2, 6, "regular"); + TEST5(3, -8, 6, "left side from-end"); + TEST5(3, 2, -4, "right side from-end"); + TEST5(3, -8, -4, "both sides from-end"); + + /* From buffer (plugin chain) */ + +#define TEST5B(Incr, Left, Right, Type) \ + testHead("Five %s elements from buffer, increment " #Incr ", " Type " addressing", typname); \ + createAndOpen(valname, "{\"arr\":{},\"arr\":{\"s\":" #Left ",\"e\":" #Right ",\"i\":" #Incr "}}", \ + "(" #Left ":" #Incr ":" #Right ")", &pch, 2); \ + testOk(pch->final_type == valaddr.field_type, \ + "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); \ + testOk(pch->final_no_elements == 4 / Incr + 1, \ + "final no_elements correct (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); \ + TEST1(5, 0, Incr, "no offset"); \ + dbChannelDelete(pch); + + /* Contiguous block of 5 */ + + TEST5B(1, 2, 6, "regular"); + TEST5B(1, -8, 6, "left side from-end"); + TEST5B(1, 2, -4, "right side from-end"); + TEST5B(1, -8, -4, "both sides from-end"); + + /* 5 elements with increment 2 */ + + TEST5B(2, 2, 6, "regular"); + TEST5B(2, -8, 6, "left side from-end"); + TEST5B(2, 2, -4, "right side from-end"); + TEST5B(2, -8, -4, "both sides from-end"); + + /* 5 elements with increment 3 */ + + TEST5B(3, 2, 6, "regular"); + TEST5B(3, -8, 6, "left side from-end"); + TEST5B(3, 2, -4, "right side from-end"); + TEST5B(3, -8, -4, "both sides from-end"); +} + +MAIN(arrTest) +{ + const chFilterPlugin *plug; + char arr[] = "arr"; + + testPlan(1402); + + /* Prepare the IOC */ + + epicsEnvSet("EPICS_CA_SERVER_PORT", server_port); + + if (dbReadDatabase(&pdbbase, "arrRecord.dbd", + "..:../../../../../dbd", NULL)) + testAbort("Database description not loaded"); + + (*pvar_func_arrInitialize)(); + arrRecord_registerRecordDeviceDriver(pdbbase); + registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine); + + if (dbReadDatabase(&pdbbase, "arrTest.db", "..", NULL)) + testAbort("Test database not loaded"); + + /* Start the IOC */ + + iocInit(); + db_init_events(); + epicsThreadSleep(0.2); + + testOk(!!(plug = dbFindFilter(arr, strlen(arr))), "plugin arr registered correctly"); + + check(DBR_LONG); + check(DBR_DOUBLE); + check(DBR_STRING); + + dbFreeBase(pdbbase); + + return testDone(); + + epicsExit(EXIT_SUCCESS); + /*Note that the following statement will never be executed*/ + return 0; +} diff --git a/src/std/filters/test/arrTest.db b/src/std/filters/test/arrTest.db new file mode 100644 index 000000000..467cf0d08 --- /dev/null +++ b/src/std/filters/test/arrTest.db @@ -0,0 +1,15 @@ +record(arr, "x") { + field(DESC, "test array record") + field(NELM, "10") + field(FTVL, "LONG") +} +record(arr, "y") { + field(DESC, "test array record") + field(NELM, "10") + field(FTVL, "DOUBLE") +} +record(arr, "z") { + field(DESC, "test array record") + field(NELM, "10") + field(FTVL, "STRING") +} diff --git a/src/std/filters/test/dbChannelTest.db b/src/std/filters/test/dbChannelTest.db new file mode 100644 index 000000000..a6fa08e40 --- /dev/null +++ b/src/std/filters/test/dbChannelTest.db @@ -0,0 +1,2 @@ +record(x, x) {} + diff --git a/src/std/filters/test/dbndTest.c b/src/std/filters/test/dbndTest.c new file mode 100644 index 000000000..18cccc361 --- /dev/null +++ b/src/std/filters/test/dbndTest.c @@ -0,0 +1,235 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#include "dbStaticLib.h" +#include "dbAccessDefs.h" +#include "db_field_log.h" +#include "dbCommon.h" +#include "chfPlugin.h" +#include "epicsUnitTest.h" +#include "epicsTime.h" +#include "testMain.h" + +#define PATTERN 0x55 + +epicsShareExtern void (*pvar_func_dbndInitialize)(void); + +static db_field_log fl; + +static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) { + return !(memcmp(pfl1, pfl2, sizeof(db_field_log))); +} + +static void fl_setup(dbChannel *chan, db_field_log *pfl) { + struct dbCommon *prec = dbChannelRecord(chan); + + pfl->ctx = dbfl_context_read; + pfl->type = dbfl_type_val; + pfl->stat = prec->stat; + pfl->sevr = prec->sevr; + pfl->time = prec->time; + pfl->field_type = dbChannelFieldType(chan); + pfl->no_elements = dbChannelElements(chan); + /* + * use memcpy to avoid a bus error on + * union copy of char in the db at an odd + * address + */ + memcpy(&pfl->u.v.field, + dbChannelField(chan), + dbChannelFieldSize(chan)); +} + +static void changeValue(db_field_log *pfl2, long val) { + pfl2->u.v.field.dbf_long = val; + testDiag("new value: %ld", val); +} + +static void mustPassOnce(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) { + db_field_log *pfl; + + changeValue(pfl2, val); + testDiag("mode=%s delta=%g filter must pass once", m, d); + pfl = dbChannelRunPreChain(pch, pfl2); + testOk(pfl2 == pfl, "call 1 does not drop or replace field_log"); + testOk(fl_equal(pfl, pfl2), "call 1 does not change field_log data"); + pfl = dbChannelRunPreChain(pch, pfl2); + testOk(NULL == pfl, "call 2 drops field_log"); +} + +static void mustDrop(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) { + db_field_log *pfl; + + changeValue(pfl2, val); + testDiag("mode=%s delta=%g filter must drop", m, d); + pfl = dbChannelRunPreChain(pch, pfl2); + testOk(NULL == pfl, "call 1 drops field_log"); +} + +static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) { + db_field_log *pfl; + + changeValue(pfl2, val); + testDiag("mode=%s delta=%g filter must pass twice", m, d); + pfl = dbChannelRunPreChain(pch, pfl2); + testOk(pfl2 == pfl, "call 1 does not drop or replace field_log"); + testOk(fl_equal(pfl, pfl2), "call 1 does not change field_log data"); + pfl = dbChannelRunPreChain(pch, pfl2); + testOk(pfl2 == pfl, "call 2 does not drop or replace field_log"); + testOk(fl_equal(pfl, pfl2), "call 2 does not change field_log data"); +} + +static void testHead (char* title) { + testDiag("--------------------------------------------------------"); + testDiag(title); + testDiag("--------------------------------------------------------"); +} + +void xRecord_registerRecordDeviceDriver(struct dbBase *); + +MAIN(dbndTest) +{ + dbChannel *pch; + chFilter *filter; + const chFilterPlugin *plug; + char dbnd[] = "dbnd"; + ELLNODE *node; + chPostEventFunc *cb_out = NULL; + void *arg_out = NULL; + db_field_log *pfl2; + db_field_log fl1; + + testPlan(59); + + db_init_events(); + + if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + testAbort("Database description not loaded"); + + (*pvar_func_dbndInitialize)(); /* manually initialize plugin */ + xRecord_registerRecordDeviceDriver(pdbbase); + + if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + testAbort("Test database not loaded"); + + testOk(!!(plug = dbFindFilter(dbnd, strlen(dbnd))), "plugin dbnd registered correctly"); + + testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{}}")), "dbChannel with plugin dbnd (delta=0) created"); + testOk((ellCount(&pch->filters) == 1), "channel has one plugin"); + + memset(&fl, PATTERN, sizeof(fl)); + fl1 = fl; + node = ellFirst(&pch->filters); + filter = CONTAINER(node, chFilter, list_node); + plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1); + testOk(!!(cb_out) && !!(arg_out), "register_pre registers one filter with argument"); + testOk(fl_equal(&fl1, &fl), "register_pre does not change field_log data type"); + + testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened"); + node = ellFirst(&pch->pre_chain); + filter = CONTAINER(node, chFilter, pre_node); + testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg != NULL), + "dbnd has one filter with argument in pre chain"); + testOk((ellCount(&pch->post_chain) == 0), "dbnd has no filter in post chain"); + + /* Field logs of type ref and rec: pass any update */ + + testHead("Field logs of type ref and rec"); + fl1.type = dbfl_type_rec; + mustPassTwice(pch, &fl1, "abs field_log=rec", 0., 0); + + fl1.type = dbfl_type_ref; + mustPassTwice(pch, &fl1, "abs field_log=ref", 0., 0); + + /* Delta = 0: pass any change */ + + testHead("Delta = 0: pass any change"); + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + + mustPassOnce(pch, pfl2, "abs", 0., 0); + mustPassOnce(pch, pfl2, "abs", 0., 1); + + db_delete_field_log(pfl2); + dbChannelDelete(pch); + + /* Delta = -1: pass any update */ + + testHead("Delta = -1: pass any update"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{\"d\":-1.0}}")), "dbChannel with plugin dbnd (delta=-1) created"); + testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened"); + + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + + mustPassTwice(pch, pfl2, "abs", -1., 0); + mustPassTwice(pch, pfl2, "abs", -1., 1); + + db_delete_field_log(pfl2); + dbChannelDelete(pch); + + /* Delta = absolute */ + + testHead("Delta = absolute"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{\"d\":3}}")), "dbChannel with plugin dbnd (delta=3) created"); + testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened"); + + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + + mustPassOnce(pch, pfl2, "abs", 3., 1); + mustDrop(pch, pfl2, "abs", 3., 3); + mustDrop(pch, pfl2, "abs", 3., 4); + mustPassOnce(pch, pfl2, "abs", 3., 5); + + db_delete_field_log(pfl2); + dbChannelDelete(pch); + + /* Delta = relative */ + + testHead("Delta = relative"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{\"m\":\"rel\",\"d\":50}}")), + "dbChannel with plugin dbnd (mode=rel, delta=50) created"); + testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened"); + + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + + mustPassOnce(pch, pfl2, "rel", 50., 1); + mustPassOnce(pch, pfl2, "rel", 50., 2); + mustDrop(pch, pfl2, "rel", 50., 3); + mustPassOnce(pch, pfl2, "rel", 50., 4); + mustDrop(pch, pfl2, "rel", 50., 5); + mustDrop(pch, pfl2, "rel", 50., 6); + mustPassOnce(pch, pfl2, "rel", 50., 7); + + db_delete_field_log(pfl2); + dbChannelDelete(pch); + dbFreeBase(pdbbase); + + return testDone(); +} + +#define GEN_SIZE_OFFSET +#include "xRecord.h" + +#include +#include + +static rset xRSET; +epicsExportAddress(rset,xRSET); diff --git a/src/std/filters/test/syncTest.c b/src/std/filters/test/syncTest.c new file mode 100644 index 000000000..11c772491 --- /dev/null +++ b/src/std/filters/test/syncTest.c @@ -0,0 +1,367 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#include "dbStaticLib.h" +#include "dbAccessDefs.h" +#include "db_field_log.h" +#include "dbCommon.h" +#include "dbChannel.h" +#include "chfPlugin.h" +#include "epicsUnitTest.h" +#include "epicsTime.h" +#include "dbState.h" +#include "testMain.h" + +#define PATTERN 0x55 + +epicsShareExtern void (*pvar_func_syncInitialize)(void); + +static db_field_log fl; +static dbStateId red; + +static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) { + return !(memcmp(pfl1, pfl2, sizeof(db_field_log))); +} + +static void fl_setup(dbChannel *chan, db_field_log *pfl, long val) { + struct dbCommon *prec = dbChannelRecord(chan); + + pfl->ctx = dbfl_context_event; + pfl->type = dbfl_type_val; + pfl->stat = prec->stat; + pfl->sevr = prec->sevr; + pfl->time = prec->time; + pfl->field_type = DBF_LONG; + pfl->no_elements = 1; + /* + * use memcpy to avoid a bus error on + * union copy of char in the db at an odd + * address + */ + memcpy(&pfl->u.v.field, + dbChannelField(chan), + dbChannelFieldSize(chan)); + pfl->u.v.field.dbf_long = val; +} + +static void testHead (char* title) { + testDiag("--------------------------------------------------------"); + testDiag(title); + testDiag("--------------------------------------------------------"); +} + +static void mustDrop(dbChannel *pch, db_field_log *pfl2, char* m) { + db_field_log *pfl = dbChannelRunPreChain(pch, pfl2); + testOk(NULL == pfl, "filter drops field_log (%s)", m); +} + +static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m) { + db_field_log *pfl; + + testDiag("%s: filter must pass twice", m); + pfl = dbChannelRunPreChain(pch, pfl2); + testOk(pfl2 == pfl, "call 1 does not drop or replace field_log"); + pfl = dbChannelRunPreChain(pch, pfl2); + testOk(pfl2 == pfl, "call 2 does not drop or replace field_log"); +} + +static void mustPassOld(dbChannel *pch, db_field_log *old, db_field_log *cur, char* m) { + db_field_log *pfl = dbChannelRunPreChain(pch, cur); + + testOk(old == pfl, "filter passes previous field log (%s)", m); +} + +static void mustPass(dbChannel *pch, db_field_log *cur, char* m) { + db_field_log *pfl = dbChannelRunPreChain(pch, cur); + + testOk(cur == pfl, "filter passes field_log (%s)", m); +} + +static void checkCtxRead(dbChannel *pch, dbStateId id) { + fl.ctx = dbfl_context_read; + dbStateClear(id); + mustPassTwice(pch, &fl, "ctx='read', state=FALSE"); + dbStateSet(id); + mustPassTwice(pch, &fl, "ctx='read', state=TRUE"); + dbStateClear(id); + mustPassTwice(pch, &fl, "ctx='read', state=FALSE"); + fl.ctx = dbfl_context_event; +} + +static void checkAndOpenChannel(dbChannel *pch, const chFilterPlugin *plug) { + ELLNODE *node; + chFilter *filter; + chPostEventFunc *cb_out = NULL; + void *arg_out = NULL; + db_field_log fl1; + + testDiag("Test filter structure and open channel"); + + testOk((ellCount(&pch->filters) == 1), "channel has one plugin"); + + fl1 = fl; + node = ellFirst(&pch->filters); + filter = CONTAINER(node, chFilter, list_node); + plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1); + testOk(!!(cb_out) && !!(arg_out), "register_pre registers one filter with argument"); + testOk(fl_equal(&fl1, &fl), "register_pre does not change field_log data type"); + + testOk(!(dbChannelOpen(pch)), "dbChannel with plugin sync opened"); + node = ellFirst(&pch->pre_chain); + filter = CONTAINER(node, chFilter, pre_node); + testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg != NULL), + "sync has one filter with argument in pre chain"); + testOk((ellCount(&pch->post_chain) == 0), "sync has no filter in post chain"); + + checkCtxRead(pch, red); +} + +void xRecord_registerRecordDeviceDriver(struct dbBase *); + +MAIN(syncTest) +{ + dbChannel *pch; + const chFilterPlugin *plug; + char myname[] = "sync"; + db_field_log *pfl[10]; + int i; + + testPlan(0); + + db_init_events(); + + if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + testAbort("Database description not loaded"); + + (*pvar_func_syncInitialize)(); /* manually initialize plugin */ + xRecord_registerRecordDeviceDriver(pdbbase); + + if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + testAbort("Test database not loaded"); + + testOk(!!(plug = dbFindFilter(myname, strlen(myname))), "plugin %s registered correctly", myname); + testOk(!!(red = dbStateCreate("red")), "state 'red' created successfully"); + + /* nonexisting state */ + testOk(!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\",\"s\":\"blue\"}}")), + "dbChannel with sync (m='while' s='blue') (nonex state) failed"); + /* missing state */ + testOk(!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\"}}")), + "dbChannel with sync (m='while') (no state) failed"); + /* missing mode */ + testOk(!(pch = dbChannelCreate("x.VAL{\"sync\":{\"s\":\"red\"}}")), + "dbChannel with sync (s='red') (no mode) failed"); + + /* mode WHILE */ + + testHead("Mode WHILE (m='while', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='while' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustDrop(pch, pfl[0], "state=FALSE, log0"); + mustDrop(pch, pfl[1], "state=FALSE, log1"); + mustDrop(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustPass(pch, pfl[3], "state=TRUE, log3"); + mustPass(pch, pfl[4], "state=TRUE, log4"); + mustPass(pch, pfl[5], "state=TRUE, log5"); + dbStateClear(red); + mustDrop(pch, pfl[6], "state=FALSE, log6"); + mustDrop(pch, pfl[7], "state=FALSE, log7"); + mustDrop(pch, pfl[8], "state=FALSE, log8"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + /* mode UNLESS */ + + testHead("Mode UNLESS (m='unless', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"unless\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='unless' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustPass(pch, pfl[0], "state=FALSE, log0"); + mustPass(pch, pfl[1], "state=FALSE, log1"); + mustPass(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustDrop(pch, pfl[3], "state=TRUE, log3"); + mustDrop(pch, pfl[4], "state=TRUE, log4"); + mustDrop(pch, pfl[5], "state=TRUE, log5"); + dbStateClear(red); + mustPass(pch, pfl[6], "state=FALSE, log6"); + mustPass(pch, pfl[7], "state=FALSE, log7"); + mustPass(pch, pfl[8], "state=FALSE, log8"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + /* mode BEFORE */ + + testHead("Mode BEFORE (m='before', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"before\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='before' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustDrop(pch, pfl[0], "state=FALSE, log0"); + mustDrop(pch, pfl[1], "state=FALSE, log1"); + mustDrop(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustPassOld(pch, pfl[2], pfl[3], "state=TRUE, log3, pass=log2"); + mustDrop(pch, pfl[4], "state=TRUE, log4"); + mustDrop(pch, pfl[5], "state=TRUE, log5"); + mustDrop(pch, pfl[6], "state=TRUE, log6"); + dbStateClear(red); + mustDrop(pch, pfl[7], "state=FALSE, log7"); + mustDrop(pch, pfl[8], "state=FALSE, log8"); + mustDrop(pch, pfl[9], "state=FALSE, log9"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + /* mode FIRST */ + + testHead("Mode FIRST (m='first', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"first\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='first' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustDrop(pch, pfl[0], "state=FALSE, log0"); + mustDrop(pch, pfl[1], "state=FALSE, log1"); + mustDrop(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustPass(pch, pfl[3], "state=TRUE, log3"); + mustDrop(pch, pfl[4], "state=TRUE, log4"); + mustDrop(pch, pfl[5], "state=TRUE, log5"); + dbStateClear(red); + mustDrop(pch, pfl[6], "state=FALSE, log6"); + mustDrop(pch, pfl[7], "state=FALSE, log7"); + mustDrop(pch, pfl[8], "state=FALSE, log8"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + /* mode LAST */ + + testHead("Mode LAST (m='last', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"last\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='last' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustDrop(pch, pfl[0], "state=FALSE, log0"); + mustDrop(pch, pfl[1], "state=FALSE, log1"); + mustDrop(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustDrop(pch, pfl[3], "state=TRUE, log3"); + mustDrop(pch, pfl[4], "state=TRUE, log4"); + mustDrop(pch, pfl[5], "state=TRUE, log5"); + dbStateClear(red); + mustPassOld(pch, pfl[5], pfl[6], "state=TRUE, log6, pass=log5"); + mustDrop(pch, pfl[7], "state=FALSE, log7"); + mustDrop(pch, pfl[8], "state=FALSE, log8"); + mustDrop(pch, pfl[9], "state=FALSE, log9"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + /* mode AFTER */ + + testHead("Mode AFTER (m='after', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"after\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='after' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustDrop(pch, pfl[0], "state=FALSE, log0"); + mustDrop(pch, pfl[1], "state=FALSE, log1"); + mustDrop(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustDrop(pch, pfl[3], "state=TRUE, log3"); + mustDrop(pch, pfl[4], "state=TRUE, log4"); + mustDrop(pch, pfl[5], "state=TRUE, log5"); + dbStateClear(red); + mustPass(pch, pfl[6], "state=FALSE, log6"); + mustDrop(pch, pfl[7], "state=FALSE, log7"); + mustDrop(pch, pfl[8], "state=FALSE, log8"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + dbChannelDelete(pch); + dbFreeBase(pdbbase); + + return testDone(); +} + +#define GEN_SIZE_OFFSET +#include "xRecord.h" + +#include +#include + +static rset xRSET; +epicsExportAddress(rset,xRSET); diff --git a/src/std/filters/test/tsTest.c b/src/std/filters/test/tsTest.c new file mode 100644 index 000000000..9c54063ce --- /dev/null +++ b/src/std/filters/test/tsTest.c @@ -0,0 +1,113 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#include "dbStaticLib.h" +#include "dbAccessDefs.h" +#include "chfPlugin.h" +#include "epicsUnitTest.h" +#include "epicsTime.h" +#include "testMain.h" + +#define PATTERN 0x55 + +epicsShareExtern void (*pvar_func_tsInitialize)(void); + +static db_field_log fl; + +static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) { + return !(memcmp(pfl1, pfl2, sizeof(db_field_log))); +} + +static int fl_equal_ex_ts(const db_field_log *pfl1, const db_field_log *pfl2) { + db_field_log fl1 = *pfl1; + + fl1.time = pfl2->time; + return fl_equal(&fl1, pfl2); +} + +void xRecord_registerRecordDeviceDriver(struct dbBase *); + +MAIN(tsTest) +{ + dbChannel *pch; + chFilter *filter; + const chFilterPlugin *plug; + char ts[] = "ts"; + ELLNODE *node; + chPostEventFunc *cb_out = NULL; + void *arg_out = NULL; + db_field_log fl1; + db_field_log *pfl2; + epicsTimeStamp stamp, now; + + testPlan(12); + + db_init_events(); + + if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + testAbort("Database description not loaded"); + + (*pvar_func_tsInitialize)(); /* manually initialize plugin */ + xRecord_registerRecordDeviceDriver(pdbbase); + + if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + testAbort("Test database not loaded"); + + testOk(!!(plug = dbFindFilter(ts, strlen(ts))), "plugin ts registered correctly"); + + testOk(!!(pch = dbChannelCreate("x.VAL{\"ts\":{}}")), "dbChannel with plugin ts created"); + testOk((ellCount(&pch->filters) == 1), "channel has one plugin"); + + memset(&fl, PATTERN, sizeof(fl)); + fl1 = fl; + node = ellFirst(&pch->filters); + filter = CONTAINER(node, chFilter, list_node); + plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1); + testOk(!!(cb_out) && !(arg_out), "register_pre registers one filter w/o argument"); + testOk(fl_equal(&fl1, &fl), "register_pre does not change field_log data type"); + + testOk(!(dbChannelOpen(pch)), "dbChannel with plugin ts opened"); + node = ellFirst(&pch->pre_chain); + filter = CONTAINER(node, chFilter, pre_node); + testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg == NULL), + "ts has one filter w/o argument in pre chain"); + testOk((ellCount(&pch->post_chain) == 0), "ts has no filter in post chain"); + + memset(&fl, PATTERN, sizeof(fl)); + fl1 = fl; + pfl2 = dbChannelRunPreChain(pch, &fl1); + testOk(pfl2 == &fl1, "ts filter does not drop or replace field_log"); + testOk(fl_equal_ex_ts(&fl1, pfl2), "ts filter does not change field_log data"); + + testOk(!!(pfl2 = db_create_read_log(pch)), "create field log from channel"); + stamp = pfl2->time; + pfl2 = dbChannelRunPreChain(pch, &fl1); + epicsTimeGetCurrent(&now); + testOk(epicsTimeDiffInSeconds(&pfl2->time, &stamp) > 0. && + epicsTimeDiffInSeconds(&now, &pfl2->time) > 0., "ts filter sets time stamp to \"now\""); + + dbChannelDelete(pch); + dbFreeBase(pdbbase); + + return testDone(); +} + +#define GEN_SIZE_OFFSET +#include "xRecord.h" + +#include +#include + +static rset xRSET; +epicsExportAddress(rset,xRSET); diff --git a/src/std/filters/test/xRecord.dbd b/src/std/filters/test/xRecord.dbd new file mode 100644 index 000000000..4837871a4 --- /dev/null +++ b/src/std/filters/test/xRecord.dbd @@ -0,0 +1,12 @@ +# This is a combined minimal DBD and DB file + +recordtype(x) { + field(NAME, DBF_STRING) { + prompt("Record Name") + special(SPC_NOMOD) + size(61) + } + field(VAL, DBF_LONG) { + prompt("Value") + } +} diff --git a/src/std/filters/ts.c b/src/std/filters/ts.c new file mode 100644 index 000000000..5bf2d4898 --- /dev/null +++ b/src/std/filters/ts.c @@ -0,0 +1,67 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#include +#include +#include + +static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { + epicsTimeStamp now; + epicsTimeGetCurrent(&now); + + /* If string or array, must make a copy (to ensure coherence between time and data) */ + if (pfl->type == dbfl_type_rec) { + dbChannelMakeArrayCopy(pvt, pfl, chan); + } + + pfl->time = now; + return pfl; +} + +static void channelRegisterPre(dbChannel *chan, void *pvt, + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) +{ + *cb_out = filter; +} + +static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent) +{ + printf("%*s plugin ts\n", indent, ""); +} + +static chfPluginIf pif = { + NULL, /* allocPvt, */ + NULL, /* freePvt, */ + + NULL, /* parse_error, */ + NULL, /* parse_ok, */ + + NULL, /* channel_open, */ + channelRegisterPre, + NULL, /* channelRegisterPost, */ + channel_report, + NULL /* channel_close */ +}; + +static void tsInitialize(void) +{ + static int firstTime = 1; + + if(!firstTime) return; + firstTime = 0; + + chfPluginRegister("ts", &pif, NULL); +} + +epicsExportRegistrar(tsInitialize); diff --git a/src/std/softIoc/base.dbd b/src/std/softIoc/base.dbd index 894a46722..3134fc4fe 100644 --- a/src/std/softIoc/base.dbd +++ b/src/std/softIoc/base.dbd @@ -10,6 +10,9 @@ include "menuConvert.dbd" # Record types include "stdRecords.dbd" +# Channel filters & plugins +include "filters.dbd" + # "Soft Channel", "Raw Soft Channel", and "Async Soft Channel" device support include "devSoft.dbd"