Adding dbChannel JSON parser and test files.

Passes 33 tests.
This commit is contained in:
Michael Davidsaver
2012-04-27 13:21:32 -04:00
parent cf623e290a
commit 68dfeb76c0
7 changed files with 513 additions and 7 deletions
+2
View File
@@ -17,6 +17,7 @@ INC += dbAccessDefs.h
INC += dbCa.h
INC += dbAddr.h
INC += dbBkpt.h
INC += dbChannel.h
INC += dbConvert.h
INC += dbConvertFast.h
INC += dbEvent.h
@@ -53,6 +54,7 @@ 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 += dbNotify.c
+6 -6
View File
@@ -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.
\*************************************************************************/
/* dbAccessDefs.h */
/* $Revision-Id$ */
@@ -47,7 +47,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 +69,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 +97,7 @@ epicsShareExtern volatile int interruptAccept;
* MYBUFFER *pbuf1;
* MYBUFFER buf;
*************************************************************************/
/* Macros for defining each option */
#define DBRstatus \
epicsUInt16 status; /* alarm status */\
@@ -143,7 +143,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,7 +198,7 @@ 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 ) && \
+1 -1
View File
@@ -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
+246
View File
@@ -0,0 +1,246 @@
/*************************************************************************\
* Copyright (c) 2010 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.
\*************************************************************************/
#include <stdio.h>
#include <string.h>
#include "cantProceed.h"
#include "dbChannel.h"
#include "dbBase.h"
#include "link.h"
#include "dbAccessDefs.h"
#include "epicsAssert.h"
#include "errlog.h"
#include "yajl_parse.h"
typedef struct parseContext {
dbChannel *chan;
chFilter *filter;
int depth;
} parseContext;
#define CALLIF(rtn) rtn && rtn
static void chp_value(parseContext *parser, int result)
{
chFilter *filter = parser->filter;
if (!result || parser->depth > 0) return;
parser->filter = NULL;
if (filter->fif->parse_end(filter)) {
ellAdd(&parser->chan->filters, &filter->node);
}
}
static int chp_null(void * ctx)
{
parseContext *parser = (parseContext *) ctx;
chFilter *filter = parser->filter;
int result;
assert(filter);
result = CALLIF(filter->fif->parse_null) (filter );
chp_value(parser, result);
return result;
}
static int chp_boolean(void * ctx, int boolVal)
{
parseContext *parser = (parseContext *) ctx;
chFilter *filter = parser->filter;
int result;
assert(filter);
result = CALLIF(filter->fif->parse_boolean) (filter , boolVal);
chp_value(parser, result);
return result;
}
static int chp_integer(void * ctx, long integerVal)
{
parseContext *parser = (parseContext *) ctx;
chFilter *filter = parser->filter;
int result;
assert(filter);
result = CALLIF(filter->fif->parse_integer) (filter , integerVal);
chp_value(parser, result);
return result;
}
static int chp_double(void * ctx, double doubleVal)
{
parseContext *parser = (parseContext *) ctx;
chFilter *filter = parser->filter;
int result;
assert(filter);
result = CALLIF(filter->fif->parse_double) (filter , doubleVal);
chp_value(parser, result);
return result;
}
static int chp_string(void * ctx, const unsigned char * stringVal,
unsigned int stringLen)
{
parseContext *parser = (parseContext *) ctx;
chFilter *filter = parser->filter;
int result;
assert(filter);
result = CALLIF(filter->fif->parse_string) (filter , (const char *) stringVal, stringLen);
chp_value(parser, result);
return result;
}
static int chp_start_map(void * ctx)
{
parseContext *parser = (parseContext *) ctx;
chFilter *filter = parser->filter;
if (!filter) {
assert(parser->depth == 0);
return 1; /* Opening '{' */
}
++parser->depth;
return CALLIF(filter->fif->parse_start_map) (filter );
}
static int chp_map_key(void * ctx, const unsigned char * key,
unsigned int stringLen)
{
parseContext *parser = (parseContext *) ctx;
chFilter *filter = parser->filter;
const chFilterIf *fif;
int result;
if (filter) {
return CALLIF(filter->fif->parse_map_key) (filter , (const char *) key, stringLen);
}
assert(parser->depth == 0);
fif = dbFindFilter((const char *) key, stringLen);
if (!fif) {
printf("Channel filter '%.*s' not found\n", stringLen, key);
return parse_stop;
}
filter = (chFilter *) callocMustSucceed(1, sizeof(*filter), "Creating dbChannel filter");
filter->chan = parser->chan;
filter->fif = fif;
filter->puser = NULL;
result = fif->parse_start(filter);
if (result == parse_continue) {
parser->filter = filter;
} else {
free(filter);
}
return result;
}
static int chp_end_map(void * ctx)
{
parseContext *parser = (parseContext *) ctx;
chFilter *filter = parser->filter;
if (filter) {
int result = CALLIF(filter->fif->parse_end_map) (filter );
if (--parser->depth == 0) chp_value(parser, result);
return result;
}
assert(parser->depth == 0);
return parse_continue; /* Final closing '}' */
}
static int chp_start_array(void * ctx)
{
parseContext *parser = (parseContext *) ctx;
chFilter *filter = parser->filter;
assert(filter);
++parser->depth;
return CALLIF(filter->fif->parse_start_array) (filter );
}
static int chp_end_array(void * ctx)
{
parseContext *parser = (parseContext *) ctx;
chFilter *filter = parser->filter;
int result;
assert(filter);
result = CALLIF(filter->fif->parse_end_array) (filter );
if (--parser->depth == 0) chp_value(parser, result);
return result;
}
static const yajl_callbacks chp_callbacks =
{ chp_null, chp_boolean, chp_integer, chp_double, NULL, chp_string,
chp_start_map, chp_map_key, chp_end_map, chp_start_array, chp_end_array };
static const yajl_parser_config chp_config =
{ 0, 1 }; /* allowComments = NO , checkUTF8 = YES */
void dbChannelInit(dbChannel *chan)
{
ellInit(&chan->filters);
}
// Move ellInit into ChannelFind, rename it again?
long dbChannelFind(dbChannel *chan, const char *name)
{
parseContext parser =
{ chan, NULL, 0};
yajl_handle yh = yajl_alloc(&chp_callbacks, &chp_config, NULL, &parser);
size_t len = strlen(name);
yajl_status status;
status = yajl_parse(yh, (const unsigned char *) name, len);
if (status != yajl_status_ok && parser.filter) {
parser.filter->fif->parse_abort(parser.filter);
free(parser.filter);
}
yajl_free(yh);
return status == yajl_status_ok;
}
long dbChannelOpen(dbChannel *chan)
{
return S_db_notFound;
}
long dbChannelClose(dbChannel *chan)
{
return 0;
}
const chFilterIf * filt = NULL;
const char *fname = NULL;
void dbRegisterFilter(const char *key, const chFilterIf *fif)
{
/* FIXME: Add filter to a list and hash in pdbbase */
filt = fif;
fname = key;
fif->plugin_init();
}
const chFilterIf * dbFindFilter(const char *key, size_t len)
{
/* FIXME: gpHash lookup */
if (strncmp(fname, key, len) == 0)
return filt;
return NULL;
}
+83
View File
@@ -0,0 +1,83 @@
/*************************************************************************\
* Copyright (c) 2010 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.
\*************************************************************************/
#ifndef INC_dbChannel_H
#define INC_dbChannel_H
#include "dbDefs.h"
#include "dbAddr.h"
#include "ellLib.h"
#include "shareLib.h"
enum {parse_stop, parse_continue};
#ifdef __cplusplus
// extern "C" {
#endif
/* A dbChannel points to a record field, and can have multiple filters */
typedef struct dbChannel {
dbAddr addr;
ELLLIST filters;
} dbChannel;
typedef struct chFilter chFilter;
/* These routines must be implemented by each filter plug-in */
typedef struct chFilterIf {
/* Plug-in management */
void (* plugin_init)(void);
void (* plugin_exit)(void);
/* Parsing event handlers */
int (* parse_start)(chFilter *filter);
void (* parse_abort)(chFilter *filter);
int (* parse_end)(chFilter *filter);
int (* parse_null)(chFilter *filter);
int (* parse_boolean)(chFilter *filter, int boolVal);
int (* parse_integer)(chFilter *filter, long integerVal);
int (* parse_double)(chFilter *filter, double doubleVal);
int (* parse_string)(chFilter *filter, const char *stringVal,
size_t stringLen); /* NB: stringVal is not zero-terminated: */
int (* parse_start_map)(chFilter *filter);
int (* parse_map_key)(chFilter *filter, const char *key,
size_t stringLen);
int (* parse_end_map)(chFilter *filter);
int (* parse_start_array)(chFilter *filter);
int (* parse_end_array)(chFilter *filter);
/* Channel operations */
long (* channel_open)(chFilter *filter);
void (* channel_report)(chFilter *filter, int level);
/* FIXME: More routines here ... */
void (* channel_close)(chFilter *filter);
} chFilterIf;
/* A chFilter holds instance data for a single filter */
struct chFilter {
ELLNODE node;
dbChannel *chan;
const chFilterIf *fif;
void *puser;
};
epicsShareFunc void dbChannelInit(dbChannel *chan);
epicsShareFunc long dbChannelFind(dbChannel *chan, const char *name);
epicsShareFunc long dbChannelOpen(dbChannel *chan);
epicsShareFunc long dbChannelClose(dbChannel *chan);
epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif);
epicsShareFunc const chFilterIf * dbFindFilter(const char *key, size_t len);
#ifdef __cplusplus
}
#endif
#endif /* INC_dbChannel_H */
+5
View File
@@ -20,6 +20,11 @@ TESTS += callbackTest
# When we add more test programs here, this must become a vxTestHarness
TESTSPEC_vxWorks = callbackTest.munch; callbackTest
TESTPROD_HOST += dbChannelTest
dbChannelTest_SRCS += dbChannelTest.c
OBJS_IOC_vxWorks += dbChannelTest
TESTS += dbChannelTest
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
include $(TOP)/configure/RULES
+170
View File
@@ -0,0 +1,170 @@
/*************************************************************************\
* Copyright (c) 2010 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.
\*************************************************************************/
#include "dbChannel.h"
#include "epicsUnitTest.h"
#include "testMain.h"
/* Expected call bit definitions */
#define e_init 0x00000001
#define e_exit 0x00000002
#define e_start 0x00000004
#define e_abort 0x00000008
#define e_end 0x00000010
#define e_null 0x00000020
#define e_boolean 0x00000040
#define e_integer 0x00000080
#define e_double 0x00000100
#define e_string 0x00000200
#define e_start_map 0x00000400
#define e_map_key 0x00000800
#define e_end_map 0x00001000
#define e_start_array 0x00002000
#define e_end_array 0x00004000
#define e_open 0x00008000
#define e_report 0x00010000
#define e_close 0x00020000
unsigned int e;
void f_init(void)
{
testOk(e & e_init, "plugin_init expected");
}
void f_exit(void)
{
testOk(e & e_exit, "plugin_exit expected");
}
int p_start(chFilter *filter)
{
testOk(e & e_start, "parse_start expected");
return parse_continue;
}
void p_abort(chFilter *filter)
{
testOk(e & e_abort, "parse_abort expected");
}
int p_end(chFilter *filter)
{
testOk(e & e_end, "parse_end expected");
return parse_continue;
}
int p_null(chFilter *filter)
{
testOk(e & e_null, "parse_null expected");
return parse_continue;
}
int p_boolean(chFilter *filter, int boolVal)
{
testOk(e & e_boolean, "parse_boolean expected, val = %d", boolVal);
return parse_continue;
}
int p_integer(chFilter *filter, long integerVal)
{
testOk(e & e_integer, "parse_integer expected, val = %ld", integerVal);
return parse_continue;
}
int p_double(chFilter *filter, double doubleVal)
{
testOk(e & e_double, "parse_double expected, val = %g", doubleVal);
return parse_continue;
}
int p_string(chFilter *filter, const char *stringVal, size_t stringLen)
{
testOk(e & e_string, "parse_string expected, val = '%.*s'", stringLen, stringVal);
return parse_continue;
}
int p_start_map(chFilter *filter)
{
testOk(e & e_start_map, "parse_start_map expected");
return parse_continue;
}
int p_map_key(chFilter *filter, const char *key, size_t stringLen)
{
testOk(e & e_map_key, "parse_map_key expected, key = '%.*s'", stringLen, key);
return parse_continue;
}
int p_end_map(chFilter *filter)
{
testOk(e & e_end_map, "parse_end_map expected");
return parse_continue;
}
int p_start_array(chFilter *filter)
{
testOk(e & e_start_array, "parse_start_array expected");
return parse_continue;
}
int p_end_array(chFilter *filter)
{
testOk(e & e_end_array, "parse_end_array expected");
return parse_continue;
}
long c_open(chFilter *filter)
{
testOk(e & e_open, "channel_open expected");
return 0;
}
void c_report(chFilter *filter, int level)
{
testOk(e & e_report, "channel_report expected, level = %d", level);
}
void c_close(chFilter *filter)
{
testOk(e & e_close, "channel_close expected");
}
chFilterIf testIf =
{ f_init, f_exit, 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_report, c_close };
chFilterIf scalarIf =
{ f_init, f_exit, p_start, p_abort, p_end, p_null, p_boolean,
p_integer, p_double, p_string, NULL, NULL, NULL, NULL, NULL, c_open,
c_report, c_close };
MAIN(dbChannelTest)
{
dbChannel ch;
testPlan(33);
dbChannelInit(&ch);
e = 0;
testOk1(dbChannelFind(&ch, "{}"));
e = e_init;
dbRegisterFilter("test", &testIf);
e = e_start | e_null | e_end;
testOk1(dbChannelFind(&ch, "{\"test\":null}"));
e = e_start | e_start_array | e_boolean | e_integer | e_end_array | e_end;
testOk1(dbChannelFind(&ch, "{\"test\":[true,1]}"));
e = e_start | e_start_map | e_map_key | e_double | e_string | e_end_map | e_end;
testOk1(dbChannelFind(&ch, "{\"test\":{\"a\":2.7183,\"b\":\"c\"}}"));
e = e_init;
dbRegisterFilter("scalar", &scalarIf);
e = e_start | e_null | e_end;
testOk1(dbChannelFind(&ch, "{\"scalar\":null}"));
e = e_start | e_abort;
testOk1(!dbChannelFind(&ch, "{\"scalar\":[null]}"));
testOk1(!dbChannelFind(&ch, "{\"scalar\":{}}"));
return testDone();
}