diff --git a/src/ioc/db/dbIocRegister.c b/src/ioc/db/dbIocRegister.c index 1a1aafb9a..07c9836c2 100644 --- a/src/ioc/db/dbIocRegister.c +++ b/src/ioc/db/dbIocRegister.c @@ -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); diff --git a/src/ioc/db/dbJLink.c b/src/ioc/db/dbJLink.c index dc0048964..7e84bda2a 100644 --- a/src/ioc/db/dbJLink.c +++ b/src/ioc/db/dbJLink.c @@ -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; +} diff --git a/src/ioc/db/dbJLink.h b/src/ioc/db/dbJLink.h index 07c07bcd4..38476b1fd 100644 --- a/src/ioc/db/dbJLink.h +++ b/src/ioc/db/dbJLink.h @@ -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 } diff --git a/src/std/link/lnkCalc.c b/src/std/link/lnkCalc.c index 58f10a20d..545be430b 100644 --- a/src/std/link/lnkCalc.c +++ b/src/std/link/lnkCalc.c @@ -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); diff --git a/src/std/link/lnkConst.c b/src/std/link/lnkConst.c index 3dc08051f..7062ff1f9 100644 --- a/src/std/link/lnkConst.c +++ b/src/std/link/lnkConst.c @@ -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);