Added JLink reporting infrastructure

Command 'dbjlr <record|*> <level>' calls the report method for
all JSON links in all records, or in one named record.
Added level and indent arguments to the jlif::report() method.

Added jlif::map_children() method for recursing through all
JSON links, plus dbJLinkMapChildren() and dbJLinkMapAll() APIs.

Implemented the report and map_children methods in the const
and calc link types.
This commit is contained in:
Andrew Johnson
2016-09-05 14:25:33 -05:00
parent 19447dc7ff
commit cd49e245c2
5 changed files with 394 additions and 43 deletions

View File

@@ -16,6 +16,7 @@
#include "dbCaTest.h"
#include "dbEvent.h"
#include "dbIocRegister.h"
#include "dbJLink.h"
#include "dbLock.h"
#include "dbNotify.h"
#include "dbScan.h"
@@ -109,6 +110,16 @@ static void dbcarCallFunc(const iocshArgBuf *args)
dbcar(args[0].sval,args[1].ival);
}
/* dbjlr */
static const iocshArg dbjlrArg0 = { "record name",iocshArgString};
static const iocshArg dbjlrArg1 = { "level",iocshArgInt};
static const iocshArg * const dbjlrArgs[2] = {&dbjlrArg0,&dbjlrArg1};
static const iocshFuncDef dbjlrFuncDef = {"dbjlr",2,dbjlrArgs};
static void dbjlrCallFunc(const iocshArgBuf *args)
{
dbjlr(args[0].sval,args[1].ival);
}
/* dbel */
static const iocshArg dbelArg0 = { "record name",iocshArgString};
static const iocshArg dbelArg1 = { "level",iocshArgInt};
@@ -395,6 +406,7 @@ void dbIocRegister(void)
iocshRegister(&dbsrFuncDef,dbsrCallFunc);
iocshRegister(&dbcarFuncDef,dbcarCallFunc);
iocshRegister(&dbelFuncDef,dbelCallFunc);
iocshRegister(&dbjlrFuncDef,dbjlrCallFunc);
iocshRegister(&dbLoadDatabaseFuncDef,dbLoadDatabaseCallFunc);
iocshRegister(&dbLoadRecordsFuncDef,dbLoadRecordsCallFunc);

View File

@@ -11,6 +11,7 @@
#include "epicsAssert.h"
#include "dbmf.h"
#include "dbStaticLib.h"
#include "errlog.h"
#include "yajl_alloc.h"
#include "yajl_parse.h"
@@ -20,6 +21,7 @@
#include "dbCommon.h"
#include "dbLink.h"
#include "dbJLink.h"
#include "dbLock.h"
#include "dbStaticLib.h"
#include "link.h"
@@ -406,7 +408,133 @@ long dbJLinkInit(struct link *plink)
return 0;
}
void dbJLinkFree(jlink *pjlink) {
void dbJLinkFree(jlink *pjlink)
{
if (pjlink)
pjlink->pif->free_jlink(pjlink);
}
void dbJLinkReport(jlink *pjlink, int level, int indent) {
if (pjlink && pjlink->pif->report)
pjlink->pif->report(pjlink, level, indent);
}
long dbJLinkMapChildren(struct link *plink, jlink_map_fn rtn, void *ctx)
{
jlink *pjlink;
long status;
if (!plink || plink->type != JSON_LINK)
return 0;
pjlink = plink->value.json.jlink;
if (!pjlink)
return 0;
status = rtn(pjlink, ctx);
if (!status && pjlink->pif->map_children)
status = pjlink->pif->map_children(pjlink, rtn, ctx);
return status;
}
long dbjlr(const char *recname, int level)
{
DBENTRY dbentry;
DBENTRY * const pdbentry = &dbentry;
long status;
if (!recname || recname[0] == '\0' || !strcmp(recname, "*")) {
recname = NULL;
printf("JSON links in all records\n\n");
}
else
printf("JSON links in record '%s'\n\n", recname);
dbInitEntry(pdbbase, pdbentry);
for (status = dbFirstRecordType(pdbentry);
status == 0;
status = dbNextRecordType(pdbentry)) {
for (status = dbFirstRecord(pdbentry);
status == 0;
status = dbNextRecord(pdbentry)) {
dbRecordType *pdbRecordType = pdbentry->precordType;
dbCommon *precord = pdbentry->precnode->precord;
char *prec = (char *) precord;
int i;
if (recname && strcmp(recname, dbGetRecordName(pdbentry)))
continue;
if (dbIsAlias(pdbentry))
continue;
printf(" %s record '%s':\n", pdbRecordType->name, precord->name);
dbScanLock(precord);
for (i = 0; i < pdbRecordType->no_links; i++) {
int idx = pdbRecordType->link_ind[i];
dbFldDes *pdbFldDes = pdbRecordType->papFldDes[idx];
DBLINK *plink = (DBLINK *) (prec + pdbFldDes->offset);
if (plink->type != JSON_LINK)
continue;
if (!dbLinkIsDefined(plink))
continue;
printf(" Link field '%s':\n", pdbFldDes->name);
dbJLinkReport(plink->value.json.jlink, level, 6);
}
dbScanUnlock(precord);
if (recname)
goto done;
}
}
done:
return 0;
}
long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx)
{
DBENTRY dbentry;
DBENTRY * const pdbentry = &dbentry;
long status;
if (recname && (recname[0] = '\0' || !strcmp(recname, "*")))
recname = NULL;
dbInitEntry(pdbbase, pdbentry);
for (status = dbFirstRecordType(pdbentry);
status == 0;
status = dbNextRecordType(pdbentry)) {
for (status = dbFirstRecord(pdbentry);
status == 0;
status = dbNextRecord(pdbentry)) {
dbRecordType *pdbRecordType = pdbentry->precordType;
dbCommon *precord = pdbentry->precnode->precord;
char *prec = (char *) precord;
int i;
if (recname && strcmp(recname, dbGetRecordName(pdbentry)))
continue;
if (dbIsAlias(pdbentry))
continue;
dbScanLock(precord);
for (i = 0; i < pdbRecordType->no_links; i++) {
int idx = pdbRecordType->link_ind[i];
dbFldDes *pdbFldDes = pdbRecordType->papFldDes[idx];
DBLINK *plink = (DBLINK *) (prec + pdbFldDes->offset);
status = dbJLinkMapChildren(plink, rtn, ctx);
if (status)
goto unlock;
}
unlock:
dbScanUnlock(precord);
if (status || recname)
goto done;
}
}
done:
return status;
}

View File

@@ -32,29 +32,83 @@ struct lset;
struct jlif;
typedef struct jlink {
struct jlif *pif;
struct jlink *parent;
int parseDepth;
struct jlif *pif; /* Link methods */
struct jlink *parent; /* NULL for top-level links */
int parseDepth; /* Used by parser, unused afterwards */
/* Link types extend or embed this structure for private storage */
} jlink;
typedef long (*jlink_map_fn)(jlink *, void *ctx);
typedef struct jlif {
/* Optional parser methods below given as NULL are equivalent to
* providing a routine that always returns jlif_stop, meaning that
* this JSON construct is not allowed at this point in the parse.
*/
const char *name;
/* Name for the link type, used in link value */
jlink* (*alloc_jlink)(short dbfType);
/* Required, allocate new link structure */
void (*free_jlink)(jlink *);
/* Required, release all resources allocated for link */
jlif_result (*parse_null)(jlink *);
/* Optional, parser saw a null value */
jlif_result (*parse_boolean)(jlink *, int val);
/* Optional, parser saw a boolean value */
jlif_result (*parse_integer)(jlink *, long num);
/* Optional, parser saw an integer value */
jlif_result (*parse_double)(jlink *, double num);
/* Optional, parser saw a double value */
jlif_result (*parse_string)(jlink *, const char *val, size_t len);
/* Optional, parser saw a string value */
jlif_key_result (*parse_start_map)(jlink *);
/* Optional, parser saw an open-brace '{'. Return jlif_key_child_link
* to expect a child link next (extra key/value pairs may follow).
*/
jlif_result (*parse_map_key)(jlink *, const char *key, size_t len);
/* Optional, parser saw a map key */
jlif_result (*parse_end_map)(jlink *);
/* Optional, parser saw a close-brace '}' */
jlif_result (*parse_start_array)(jlink *);
/* Optional, parser saw an open-bracket */
jlif_result (*parse_end_array)(jlink *);
/* Optional, parser saw a close-bracket */
void (*end_child)(jlink *parent, jlink *child);
/* Optional, called with pointer to the new child link after
* parse_start_map() returned jlif_key_child_link */
struct lset* (*get_lset)(const jlink *);
void (*report)(const jlink *);
/* Required, return lset for this link instance */
void (*report)(const jlink *, int level, int indent);
/* Optional, print status information about this link instance, then
* if (level > 0) print a link identifier (at indent+2) and call
* dbJLinkReport(child, level-1, indent+4)
* for each child.
*/
long (*map_children)(jlink *, jlink_map_fn rtn, void *ctx);
/* Optional, call dbJLinkMapChildren() on all embedded links.
* Stop immediately and return status if non-zero.
*/
/* Link types must NOT extend this table with their own routines,
* this space is reserved for extensions to the jlink interface.
*/
} jlif;
epicsShareFunc long dbJLinkParse(const char *json, size_t len, short dbfType,
@@ -62,6 +116,13 @@ epicsShareFunc long dbJLinkParse(const char *json, size_t len, short dbfType,
epicsShareFunc long dbJLinkInit(struct link *plink);
epicsShareFunc void dbJLinkFree(jlink *);
epicsShareFunc void dbJLinkReport(jlink *, int level, int indent);
epicsShareFunc long dbJLinkMapChildren(struct link *,
jlink_map_fn rtn, void *ctx);
epicsShareFunc long dbjlr(const char *recname, int level);
epicsShareFunc long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx);
#ifdef __cplusplus
}

View File

@@ -12,7 +12,6 @@
#include "alarm.h"
#include "dbDefs.h"
#include "dbmf.h"
#include "errlog.h"
#include "epicsAssert.h"
#include "epicsString.h"
@@ -56,6 +55,9 @@ typedef struct calc_link {
epicsEnum16 stat;
epicsEnum16 sevr;
short prec;
char *expr;
char *major;
char *minor;
char *post_expr;
char *post_major;
char *post_minor;
@@ -70,7 +72,8 @@ static lset lnkCalc_lset;
/*************************** jlif Routines **************************/
static jlink* lnkCalc_alloc(short dbfType) {
static jlink* lnkCalc_alloc(short dbfType)
{
calc_link *clink = calloc(1, sizeof(struct calc_link));
IFDEBUG(10)
@@ -86,7 +89,8 @@ static jlink* lnkCalc_alloc(short dbfType) {
return &clink->jlink;
}
static void lnkCalc_free(jlink *pjlink) {
static void lnkCalc_free(jlink *pjlink)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
int i;
@@ -96,6 +100,9 @@ static void lnkCalc_free(jlink *pjlink) {
for (i = 0; i < clink->nArgs; i++)
dbJLinkFree(clink->inp[i].value.json.jlink);
free(clink->expr);
free(clink->major);
free(clink->minor);
free(clink->post_expr);
free(clink->post_major);
free(clink->post_minor);
@@ -103,7 +110,8 @@ static void lnkCalc_free(jlink *pjlink) {
free(clink);
}
static jlif_result lnkCalc_integer(jlink *pjlink, long num) {
static jlif_result lnkCalc_integer(jlink *pjlink, long num)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
IFDEBUG(10)
@@ -130,7 +138,8 @@ static jlif_result lnkCalc_integer(jlink *pjlink, long num) {
return jlif_continue;
}
static jlif_result lnkCalc_double(jlink *pjlink, double num) {
static jlif_result lnkCalc_double(jlink *pjlink, double num)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
IFDEBUG(10)
@@ -152,7 +161,8 @@ static jlif_result lnkCalc_double(jlink *pjlink, double num) {
return jlif_continue;
}
static jlif_result lnkCalc_string(jlink *pjlink, const char *val, size_t len) {
static jlif_result lnkCalc_string(jlink *pjlink, const char *val, size_t len)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
char *inbuf, *postbuf;
short err;
@@ -176,27 +186,32 @@ static jlif_result lnkCalc_string(jlink *pjlink, const char *val, size_t len) {
return jlif_stop;
}
if (clink->pstate == ps_major)
clink->post_major = postbuf;
else if (clink->pstate == ps_minor)
clink->post_minor = postbuf;
else
clink->post_expr = postbuf;
inbuf = epicsStrnDup(val, len);
inbuf = dbmfStrndup(val, len);
if (clink->pstate == ps_major) {
clink->major = inbuf;
clink->post_major = postbuf;
}
else if (clink->pstate == ps_minor) {
clink->minor = inbuf;
clink->post_minor = postbuf;
}
else {
clink->expr = inbuf;
clink->post_expr = postbuf;
}
if (postfix(inbuf, postbuf, &err) < 0) {
errlogPrintf("lnkCalc: Error in calc expression, %s\n",
calcErrorStr(err));
dbmfFree(inbuf);
return jlif_stop;
}
dbmfFree(inbuf);
return jlif_continue;
}
static jlif_key_result lnkCalc_start_map(jlink *pjlink) {
static jlif_key_result lnkCalc_start_map(jlink *pjlink)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
IFDEBUG(10)
@@ -213,7 +228,8 @@ static jlif_key_result lnkCalc_start_map(jlink *pjlink) {
return jlif_continue;
}
static jlif_result lnkCalc_map_key(jlink *pjlink, const char *key, size_t len) {
static jlif_result lnkCalc_map_key(jlink *pjlink, const char *key, size_t len)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
IFDEBUG(10)
@@ -251,7 +267,8 @@ static jlif_result lnkCalc_map_key(jlink *pjlink, const char *key, size_t len) {
return jlif_continue;
}
static jlif_result lnkCalc_end_map(jlink *pjlink) {
static jlif_result lnkCalc_end_map(jlink *pjlink)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
IFDEBUG(10)
@@ -263,7 +280,8 @@ static jlif_result lnkCalc_end_map(jlink *pjlink) {
return jlif_continue;
}
static jlif_result lnkCalc_start_array(jlink *pjlink) {
static jlif_result lnkCalc_start_array(jlink *pjlink)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
IFDEBUG(10)
@@ -277,7 +295,8 @@ static jlif_result lnkCalc_start_array(jlink *pjlink) {
return jlif_continue;
}
static jlif_result lnkCalc_end_array(jlink *pjlink) {
static jlif_result lnkCalc_end_array(jlink *pjlink)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
IFDEBUG(10)
@@ -289,7 +308,8 @@ static jlif_result lnkCalc_end_array(jlink *pjlink) {
return jlif_continue;
}
static void lnkCalc_end_child(jlink *parent, jlink *child) {
static void lnkCalc_end_child(jlink *parent, jlink *child)
{
calc_link *clink = CONTAINER(parent, struct calc_link, jlink);
struct link *plink;
@@ -307,20 +327,64 @@ static void lnkCalc_end_child(jlink *parent, jlink *child) {
plink->value.json.jlink = child;
}
static struct lset* lnkCalc_get_lset(const jlink *pjlink) {
static struct lset* lnkCalc_get_lset(const jlink *pjlink)
{
IFDEBUG(10)
printf("lnkCalc_get_lset(calc@%p)\n", pjlink);
return &lnkCalc_lset;
}
static void lnkCalc_report(const jlink *pjlink) {
static void lnkCalc_report(const jlink *pjlink, int level, int indent)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
int i;
IFDEBUG(10)
printf("lnkCalc_report(calc@%p)\n", clink);
/* FIXME Implement! */
printf("%*s'calc': \"%s\" = %.*g %s\n", indent, "",
clink->expr, clink->prec, clink->val,
clink->units ? clink->units : "");
if (level > 0) {
if (clink->post_major)
printf("%*s Major alarm: \"%s\"\n", indent, "",
clink->major);
if (clink->post_minor)
printf("%*s Minor alarm: \"%s\"\n", indent, "",
clink->minor);
for (i = 0; i < clink->nArgs; i++) {
struct link *plink = &clink->inp[i];
jlink *child = plink->type == JSON_LINK ?
plink->value.json.jlink : NULL;
printf("%*s Input %c: %g\n", indent, "",
i + 'A', clink->arg[i]);
if (child)
dbJLinkReport(child, level - 1, indent + 4);
}
}
}
long lnkCalc_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx)
{
calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink);
int i;
IFDEBUG(10)
printf("lnkCalc_map_children(calc@%p)\n", clink);
for (i = 0; i < clink->nArgs; i++) {
struct link *child = &clink->inp[i];
long status = dbJLinkMapChildren(child, rtn, ctx);
if (status)
return status;
}
return 0;
}
/*************************** lset Routines **************************/
@@ -431,7 +495,7 @@ static long lnkCalc_getValue(struct link *plink, short dbrType, void *pbuffer,
long nReq = 1;
dbGetLink(child, DBR_DOUBLE, &clink->arg[i], NULL, &nReq);
/* FIXME Should we look at the return status from dbGetLink? */
/* Any errors have already triggered a LINK/INVALID alarm */
}
clink->stat = 0;
clink->sevr = 0;
@@ -541,7 +605,8 @@ static jlif lnkCalcIf = {
NULL, NULL, lnkCalc_integer, lnkCalc_double, lnkCalc_string,
lnkCalc_start_map, lnkCalc_map_key, lnkCalc_end_map,
lnkCalc_start_array, lnkCalc_end_array,
lnkCalc_end_child, lnkCalc_get_lset, lnkCalc_report
lnkCalc_end_child, lnkCalc_get_lset,
lnkCalc_report, lnkCalc_map_children
};
epicsExportAddress(jlif, lnkCalcIf);

View File

@@ -56,7 +56,8 @@ static lset lnkConst_lset;
/*************************** jlif Routines **************************/
static jlink* lnkConst_alloc(short dbfType) {
static jlink* lnkConst_alloc(short dbfType)
{
clink *clink = calloc(1, sizeof(struct clink));
IFDEBUG(10)
@@ -72,7 +73,8 @@ static jlink* lnkConst_alloc(short dbfType) {
return &clink->jlink;
}
static void lnkConst_free(jlink *pjlink) {
static void lnkConst_free(jlink *pjlink)
{
clink *clink = CONTAINER(pjlink, struct clink, jlink);
IFDEBUG(10)
@@ -93,7 +95,8 @@ static void lnkConst_free(jlink *pjlink) {
free(clink);
}
static jlif_result lnkConst_integer(jlink *pjlink, long num) {
static jlif_result lnkConst_integer(jlink *pjlink, long num)
{
clink *clink = CONTAINER(pjlink, struct clink, jlink);
IFDEBUG(10)
@@ -145,7 +148,8 @@ static jlif_result lnkConst_boolean(jlink *pjlink, int val) {
return lnkConst_integer(pjlink, val);
}
static jlif_result lnkConst_double(jlink *pjlink, double num) {
static jlif_result lnkConst_double(jlink *pjlink, double num)
{
clink *clink = CONTAINER(pjlink, struct clink, jlink);
IFDEBUG(10)
@@ -194,7 +198,8 @@ static jlif_result lnkConst_double(jlink *pjlink, double num) {
return jlif_continue;
}
static jlif_result lnkConst_string(jlink *pjlink, const char *val, size_t len) {
static jlif_result lnkConst_string(jlink *pjlink, const char *val, size_t len)
{
clink *clink = CONTAINER(pjlink, struct clink, jlink);
IFDEBUG(10)
@@ -238,7 +243,8 @@ static jlif_result lnkConst_string(jlink *pjlink, const char *val, size_t len) {
return jlif_continue;
}
static jlif_result lnkConst_start_array(jlink *pjlink) {
static jlif_result lnkConst_start_array(jlink *pjlink)
{
clink *clink = CONTAINER(pjlink, struct clink, jlink);
IFDEBUG(10)
@@ -253,27 +259,104 @@ static jlif_result lnkConst_start_array(jlink *pjlink) {
return jlif_continue;
}
static jlif_result lnkConst_end_array(jlink *pjlink) {
static jlif_result lnkConst_end_array(jlink *pjlink)
{
IFDEBUG(10)
printf("lnkConst_end_array(const@%p)\n", pjlink);
return jlif_continue;
}
static struct lset* lnkConst_get_lset(const jlink *pjlink) {
static struct lset* lnkConst_get_lset(const jlink *pjlink)
{
IFDEBUG(10)
printf("lnkConst_get_lset(const@%p)\n", pjlink);
return &lnkConst_lset;
}
static void lnkConst_report(const jlink *pjlink) {
/* Report outputs:
* 'const': integer 21
* 'const': double 5.23
* 'const': string "something"
* 'const': array of 999 integers
* [1, 2, 3]
* 'const': array of 1 double
* [1.2345]
* 'const': array of 2 strings
* ["hello", "world"]
*
* Array values are only printed at level 2
* because there might be quite a few of them.
*/
static void lnkConst_report(const jlink *pjlink, int level, int indent)
{
clink *clink = CONTAINER(pjlink, struct clink, jlink);
const char * const type_names[4] = {
"bug", "integer", "double", "string"
};
const char * const dtype = type_names[clink->type & 3];
IFDEBUG(10)
printf("lnkConst_report(const@%p)\n", clink);
/* FIXME Implement! */
if (clink->type > a0) {
const char * const plural = clink->nElems > 1 ? "s" : "";
printf("%*s'const': array of %d %s%s", indent, "",
clink->nElems, dtype, plural);
if (level < 2) {
putchar('\n');
}
else {
int i;
switch (clink->type) {
case ai32:
printf("\n%*s[%d", indent+2, "", clink->value.pintegers[0]);
for (i = 1; i < clink->nElems; i++) {
printf(", %d", clink->value.pintegers[i]);
}
break;
case af64:
printf("\n%*s[%g", indent+2, "", clink->value.pdoubles[0]);
for (i = 1; i < clink->nElems; i++) {
printf(", %g", clink->value.pdoubles[i]);
}
break;
case ac40:
printf("\n%*s[\"%s\"", indent+2, "", clink->value.pstrings[0]);
for (i = 1; i < clink->nElems; i++) {
printf(", \"%s\"", clink->value.pstrings[i]);
}
break;
default:
break;
}
printf("]\n");
}
return;
}
printf("%*s'const': %s", indent, "", dtype);
switch (clink->type) {
case si32:
printf(" %d\n", clink->value.scalar_integer);
return;
case sf64:
printf(" %g\n", clink->value.scalar_double);
return;
case sc40:
printf(" \"%s\"\n", clink->value.scalar_string);
return;
default:
printf(" -- type=%d\n", clink->type);
return;
}
}
/*************************** lset Routines **************************/
@@ -474,8 +557,10 @@ static lset lnkConst_lset = {
static jlif lnkConstIf = {
"const", lnkConst_alloc, lnkConst_free,
NULL, lnkConst_boolean, lnkConst_integer, lnkConst_double, lnkConst_string,
NULL, NULL, NULL, lnkConst_start_array, lnkConst_end_array,
NULL, lnkConst_get_lset, lnkConst_report
NULL, NULL, NULL,
lnkConst_start_array, lnkConst_end_array,
NULL, lnkConst_get_lset,
lnkConst_report, NULL
};
epicsExportAddress(jlif, lnkConstIf);