Adding dbChannel JSON parser and test files.
Passes 33 tests.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
Reference in New Issue
Block a user