From 98f656fc968736d8ac4298efbbe39592dac1fb2a Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 11 Dec 2017 00:07:26 -0600 Subject: [PATCH 01/29] Remove duplicate variables from lnkCalc.c --- src/std/link/lnkCalc.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/std/link/lnkCalc.c b/src/std/link/lnkCalc.c index e370c36d2..c63495df0 100644 --- a/src/std/link/lnkCalc.c +++ b/src/std/link/lnkCalc.c @@ -496,12 +496,8 @@ static int lnkCalc_getDBFtype(const struct link *plink) calc_link *clink = CONTAINER(plink->value.json.jlink, struct calc_link, jlink); - IFDEBUG(10) { - calc_link *clink = CONTAINER(plink->value.json.jlink, - struct calc_link, jlink); - + IFDEBUG(10) printf("lnkCalc_getDBFtype(calc@%p)\n", clink); - } return DBF_DOUBLE; } @@ -511,13 +507,9 @@ static long lnkCalc_getElements(const struct link *plink, long *nelements) calc_link *clink = CONTAINER(plink->value.json.jlink, struct calc_link, jlink); - IFDEBUG(10) { - calc_link *clink = CONTAINER(plink->value.json.jlink, - struct calc_link, jlink); - + IFDEBUG(10) printf("lnkCalc_getElements(calc@%p, (%ld))\n", clink, *nelements); - } *nelements = 1; return 0; From 8ae34ba01d4d3a86574afd3e1c9d910cfd2bbcb2 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 11 Dec 2017 00:11:18 -0600 Subject: [PATCH 02/29] Add a "state" link type using the dbState API Includes documentation in links.dbd.pod --- src/std/link/Makefile | 4 +- src/std/link/links.dbd.pod | 29 +++++ src/std/link/lnkState.c | 236 +++++++++++++++++++++++++++++++++++++ 3 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 src/std/link/lnkState.c diff --git a/src/std/link/Makefile b/src/std/link/Makefile index 31d14b825..5ad9777a8 100644 --- a/src/std/link/Makefile +++ b/src/std/link/Makefile @@ -2,7 +2,7 @@ # Copyright (c) 2016 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. +# in file LICENSE that is included with this distribution. #************************************************************************* # This is a Makefile fragment, see src/std/Makefile. @@ -13,6 +13,6 @@ DBD += links.dbd dbRecStd_SRCS += lnkConst.c dbRecStd_SRCS += lnkCalc.c +dbRecStd_SRCS += lnkState.c HTMLS += links.html - diff --git a/src/std/link/links.dbd.pod b/src/std/link/links.dbd.pod index d94e9e8f0..07e1023f2 100644 --- a/src/std/link/links.dbd.pod +++ b/src/std/link/links.dbd.pod @@ -13,6 +13,8 @@ The following additional link types are available in this release: =item * L +=item * L + =back =head2 Using JSON Links @@ -129,3 +131,30 @@ atomically with the value of the input argument. {calc: {expr:"A*B", args:[{db:"record.VAL"}, 1.5], prec:3}} =cut + +link(state, lnkStateIf) + +=head3 dbState Link C<"state"> + +A dbState link is one that reads or writes a boolean value from/to a named +global flag as implemented by the dbState facility in C. + +The value of the named flag is read or written at the time of the link I/O +operation. The boolean flag data is represented as a C that can only +hold the values zero or one; putting any non-zero numeric value to a link sets +the boolean flag value, putting a zero value clears it. + +These dbState flags can be accessed from the IOC Shell with various dbState +commands, and are also used by the C<"sync"> Channel-Access server-side filter +mechanism. + +=head4 Parameters + +The link address must be a string providing the name of the dbState object, +which will be created when the link is initialized if it doesn't already exist. + +=head4 Example + + {state:"redBeam"} + +=cut diff --git a/src/std/link/lnkState.c b/src/std/link/lnkState.c new file mode 100644 index 000000000..b1f33a552 --- /dev/null +++ b/src/std/link/lnkState.c @@ -0,0 +1,236 @@ +/*************************************************************************\ +* Copyright (c) 2017 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. +\*************************************************************************/ +/* lnkState.c */ + +/* Usage: + * {state:green} + */ + +#include +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "errlog.h" +#include "epicsAssert.h" +#include "epicsString.h" +#include "epicsTypes.h" +#include "dbAccessDefs.h" +#include "dbCommon.h" +#include "dbConvertFast.h" +#include "dbLink.h" +#include "dbJLink.h" +#include "dbStaticLib.h" +#include "dbStaticPvt.h" +#include "dbState.h" +#include "recGbl.h" +#include "epicsExport.h" + + +typedef long (*FASTCONVERT)(); + +#define IFDEBUG(n) if(slink->jlink.debug) + +typedef struct state_link { + jlink jlink; /* embedded object */ + char *name; + int val; + dbStateId state; +} state_link; + +static lset lnkState_lset; + + +/*************************** jlif Routines **************************/ + +static jlink* lnkState_alloc(short dbfType) +{ + state_link *slink = calloc(1, sizeof(struct state_link)); + + IFDEBUG(10) + printf("lnkState_alloc()\n"); + + slink->name = NULL; + slink->state = NULL; + slink->val = 0; + + IFDEBUG(10) + printf("lnkState_alloc -> state@%p\n", slink); + + return &slink->jlink; +} + +static void lnkState_free(jlink *pjlink) +{ + state_link *slink = CONTAINER(pjlink, struct state_link, jlink); + + IFDEBUG(10) + printf("lnkState_free(state@%p)\n", slink); + + free(slink->name); + free(slink); +} + +static jlif_result lnkState_string(jlink *pjlink, const char *val, size_t len) +{ + state_link *slink = CONTAINER(pjlink, struct state_link, jlink); + + IFDEBUG(10) + printf("lnkState_string(state@%p, \"%.*s\")\n", slink, (int) len, val); + + slink->name = epicsStrnDup(val, len); + return jlif_continue; +} + +static struct lset* lnkState_get_lset(const jlink *pjlink) +{ + state_link *slink = CONTAINER(pjlink, struct state_link, jlink); + + IFDEBUG(10) + printf("lnkState_get_lset(state@%p)\n", pjlink); + + return &lnkState_lset; +} + +static void lnkState_report(const jlink *pjlink, int level, int indent) +{ + state_link *slink = CONTAINER(pjlink, struct state_link, jlink); + + IFDEBUG(10) + printf("lnkState_report(state@%p)\n", slink); + + printf("%*s'state': \"%s\" = %d\n", indent, "", + slink->name, slink->val); +} + +/*************************** lset Routines **************************/ + +static void lnkState_open(struct link *plink) +{ + state_link *slink = CONTAINER(plink->value.json.jlink, + struct state_link, jlink); + + IFDEBUG(10) + printf("lnkState_open(state@%p)\n", slink); + + slink->state = dbStateCreate(slink->name); +} + +static void lnkState_remove(struct dbLocker *locker, struct link *plink) +{ + state_link *slink = CONTAINER(plink->value.json.jlink, + struct state_link, jlink); + + IFDEBUG(10) + printf("lnkState_remove(state@%p)\n", slink); + + free(slink->name); + free(slink); + + plink->value.json.jlink = NULL; +} + +static int lnkState_isConn(const struct link *plink) +{ + state_link *slink = CONTAINER(plink->value.json.jlink, + struct state_link, jlink); + + IFDEBUG(10) + printf("lnkState_isConn(state@%p)\n", slink); + + return !! slink->state; +} + +static int lnkState_getDBFtype(const struct link *plink) +{ + state_link *slink = CONTAINER(plink->value.json.jlink, + struct state_link, jlink); + + IFDEBUG(10) + printf("lnkState_getDBFtype(state@%p)\n", slink); + + return DBF_LONG; +} + +static long lnkState_getElements(const struct link *plink, long *nelements) +{ + state_link *slink = CONTAINER(plink->value.json.jlink, + struct state_link, jlink); + + IFDEBUG(10) + printf("lnkState_getElements(state@%p, (%ld))\n", + slink, *nelements); + + *nelements = 1; + return 0; +} + +static long lnkState_getValue(struct link *plink, short dbrType, void *pbuffer, + long *pnRequest) +{ + state_link *slink = CONTAINER(plink->value.json.jlink, + struct state_link, jlink); + long status; + FASTCONVERT conv = dbFastPutConvertRoutine[DBR_LONG][dbrType]; + + IFDEBUG(10) + printf("lnkState_getValue(state@%p, %d, ...)\n", + slink, dbrType); + + slink->val = dbStateGet(slink->state); + status = conv(&slink->val, pbuffer, NULL); + + return status; +} + +static long lnkState_putValue(struct link *plink, short dbrType, + const void *pbuffer, long nRequest) +{ + state_link *slink = CONTAINER(plink->value.json.jlink, + struct state_link, jlink); + long status; + FASTCONVERT conv = dbFastPutConvertRoutine[dbrType][DBR_LONG]; + + IFDEBUG(10) + printf("lnkState_getValue(state@%p, %d, ...)\n", + slink, dbrType); + + if (nRequest == 0) + return 0; + + status = conv(pbuffer, &slink->val, NULL); + (slink->val ? dbStateSet : dbStateClear)(slink->state); + + return status; +} + +/************************* Interface Tables *************************/ + +static lset lnkState_lset = { + 0, 1, /* not Constant, Volatile */ + lnkState_open, lnkState_remove, + NULL, NULL, NULL, + lnkState_isConn, lnkState_getDBFtype, lnkState_getElements, + lnkState_getValue, + NULL, NULL, NULL, + NULL, NULL, + NULL, NULL, + lnkState_putValue, NULL, + NULL, NULL +}; + +static jlif lnkStateIf = { + "state", lnkState_alloc, lnkState_free, + NULL, NULL, NULL, NULL, lnkState_string, + NULL, NULL, NULL, + NULL, NULL, + NULL, lnkState_get_lset, + lnkState_report, NULL +}; +epicsExportAddress(jlif, lnkStateIf); From c0d4835e6699972c95a55bc11e9fa8b0ddeebb52 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 11 Dec 2017 23:45:35 -0600 Subject: [PATCH 03/29] lnkState: Add support for inverting the flag data Determine the truthiness of put data in its original data type. Adjust the link documentation to cover these changes. --- src/std/link/links.dbd.pod | 23 ++++++++---- src/std/link/lnkState.c | 72 +++++++++++++++++++++++++++++++------- 2 files changed, 76 insertions(+), 19 deletions(-) diff --git a/src/std/link/links.dbd.pod b/src/std/link/links.dbd.pod index 07e1023f2..9b802b513 100644 --- a/src/std/link/links.dbd.pod +++ b/src/std/link/links.dbd.pod @@ -137,12 +137,18 @@ link(state, lnkStateIf) =head3 dbState Link C<"state"> A dbState link is one that reads or writes a boolean value from/to a named -global flag as implemented by the dbState facility in C. +global flag as implemented by the dbState facility in C. The link +type can invert the sense of the dbState flag if desired. The value of the named flag is read or written at the time of the link I/O -operation. The boolean flag data is represented as a C that can only -hold the values zero or one; putting any non-zero numeric value to a link sets -the boolean flag value, putting a zero value clears it. +operation. When reading a flag, the value returned by the link will be zero or +one converted to the requested data type. When writing to a flag the boolean +value of the data written is determined in the originating data type. All +strings are regarded as true other than C<""> and C<"0"> which are both false. + +A link can be configured to invert the sense of the flag data by putting an +exclamation mark C before the first character of the flag's name in the link +address. These dbState flags can be accessed from the IOC Shell with various dbState commands, and are also used by the C<"sync"> Channel-Access server-side filter @@ -150,11 +156,14 @@ mechanism. =head4 Parameters -The link address must be a string providing the name of the dbState object, -which will be created when the link is initialized if it doesn't already exist. +The link address must be a string providing the name of the dbState object, with +an optional leading C charater to indicate the flag's value should be +inverted. The dbState object will be created when the link is initialized if it +doesn't already exist. -=head4 Example +=head4 Examples {state:"redBeam"} + {state:"!simEnable"} =cut diff --git a/src/std/link/lnkState.c b/src/std/link/lnkState.c index b1f33a552..e24773727 100644 --- a/src/std/link/lnkState.c +++ b/src/std/link/lnkState.c @@ -40,7 +40,8 @@ typedef long (*FASTCONVERT)(); typedef struct state_link { jlink jlink; /* embedded object */ char *name; - int val; + short val; + short invert; dbStateId state; } state_link; @@ -58,6 +59,7 @@ static jlink* lnkState_alloc(short dbfType) slink->name = NULL; slink->state = NULL; + slink->invert = 0; slink->val = 0; IFDEBUG(10) @@ -84,6 +86,11 @@ static jlif_result lnkState_string(jlink *pjlink, const char *val, size_t len) IFDEBUG(10) printf("lnkState_string(state@%p, \"%.*s\")\n", slink, (int) len, val); + if (len > 1 && val[0] == '!') { + slink->invert = 1; + val++; len--; + } + slink->name = epicsStrnDup(val, len); return jlif_continue; } @@ -105,8 +112,8 @@ static void lnkState_report(const jlink *pjlink, int level, int indent) IFDEBUG(10) printf("lnkState_report(state@%p)\n", slink); - printf("%*s'state': \"%s\" = %d\n", indent, "", - slink->name, slink->val); + printf("%*s'state': \"%s\" = %s%s\n", indent, "", + slink->name, slink->invert ? "! " : "", slink->val ? "TRUE" : "FALSE"); } /*************************** lset Routines **************************/ @@ -155,7 +162,7 @@ static int lnkState_getDBFtype(const struct link *plink) IFDEBUG(10) printf("lnkState_getDBFtype(state@%p)\n", slink); - return DBF_LONG; + return DBF_SHORT; } static long lnkState_getElements(const struct link *plink, long *nelements) @@ -177,13 +184,15 @@ static long lnkState_getValue(struct link *plink, short dbrType, void *pbuffer, state_link *slink = CONTAINER(plink->value.json.jlink, struct state_link, jlink); long status; - FASTCONVERT conv = dbFastPutConvertRoutine[DBR_LONG][dbrType]; + short flag; + FASTCONVERT conv = dbFastPutConvertRoutine[DBR_SHORT][dbrType]; IFDEBUG(10) printf("lnkState_getValue(state@%p, %d, ...)\n", slink, dbrType); - slink->val = dbStateGet(slink->state); + flag = dbStateGet(slink->state); + slink->val = slink->invert ^ flag; status = conv(&slink->val, pbuffer, NULL); return status; @@ -194,20 +203,59 @@ static long lnkState_putValue(struct link *plink, short dbrType, { state_link *slink = CONTAINER(plink->value.json.jlink, struct state_link, jlink); - long status; - FASTCONVERT conv = dbFastPutConvertRoutine[dbrType][DBR_LONG]; + short val; + const char *pstr; IFDEBUG(10) - printf("lnkState_getValue(state@%p, %d, ...)\n", + printf("lnkState_putValue(state@%p, %d, ...)\n", slink, dbrType); if (nRequest == 0) return 0; - status = conv(pbuffer, &slink->val, NULL); - (slink->val ? dbStateSet : dbStateClear)(slink->state); + switch(dbrType) { + case DBR_CHAR: + case DBR_UCHAR: + val = !! *(const epicsInt8 *) pbuffer; + break; - return status; + case DBR_SHORT: + case DBR_USHORT: + val = !! *(const epicsInt16 *) pbuffer; + break; + + case DBR_LONG: + case DBR_ULONG: + val = !! *(const epicsInt32 *) pbuffer; + break; + + case DBR_INT64: + case DBR_UINT64: + val = !! *(const epicsInt64 *) pbuffer; + break; + + case DBR_FLOAT: + val = !! *(const epicsFloat32 *) pbuffer; + break; + + case DBR_DOUBLE: + val = !! *(const epicsFloat64 *) pbuffer; + break; + + case DBR_STRING: /* Only "" and "0" are FALSE */ + pstr = (const char *) pbuffer; + val = (pstr[0] != 0) && ((pstr[0] != '0') || (pstr[1] != 0)); + break; + + default: + return S_db_badDbrtype; + } + slink->val = val; + + val ^= slink->invert; + (val ? dbStateSet : dbStateClear)(slink->state); + + return 0; } /************************* Interface Tables *************************/ From 5e394e4928c3dcd408428a91e4e1adcd5ba87e3e Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 20 Jan 2018 19:50:33 -0600 Subject: [PATCH 04/29] dbJLink: Handle NULL returned by jlif::alloc() better It might not mean out-of-memory, adjust error message. Add OOM and dbfType error checks to all jlif::alloc() routines. Change all IFDEBUG() in JLink types to use exported global variables. This allows debug messages to be output from the jlif::alloc() routines, since the jlink field isn't set when they're called. --- src/ioc/db/dbJLink.c | 16 ++++++++++------ src/std/link/links.dbd.pod | 38 ++++++++++++++++++++++---------------- src/std/link/lnkCalc.c | 22 +++++++++++++++++----- src/std/link/lnkConst.c | 28 ++++++++++++++++++---------- src/std/link/lnkState.c | 22 +++++++++++++++++----- 5 files changed, 84 insertions(+), 42 deletions(-) diff --git a/src/ioc/db/dbJLink.c b/src/ioc/db/dbJLink.c index ea054eee9..0bc8d6ba5 100644 --- a/src/ioc/db/dbJLink.c +++ b/src/ioc/db/dbJLink.c @@ -2,7 +2,7 @@ * Copyright (c) 2016 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. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbJLink.c */ @@ -241,25 +241,29 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) { return dbjl_return(parser, jlif_stop); } - dbmfFree(link_name); - pjlink = pjlif->alloc_jlink(parser->dbfType); if (!pjlink) { - errlogPrintf("dbJLinkInit: Out of memory\n"); + errlogPrintf("dbJLinkInit: Link type '%s' allocation failed. \n", + link_name); + dbmfFree(link_name); return dbjl_return(parser, jlif_stop); } + pjlink->pif = pjlif; - pjlink->parent = NULL; pjlink->parseDepth = 0; pjlink->debug = !!parser->lset_debug; - if (parser->pjlink) { /* We're starting a child link, save its parent */ pjlink->parent = parser->pjlink; } + else + pjlink->parent = NULL; + parser->pjlink = pjlink; parser->key_is_link = 0; + dbmfFree(link_name); + IFDEBUG(8) printf("dbjl_map_key: New %s@%p\n", pjlink ? pjlink->pif->name : "", pjlink); diff --git a/src/std/link/links.dbd.pod b/src/std/link/links.dbd.pod index 9b802b513..6eb8b77d3 100644 --- a/src/std/link/links.dbd.pod +++ b/src/std/link/links.dbd.pod @@ -33,15 +33,17 @@ database file syntax. =cut + link(const, lnkConstIf) +variable(lnkConst_debug, int) =head3 Constant Link C<"const"> -Constant links provide one or more values at link initalization time, but do not -return any data when their C routine is called. Most record types -support the use of constant links by calling C at -record initialization, which results in the constant value being loaded into the -target field at that time. +Constant links are input links that provide literal values at link initalization +time, but do not return any data when their C routine is called. +Most record types support the use of constant links on their input links by +calling C at record initialization, which results in +the constant value being loaded into the target field at that time. Note that for most record types (the C and C records are the main exceptions) it is pointless to set an input link to a constant link at @@ -72,14 +74,16 @@ converted to the desired double value at initialization, for example: =cut + link(calc, lnkCalcIf) +variable(lnkCalc_debug, int) =head3 Calculation Link C<"calc"> -Calculation links can perform simple mathematical expressions on scalar -(double-precision floating-point) values obtained from other link types and -return a single double-precision floating-point result. The expressions are -evaluated by the EPICS Calc engine, and up to 12 inputs can be provided. +Calculation links are input links that can evaluate mathematical expressions on +scalar (double- precision floating-point) values obtained from child links, and +return a double-precision floating-point result. The expressions are evaluated +by the EPICS Calc engine, and up to 12 inputs can be provided. =head4 Parameters @@ -132,13 +136,15 @@ atomically with the value of the input argument. =cut + link(state, lnkStateIf) +variable(lnkState_debug, int) =head3 dbState Link C<"state"> -A dbState link is one that reads or writes a boolean value from/to a named -global flag as implemented by the dbState facility in C. The link -type can invert the sense of the dbState flag if desired. +A dbState link is one that gets or puts a boolean value from/to a named global +flag as implemented by the dbState facility in C. The link type can +invert the sense of the dbState flag during the get or put if desired. The value of the named flag is read or written at the time of the link I/O operation. When reading a flag, the value returned by the link will be zero or @@ -156,10 +162,10 @@ mechanism. =head4 Parameters -The link address must be a string providing the name of the dbState object, with -an optional leading C charater to indicate the flag's value should be -inverted. The dbState object will be created when the link is initialized if it -doesn't already exist. +The link takes a single parameter which must be a string, providing the name of +the dbState object, with an optional leading C character to indicate that the +flag's value should be inverted. The dbState object will be created when the +link is initialized if it doesn't already exist. =head4 Examples diff --git a/src/std/link/lnkCalc.c b/src/std/link/lnkCalc.c index c63495df0..87f3b4a3e 100644 --- a/src/std/link/lnkCalc.c +++ b/src/std/link/lnkCalc.c @@ -40,7 +40,10 @@ typedef long (*FASTCONVERT)(); -#define IFDEBUG(n) if(clink->jlink.debug) +int lnkCalc_debug; +epicsExportAddress(int, lnkCalc_debug); + +#define IFDEBUG(n) if (lnkCalc_debug >= (n)) typedef struct calc_link { jlink jlink; /* embedded object */ @@ -77,10 +80,21 @@ static lset lnkCalc_lset; static jlink* lnkCalc_alloc(short dbfType) { - calc_link *clink = calloc(1, sizeof(struct calc_link)); + calc_link *clink; IFDEBUG(10) - printf("lnkCalc_alloc()\n"); + printf("lnkCalc_alloc(%d)\n", dbfType); + + if (dbfType != DBF_INLINK) { + errlogPrintf("lnkCalc: Only works with input links\n"); + return NULL; + } + + clink = calloc(1, sizeof(struct calc_link)); + if (!clink) { + errlogPrintf("lnkCalc: calloc() failed.\n"); + return NULL; + } clink->nArgs = 0; clink->pstate = ps_init; @@ -355,8 +369,6 @@ static void lnkCalc_end_child(jlink *parent, jlink *child) static struct lset* lnkCalc_get_lset(const jlink *pjlink) { - calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); - IFDEBUG(10) printf("lnkCalc_get_lset(calc@%p)\n", pjlink); diff --git a/src/std/link/lnkConst.c b/src/std/link/lnkConst.c index c2eb032c1..881bf6bff 100644 --- a/src/std/link/lnkConst.c +++ b/src/std/link/lnkConst.c @@ -2,7 +2,7 @@ * Copyright (c) 2016 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. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* lnkConst.c */ @@ -22,7 +22,10 @@ #include "epicsExport.h" -#define IFDEBUG(n) if (clink->jlink.debug) +int lnkConst_debug; +epicsExportAddress(int, lnkConst_debug); + +#define IFDEBUG(n) if (lnkConst_debug >= (n)) typedef long (*FASTCONVERT)(); @@ -48,10 +51,21 @@ static lset lnkConst_lset; static jlink* lnkConst_alloc(short dbfType) { - const_link *clink = calloc(1, sizeof(*clink)); + const_link *clink; IFDEBUG(10) - printf("lnkConst_alloc()\n"); + printf("lnkConst_alloc(%d)\n", dbfType); + + if (dbfType != DBF_INLINK) { + errlogPrintf("lnkConst: Only works with input links\n"); + return NULL; + } + + clink = calloc(1, sizeof(*clink)); + if (!clink) { + errlogPrintf("lnkConst: calloc() failed.\n"); + return NULL; + } clink->type = s0; clink->nElems = 0; @@ -146,7 +160,6 @@ static jlif_result lnkConst_integer(jlink *pjlink, long long num) static jlif_result lnkConst_boolean(jlink *pjlink, int val) { - const_link *clink = CONTAINER(pjlink, const_link, jlink); IFDEBUG(10) printf("lnkConst_boolean(const@%p, %d)\n", pjlink, val); @@ -276,8 +289,6 @@ static jlif_result lnkConst_start_array(jlink *pjlink) static jlif_result lnkConst_end_array(jlink *pjlink) { - const_link *clink = CONTAINER(pjlink, const_link, jlink); - IFDEBUG(10) printf("lnkConst_end_array(const@%p)\n", pjlink); @@ -286,8 +297,6 @@ static jlif_result lnkConst_end_array(jlink *pjlink) static struct lset* lnkConst_get_lset(const jlink *pjlink) { - const_link *clink = CONTAINER(pjlink, const_link, jlink); - IFDEBUG(10) printf("lnkConst_get_lset(const@%p)\n", pjlink); @@ -629,4 +638,3 @@ static jlif lnkConstIf = { lnkConst_report, NULL }; epicsExportAddress(jlif, lnkConstIf); - diff --git a/src/std/link/lnkState.c b/src/std/link/lnkState.c index e24773727..160dc78ca 100644 --- a/src/std/link/lnkState.c +++ b/src/std/link/lnkState.c @@ -35,7 +35,10 @@ typedef long (*FASTCONVERT)(); -#define IFDEBUG(n) if(slink->jlink.debug) +int lnkState_debug; +epicsExportAddress(int, lnkState_debug); + +#define IFDEBUG(n) if (lnkState_debug >= (n)) typedef struct state_link { jlink jlink; /* embedded object */ @@ -52,10 +55,21 @@ static lset lnkState_lset; static jlink* lnkState_alloc(short dbfType) { - state_link *slink = calloc(1, sizeof(struct state_link)); + state_link *slink; IFDEBUG(10) - printf("lnkState_alloc()\n"); + printf("lnkState_alloc(%d)\n", dbfType); + + if (dbfType == DBF_FWDLINK) { + errlogPrintf("lnkState: DBF_FWDLINK not supported\n"); + return NULL; + } + + slink = calloc(1, sizeof(struct state_link)); + if (!slink) { + errlogPrintf("lnkState: calloc() failed.\n"); + return NULL; + } slink->name = NULL; slink->state = NULL; @@ -97,8 +111,6 @@ static jlif_result lnkState_string(jlink *pjlink, const char *val, size_t len) static struct lset* lnkState_get_lset(const jlink *pjlink) { - state_link *slink = CONTAINER(pjlink, struct state_link, jlink); - IFDEBUG(10) printf("lnkState_get_lset(state@%p)\n", pjlink); From 1fa5d9d3b633f5b6fcabe8c1b41aeb1430847837 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 20 Jan 2018 20:01:31 -0600 Subject: [PATCH 05/29] Cleanup warnings in lnkConst.c --- src/std/link/lnkConst.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/std/link/lnkConst.c b/src/std/link/lnkConst.c index 881bf6bff..ee93830ff 100644 --- a/src/std/link/lnkConst.c +++ b/src/std/link/lnkConst.c @@ -583,8 +583,6 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer, static long lnkConst_getNelements(const struct link *plink, long *nelements) { - const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink); - IFDEBUG(10) printf("lnkConst_getNelements(const@%p, (%ld))\n", plink->value.json.jlink, *nelements); @@ -596,8 +594,6 @@ static long lnkConst_getNelements(const struct link *plink, long *nelements) static long lnkConst_getValue(struct link *plink, short dbrType, void *pbuffer, long *pnRequest) { - const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink); - IFDEBUG(10) printf("lnkConst_getValue(const@%p, %d, %p, ... (%ld))\n", plink->value.json.jlink, dbrType, pbuffer, From 20404003bf89517863753a8a9bd10ac95e219fca Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 20 Jan 2018 22:48:20 -0600 Subject: [PATCH 06/29] More timestamp support for lnkCalc Link now remembers the last timestamp read if a timestamp input was nominated, and implements getTimestamp() to return this. Also displays the timestamp value in the report output. --- src/std/link/lnkCalc.c | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/std/link/lnkCalc.c b/src/std/link/lnkCalc.c index 87f3b4a3e..cc08e84f8 100644 --- a/src/std/link/lnkCalc.c +++ b/src/std/link/lnkCalc.c @@ -26,6 +26,7 @@ #include "epicsAssert.h" #include "epicsString.h" #include "epicsTypes.h" +#include "epicsTime.h" #include "dbAccessDefs.h" #include "dbCommon.h" #include "dbConvertFast.h" @@ -70,6 +71,7 @@ typedef struct calc_link { short tinp; struct link inp[CALCPERFORM_NARGS]; double arg[CALCPERFORM_NARGS]; + epicsTimeStamp time; double val; } calc_link; @@ -400,9 +402,13 @@ static void lnkCalc_report(const jlink *pjlink, int level, int indent) printf("%*s Minor expression: \"%s\"\n", indent, "", clink->minor); - if (clink->tinp >= 0 && clink->tinp < clink->nArgs) - printf("%*s Timestamp input \"%c\"\n", indent, "", - clink->tinp + 'A'); + if (clink->tinp >= 0) { + char timeStr[40]; + epicsTimeToStrftime(timeStr, 40, "%Y-%m-%d %H:%M:%S.%09f", + &clink->time); + printf("%*s Timestamp input %c: %s\n", indent, "", + clink->tinp + 'A', timeStr); + } for (i = 0; i < clink->nArgs; i++) { struct link *plink = &clink->inp[i]; @@ -564,14 +570,17 @@ static long lnkCalc_getValue(struct link *plink, short dbrType, void *pbuffer, struct link *child = &clink->inp[i]; long nReq = 1; - if (i == clink->tinp && - dbLinkIsConstant(&prec->tsel) && - prec->tse == epicsTimeEventDeviceTime) { - struct lcvt vt = {&clink->arg[i], &prec->time}; + if (i == clink->tinp) { + struct lcvt vt = {&clink->arg[i], &clink->time}; status = dbLinkDoLocked(child, readLocked, &vt); if (status == S_db_noLSET) status = readLocked(child, &vt); + + if (dbLinkIsConstant(&prec->tsel) && + prec->tse == epicsTimeEventDeviceTime) { + prec->time = clink->time; + } } else dbGetLink(child, DBR_DOUBLE, &clink->arg[i], NULL, &nReq); @@ -663,6 +672,22 @@ static long lnkCalc_getAlarm(const struct link *plink, epicsEnum16 *status, return 0; } +static long lnkCalc_getTimestamp(const struct link *plink, epicsTimeStamp *pstamp) +{ + calc_link *clink = CONTAINER(plink->value.json.jlink, + struct calc_link, jlink); + + IFDEBUG(10) + printf("lnkCalc_getTimestamp(calc@%p)\n", clink); + + if (clink->tinp >= 0) { + *pstamp = clink->time; + return 0; + } + + return -1; +} + static long doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv) { return rtn(plink, priv); @@ -679,7 +704,7 @@ static lset lnkCalc_lset = { lnkCalc_getValue, NULL, NULL, NULL, lnkCalc_getPrecision, lnkCalc_getUnits, - lnkCalc_getAlarm, NULL, + lnkCalc_getAlarm, lnkCalc_getTimestamp, NULL, NULL, NULL, doLocked }; From d87ac0319b6409a169e7a5fcebb8534792094e40 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 30 Sep 2017 15:51:51 -0500 Subject: [PATCH 07/29] Replace the base:jlinkDebug info-item with a global variable The info item only works in dbPutString() which means dbLoadRecords() but not dbPutField(). --- src/ioc/db/dbJLink.c | 20 ++++++++++++++++---- src/ioc/dbStatic/dbStaticLib.c | 2 -- src/ioc/dbStatic/dbStaticPvt.h | 3 +-- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/ioc/db/dbJLink.c b/src/ioc/db/dbJLink.c index 0bc8d6ba5..feedf35a1 100644 --- a/src/ioc/db/dbJLink.c +++ b/src/ioc/db/dbJLink.c @@ -26,7 +26,9 @@ #include "dbStaticLib.h" #include "link.h" -#define IFDEBUG(n) if(parser->parse_debug) +int dbJLinkDebug = 0; + +#define IFDEBUG(n) if (dbJLinkDebug >= (n)) typedef struct parseContext { jlink *pjlink; @@ -34,7 +36,6 @@ typedef struct parseContext { short dbfType; short jsonDepth; unsigned key_is_link:1; - unsigned parse_debug:1; unsigned lset_debug:1; } parseContext; @@ -59,6 +60,9 @@ static int dbjl_return(parseContext *parser, jlif_result result) { pjlink->pif->free_jlink(pjlink); } + IFDEBUG(10) + printf(" returning %d %s\n", result, + result == jlif_stop ? "*** STOP ***" : "Continue"); return result; } @@ -352,7 +356,6 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType, parser->dbfType = dbfType; parser->jsonDepth = 0; parser->key_is_link = 0; - parser->parse_debug = !!(opts&LINK_DEBUG_JPARSE); parser->lset_debug = !!(opts&LINK_DEBUG_LSET); IFDEBUG(10) @@ -369,8 +372,14 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType, return S_db_noMemory; ys = yajl_parse(yh, (const unsigned char *) json, jlen); - if (ys == yajl_status_ok) + IFDEBUG(10) + printf("dbJLinkInit: yajl_parse() returned %d\n", ys); + + if (ys == yajl_status_ok) { ys = yajl_complete_parse(yh); + IFDEBUG(10) + printf("dbJLinkInit: yajl_complete_parse() returned %d\n", ys); + } switch (ys) { unsigned char *err; @@ -382,6 +391,9 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType, break; case yajl_status_error: + IFDEBUG(10) + printf(" jsonDepth=%d, product=%p, pjlink=%p\n", + parser->jsonDepth, parser->product, parser->pjlink); err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen); errlogPrintf("dbJLinkInit: %s\n", err); yajl_free_error(yh, err); diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 6e9997ad1..7549a0da6 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -2613,8 +2613,6 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring) if(dbFindInfo(&infoentry, "base:lsetDebug")==0 && epicsStrCaseCmp(dbGetInfoString(&infoentry), "YES")==0) opts |= LINK_DEBUG_LSET; - if(dbFindInfo(&infoentry, "base:jlinkDebug")==0 && epicsStrCaseCmp(dbGetInfoString(&infoentry), "YES")==0) - opts |= LINK_DEBUG_JPARSE; dbFinishEntry(&infoentry); } diff --git a/src/ioc/dbStatic/dbStaticPvt.h b/src/ioc/dbStatic/dbStaticPvt.h index 58d32c28c..b1ad70de6 100644 --- a/src/ioc/dbStatic/dbStaticPvt.h +++ b/src/ioc/dbStatic/dbStaticPvt.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. \*************************************************************************/ /* dbStaticPvt.h */ /* @@ -60,7 +60,6 @@ typedef struct dbLinkInfo { long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec); #define LINK_DEBUG_LSET 1 -#define LINK_DEBUG_JPARSE 2 /* Parse link string. no record locks needed. * on success caller must free pinfo->target From 54c47f02de8813b578ded1c48c080d7eef347367 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 30 Sep 2017 15:52:21 -0500 Subject: [PATCH 08/29] Export dbJLinkDebug as a shell variable --- src/ioc/db/dbJLink.c | 4 +++- src/ioc/misc/dbCore.dbd | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/ioc/db/dbJLink.c b/src/ioc/db/dbJLink.c index feedf35a1..13470fe9a 100644 --- a/src/ioc/db/dbJLink.c +++ b/src/ioc/db/dbJLink.c @@ -25,8 +25,10 @@ #include "dbLock.h" #include "dbStaticLib.h" #include "link.h" +#include "epicsExport.h" -int dbJLinkDebug = 0; +epicsShareDef int dbJLinkDebug = 0; +epicsExportAddress(int, dbJLinkDebug); #define IFDEBUG(n) if (dbJLinkDebug >= (n)) diff --git a/src/ioc/misc/dbCore.dbd b/src/ioc/misc/dbCore.dbd index 921b3818e..6bd0b6bab 100644 --- a/src/ioc/misc/dbCore.dbd +++ b/src/ioc/misc/dbCore.dbd @@ -12,6 +12,9 @@ variable(asCaDebug,int) # CA server debug flag (very verbose) range[0,5] variable(CASDEBUG,int) +# Link parsing debug +variable(dbJLinkDebug,int) + # Static database access variables variable(dbRecordsOnceOnly,int) variable(dbRecordsAbcSorted,int) From 8f64af96fd75dab7ae130184547c4572448c94ef Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 5 Mar 2018 20:53:45 -0600 Subject: [PATCH 09/29] Remove final traces of link debug info tags --- src/ioc/db/dbAccess.c | 3 +-- src/ioc/db/dbJLink.c | 7 ++----- src/ioc/db/dbJLink.h | 8 +++----- src/ioc/db/test/dbPutLinkTest.c | 4 ++-- src/ioc/dbStatic/dbStaticLib.c | 19 ++++--------------- src/ioc/dbStatic/dbStaticPvt.h | 4 +--- 6 files changed, 13 insertions(+), 32 deletions(-) diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index 973a66836..6e43959e3 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -1036,7 +1036,7 @@ static long dbPutFieldLink(DBADDR *paddr, return S_db_badDbrtype; } - status = dbParseLink(pstring, pfldDes->field_type, &link_info, 0); + status = dbParseLink(pstring, pfldDes->field_type, &link_info); if (status) return status; @@ -1343,4 +1343,3 @@ done: paddr->pfield = pfieldsave; return status; } - diff --git a/src/ioc/db/dbJLink.c b/src/ioc/db/dbJLink.c index 13470fe9a..10a4e62d2 100644 --- a/src/ioc/db/dbJLink.c +++ b/src/ioc/db/dbJLink.c @@ -38,7 +38,6 @@ typedef struct parseContext { short dbfType; short jsonDepth; unsigned key_is_link:1; - unsigned lset_debug:1; } parseContext; #define CALL_OR_STOP(routine) !(routine) ? jlif_stop : (routine) @@ -87,7 +86,6 @@ static int dbjl_value(parseContext *parser, jlif_result result) { } else if (parent->pif->end_child) { parent->pif->end_child(parent, pjlink); } - pjlink->debug = 0; parser->pjlink = parent; @@ -257,7 +255,7 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) { pjlink->pif = pjlif; pjlink->parseDepth = 0; - pjlink->debug = !!parser->lset_debug; + if (parser->pjlink) { /* We're starting a child link, save its parent */ pjlink->parent = parser->pjlink; @@ -345,7 +343,7 @@ static yajl_callbacks dbjl_callbacks = { }; long dbJLinkParse(const char *json, size_t jlen, short dbfType, - jlink **ppjlink, unsigned opts) + jlink **ppjlink) { parseContext context, *parser = &context; yajl_alloc_funcs dbjl_allocs; @@ -358,7 +356,6 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType, parser->dbfType = dbfType; parser->jsonDepth = 0; parser->key_is_link = 0; - parser->lset_debug = !!(opts&LINK_DEBUG_LSET); IFDEBUG(10) printf("dbJLinkInit(\"%.*s\", %d, %p)\n", diff --git a/src/ioc/db/dbJLink.h b/src/ioc/db/dbJLink.h index 61b59670b..954b69be5 100644 --- a/src/ioc/db/dbJLink.h +++ b/src/ioc/db/dbJLink.h @@ -2,7 +2,7 @@ * Copyright (c) 2016 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. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbJLink.h */ @@ -35,7 +35,6 @@ typedef struct jlink { struct jlif *pif; /* Link methods */ struct jlink *parent; /* NULL for top-level links */ int parseDepth; /* Used by parser, unused afterwards */ - unsigned debug:1; /* set by caller of jlif operations to request debug output to console */ /* Link types extend or embed this structure for private storage */ } jlink; @@ -98,7 +97,7 @@ typedef struct jlif { 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) + * dbJLinkReport(child, level-1, indent+4) * for each child. */ @@ -113,7 +112,7 @@ typedef struct jlif { } jlif; epicsShareFunc long dbJLinkParse(const char *json, size_t len, short dbfType, - jlink **ppjlink, unsigned opts); + jlink **ppjlink); epicsShareFunc long dbJLinkInit(struct link *plink); epicsShareFunc void dbJLinkFree(jlink *); @@ -130,4 +129,3 @@ epicsShareFunc long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx); #endif #endif /* INC_dbJLink_H */ - diff --git a/src/ioc/db/test/dbPutLinkTest.c b/src/ioc/db/test/dbPutLinkTest.c index d6748a9eb..00634fd66 100644 --- a/src/ioc/db/test/dbPutLinkTest.c +++ b/src/ioc/db/test/dbPutLinkTest.c @@ -87,7 +87,7 @@ static void testLinkParse(void) for (;td->str; td++) { int i, N; testDiag("Parsing \"%s\"", td->str); - testOk(dbParseLink(td->str, DBF_INLINK, &info, 0) == 0, "Parser returned OK"); + testOk(dbParseLink(td->str, DBF_INLINK, &info) == 0, "Parser returned OK"); if (!testOk(info.ltype == td->info.ltype, "Link type value")) testDiag("Expected %d, got %d", td->info.ltype, info.ltype); if (td->info.target) @@ -147,7 +147,7 @@ static void testLinkFailParse(void) eltc(1); for(;*td; td++) { - testOk(dbParseLink(*td, DBF_INLINK, &info, 0) == S_dbLib_badField, + testOk(dbParseLink(*td, DBF_INLINK, &info) == S_dbLib_badField, "dbParseLink correctly rejected \"%s\"", *td); } diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 7549a0da6..4f14c5596 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -2205,7 +2205,7 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec) if(!plink->text) continue; - if(dbParseLink(plink->text, pflddes->field_type, &link_info, 0)!=0) { + if(dbParseLink(plink->text, pflddes->field_type, &link_info)!=0) { /* This was already parsed once when ->text was set. * Any syntax error messages were printed at that time. */ @@ -2234,7 +2234,7 @@ void dbFreeLinkInfo(dbLinkInfo *pinfo) pinfo->target = NULL; } -long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts) +long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo) { char *pstr; size_t len; @@ -2270,7 +2270,7 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts) /* Check for braces => JSON */ if (*str == '{' && str[len-1] == '}') { - if (dbJLinkParse(str, len, ftype, &pinfo->jlink, opts)) + if (dbJLinkParse(str, len, ftype, &pinfo->jlink)) goto fail; pinfo->ltype = JSON_LINK; @@ -2605,19 +2605,8 @@ long dbPutString(DBENTRY *pdbentry,const char *pstring) case DBF_FWDLINK: { dbLinkInfo link_info; DBLINK *plink = (DBLINK *)pfield; - DBENTRY infoentry; - unsigned opts = 0; - if(pdbentry->precnode && ellCount(&pdbentry->precnode->infoList)) { - dbCopyEntryContents(pdbentry, &infoentry); - - if(dbFindInfo(&infoentry, "base:lsetDebug")==0 && epicsStrCaseCmp(dbGetInfoString(&infoentry), "YES")==0) - opts |= LINK_DEBUG_LSET; - - dbFinishEntry(&infoentry); - } - - status = dbParseLink(pstring, pflddes->field_type, &link_info, opts); + status = dbParseLink(pstring, pflddes->field_type, &link_info); if (status) break; if (plink->type==CONSTANT && plink->value.constantStr==NULL) { diff --git a/src/ioc/dbStatic/dbStaticPvt.h b/src/ioc/dbStatic/dbStaticPvt.h index b1ad70de6..85fc02217 100644 --- a/src/ioc/dbStatic/dbStaticPvt.h +++ b/src/ioc/dbStatic/dbStaticPvt.h @@ -59,12 +59,10 @@ typedef struct dbLinkInfo { long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec); -#define LINK_DEBUG_LSET 1 - /* Parse link string. no record locks needed. * on success caller must free pinfo->target */ -epicsShareFunc long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo, unsigned opts); +epicsShareFunc long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo); /* Check if link type allow the parsed link value pinfo * to be assigned to the given link. * Record containing plink must be locked. From 97ea68d40c65d4d1e5ec2e36050475204ca69c27 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 9 Mar 2018 22:42:59 -0600 Subject: [PATCH 10/29] Added DBF_OUTLINK support to lnkCalc New out key specifying a child link to put the calculated result to. --- src/std/link/links.dbd.pod | 35 +++++++- src/std/link/lnkCalc.c | 168 ++++++++++++++++++++++++++++++++----- 2 files changed, 180 insertions(+), 23 deletions(-) diff --git a/src/std/link/links.dbd.pod b/src/std/link/links.dbd.pod index 6eb8b77d3..6b48c13fb 100644 --- a/src/std/link/links.dbd.pod +++ b/src/std/link/links.dbd.pod @@ -80,10 +80,30 @@ variable(lnkCalc_debug, int) =head3 Calculation Link C<"calc"> -Calculation links are input links that can evaluate mathematical expressions on -scalar (double- precision floating-point) values obtained from child links, and -return a double-precision floating-point result. The expressions are evaluated -by the EPICS Calc engine, and up to 12 inputs can be provided. +A calculation link is an input link that can evaluate mathematical expressions +on scalar (double-precision floating-point) values obtained from up to 12 child +input links, and returns a double-precision floating-point result. The +expression is evaluated by the EPICS Calc engine, and the result is returned as +the value of the link. + +Two additional expressions may also be provided and are evaluated to determine +whether the record owning the link should be placed in alarm state. In both +cases the result of the main calculation is available to these expressions as +C (attempts to assign to C inside either expression will have no +lasting effect). If the C expression evaluates to a non-zero value the +record will be placed in C alarm. If not and the C expression +evaluates to non-zero the record will be placed in C alarm state. + +A calculation link can also be an output link, with the scalar output value +being converted to a double and provided to the expression as C. Up to 12 +additional input links can also be read and provided to the expression as above. +The result of the calculation is forwarded to a child output link specified in +the link's C parameter. + +For an output link the main expression is actually optional; if not provided the +converted value will be forwarded to the output link unchanged. The two alarm +expressions may still be used to put the output link into alarm state as +described above. =head4 Parameters @@ -94,6 +114,7 @@ The link address is a JSON map with the following keys: =item expr The primary expression to be evaluated, given as a string. +This is optional for output links, required for input links. =item major @@ -110,6 +131,12 @@ to the inputs C, C, C, ... C. Each input argument may be either a numeric literal or an embedded JSON link inside C<{}> braces. The same input values are provided to the two alarm expressions as to the primary expression. +=item out + +A JSON link inside C<{}> braces which specifies the destination of C +operations after any expressions have been evaluated. +This key is required for output links, not used by input links. + =item units An optional string specifying the engineering units for the result of the diff --git a/src/std/link/lnkCalc.c b/src/std/link/lnkCalc.c index cc08e84f8..de76948a9 100644 --- a/src/std/link/lnkCalc.c +++ b/src/std/link/lnkCalc.c @@ -6,13 +6,9 @@ \*************************************************************************/ /* lnkCalc.c */ -/* Current usage - * {calc:{expr:"A", args:[{...}, ...]}} +/* Usage + * {calc:{expr:"A*B", args:[{...}, ...], units:"mm"}} * First link in 'args' is 'A', second is 'B', and so forth. - * - * TODO: - * Support setting individual input links instead of the args list. - * {calc:{expr:"K", K:{...}}} */ #include @@ -49,10 +45,11 @@ epicsExportAddress(int, lnkCalc_debug); typedef struct calc_link { jlink jlink; /* embedded object */ int nArgs; + short dbfType; enum { ps_init, ps_expr, ps_major, ps_minor, - ps_args, + ps_args, ps_out, ps_prec, ps_units, ps_time, @@ -70,6 +67,7 @@ typedef struct calc_link { char *units; short tinp; struct link inp[CALCPERFORM_NARGS]; + struct link out; double arg[CALCPERFORM_NARGS]; epicsTimeStamp time; double val; @@ -87,8 +85,8 @@ static jlink* lnkCalc_alloc(short dbfType) IFDEBUG(10) printf("lnkCalc_alloc(%d)\n", dbfType); - if (dbfType != DBF_INLINK) { - errlogPrintf("lnkCalc: Only works with input links\n"); + if (dbfType == DBF_FWDLINK) { + errlogPrintf("lnkCalc: No support for forward links\n"); return NULL; } @@ -99,6 +97,7 @@ static jlink* lnkCalc_alloc(short dbfType) } clink->nArgs = 0; + clink->dbfType = dbfType; clink->pstate = ps_init; clink->prec = 15; /* standard value for a double */ clink->tinp = -1; @@ -120,6 +119,8 @@ static void lnkCalc_free(jlink *pjlink) for (i = 0; i < clink->nArgs; i++) dbJLinkFree(clink->inp[i].value.json.jlink); + dbJLinkFree(clink->out.value.json.jlink); + free(clink->expr); free(clink->major); free(clink->minor); @@ -253,7 +254,7 @@ static jlif_key_result lnkCalc_start_map(jlink *pjlink) IFDEBUG(10) printf("lnkCalc_start_map(calc@%p)\n", clink); - if (clink->pstate == ps_args) + if (clink->pstate == ps_args || clink->pstate == ps_out) return jlif_key_child_link; if (clink->pstate != ps_init) { @@ -271,7 +272,21 @@ static jlif_result lnkCalc_map_key(jlink *pjlink, const char *key, size_t len) IFDEBUG(10) printf("lnkCalc_map_key(calc@%p, \"%.*s\")\n", pjlink, (int) len, key); - if (len == 4) { + /* FIXME: These errors messages are wrong when a key is duplicated. + * The key is known, we just don't allow it more than once. + */ + + if (len == 3) { + if (!strncmp(key, "out", len) && + clink->dbfType == DBF_OUTLINK && + clink->out.type == 0) + clink->pstate = ps_out; + else { + errlogPrintf("lnkCalc: Unknown key \"%.3s\"\n", key); + return jlif_stop; + } + } + else if (len == 4) { if (!strncmp(key, "expr", len) && !clink->post_expr) clink->pstate = ps_expr; else if (!strncmp(key, "args", len) && !clink->nArgs) @@ -314,8 +329,14 @@ static jlif_result lnkCalc_end_map(jlink *pjlink) if (clink->pstate == ps_error) return jlif_stop; - else if (!clink->post_expr) { - errlogPrintf("lnkCalc: no expression ('expr' key)\n"); + else if (clink->dbfType == DBF_INLINK && + !clink->post_expr) { + errlogPrintf("lnkCalc: No expression ('expr' key)\n"); + return jlif_stop; + } + else if (clink->dbfType == DBF_OUTLINK && + clink->out.type != JSON_LINK) { + errlogPrintf("lnkCalc: No output link ('out' key)\n"); return jlif_stop; } @@ -355,15 +376,27 @@ static void lnkCalc_end_child(jlink *parent, jlink *child) calc_link *clink = CONTAINER(parent, struct calc_link, jlink); struct link *plink; - if (clink->nArgs == CALCPERFORM_NARGS) { - dbJLinkFree(child); - errlogPrintf("lnkCalc: Too many input args, limit is %d\n", - CALCPERFORM_NARGS); + if (clink->pstate == ps_args) { + if (clink->nArgs == CALCPERFORM_NARGS) { + errlogPrintf("lnkCalc: Too many input args, limit is %d\n", + CALCPERFORM_NARGS); + goto errOut; + } + + plink = &clink->inp[clink->nArgs++]; + } + else if (clink->pstate == ps_out) { + plink = &clink->out; + } + else { + errlogPrintf("lnkCalc: Unexpected child link, parser state = %d\n", + clink->pstate); +errOut: clink->pstate = ps_error; + dbJLinkFree(child); return; } - plink = &clink->inp[clink->nArgs++]; plink->type = JSON_LINK; plink->value.json.string = NULL; plink->value.json.jlink = child; @@ -421,6 +454,12 @@ static void lnkCalc_report(const jlink *pjlink, int level, int indent) if (child) dbJLinkReport(child, level - 1, indent + 4); } + + if (clink->out.type == JSON_LINK) { + printf("%*s Output:\n", indent, ""); + + dbJLinkReport(clink->out.value.json.jlink, level - 1, indent + 4); + } } } @@ -439,6 +478,10 @@ long lnkCalc_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx) if (status) return status; } + + if (clink->out.type == JSON_LINK) { + return dbJLinkMapChildren(&clink->out, rtn, ctx); + } return 0; } @@ -460,6 +503,10 @@ static void lnkCalc_open(struct link *plink) dbJLinkInit(child); dbLoadLink(child, DBR_DOUBLE, &clink->arg[i]); } + + if (clink->out.type == JSON_LINK) { + dbJLinkInit(&clink->out); + } } static void lnkCalc_remove(struct dbLocker *locker, struct link *plink) @@ -477,6 +524,10 @@ static void lnkCalc_remove(struct dbLocker *locker, struct link *plink) dbRemoveLink(locker, child); } + if (clink->out.type == JSON_LINK) { + dbRemoveLink(locker, &clink->out); + } + free(clink->expr); free(clink->major); free(clink->minor); @@ -506,6 +557,14 @@ static int lnkCalc_isConn(const struct link *plink) connected = 0; } + if (clink->out.type == JSON_LINK) { + struct link *child = &clink->out; + + if (dbLinkIsVolatile(child) && + !dbIsLinkConnected(child)) + connected = 0; + } + return connected; } @@ -626,6 +685,77 @@ static long lnkCalc_getValue(struct link *plink, short dbrType, void *pbuffer, return status; } +static long lnkCalc_putValue(struct link *plink, short dbrType, + const void *pbuffer, long nRequest) +{ + calc_link *clink = CONTAINER(plink->value.json.jlink, + struct calc_link, jlink); + dbCommon *prec = plink->precord; + int i; + long status; + FASTCONVERT conv = dbFastGetConvertRoutine[dbrType][DBR_DOUBLE]; + + IFDEBUG(10) + printf("lnkCalc_putValue(calc@%p, %d, ...)\n", clink, dbrType); + + /* Any link errors will trigger a LINK/INVALID alarm in the child link */ + for (i = 0; i < clink->nArgs; i++) { + struct link *child = &clink->inp[i]; + long nReq = 1; + + if (i == clink->tinp) { + struct lcvt vt = {&clink->arg[i], &clink->time}; + + status = dbLinkDoLocked(child, readLocked, &vt); + if (status == S_db_noLSET) + status = readLocked(child, &vt); + + if (dbLinkIsConstant(&prec->tsel) && + prec->tse == epicsTimeEventDeviceTime) { + prec->time = clink->time; + } + } + else + dbGetLink(child, DBR_DOUBLE, &clink->arg[i], NULL, &nReq); + } + clink->stat = 0; + clink->sevr = 0; + + /* Get the value being output as VAL */ + status = conv(pbuffer, &clink->val, NULL); + + if (!status && clink->post_expr) + status = calcPerform(clink->arg, &clink->val, clink->post_expr); + + if (!status && clink->post_major) { + double alval = clink->val; + + status = calcPerform(clink->arg, &alval, clink->post_major); + if (!status && alval) { + clink->stat = LINK_ALARM; + clink->sevr = MAJOR_ALARM; + recGblSetSevr(prec, clink->stat, clink->sevr); + } + } + + if (!status && clink->post_minor) { + double alval = clink->val; + + status = calcPerform(clink->arg, &alval, clink->post_minor); + if (!status && alval) { + clink->stat = LINK_ALARM; + clink->sevr = MINOR_ALARM; + recGblSetSevr(prec, clink->stat, clink->sevr); + } + } + + if (!status) { + status = dbPutLink(&clink->out, DBR_DOUBLE, &clink->val, 1); + } + + return status; +} + static long lnkCalc_getPrecision(const struct link *plink, short *precision) { calc_link *clink = CONTAINER(plink->value.json.jlink, @@ -705,7 +835,7 @@ static lset lnkCalc_lset = { NULL, NULL, NULL, lnkCalc_getPrecision, lnkCalc_getUnits, lnkCalc_getAlarm, lnkCalc_getTimestamp, - NULL, NULL, + lnkCalc_putValue, NULL, NULL, doLocked }; From ca2003bb6338e9ee153fdd7cbc6e52328946f476 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 31 Mar 2018 15:42:35 +0100 Subject: [PATCH 11/29] dbLink: Clarify meaning of lset isConstant and isVolatile flags Modify dbIsLinkConnected() to check lset->isVolatile first. --- src/ioc/db/dbLink.c | 13 ++++++++++++- src/ioc/db/dbLink.h | 4 ++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index c90dcb3df..ed46a9b51 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -265,8 +265,19 @@ int dbIsLinkConnected(const struct link *plink) { lset *plset = plink->lset; - if (!plset || !plset->isConnected) + if (!plset) return FALSE; + if (!plset->isVolatile) + return TRUE; + + if (!plset->isConnected) { + struct dbCommon *precord = plink->precord; + + errlogPrintf("dbLink: Link type for '%s.%s' is volatile but has no" + " lset::isConnected() method\n", + precord->name, link_field_name(plink)); + return FALSE; + } return plset->isConnected(plink); } diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h index ad4ac2f45..d6f39ca36 100644 --- a/src/ioc/db/dbLink.h +++ b/src/ioc/db/dbLink.h @@ -31,8 +31,8 @@ typedef long (*dbLinkUserCallback)(struct link *plink, void *priv); typedef struct lset { /* Characteristics of the link type */ - const unsigned isConstant:1; - const unsigned isVolatile:1; + const unsigned isConstant:1; /* 1 means value doesn't change */ + const unsigned isVolatile:1; /* 0 means link always connected */ /* Activation */ void (*openLink)(struct link *plink); From c2c32e58766f6cc6878f12d714fcb9ffa2710809 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 31 Mar 2018 15:48:58 +0100 Subject: [PATCH 12/29] lnkState: Mark as non-volatile, remove lset::isConnected() method --- src/std/link/lnkState.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/std/link/lnkState.c b/src/std/link/lnkState.c index 160dc78ca..8792a1f64 100644 --- a/src/std/link/lnkState.c +++ b/src/std/link/lnkState.c @@ -155,17 +155,6 @@ static void lnkState_remove(struct dbLocker *locker, struct link *plink) plink->value.json.jlink = NULL; } -static int lnkState_isConn(const struct link *plink) -{ - state_link *slink = CONTAINER(plink->value.json.jlink, - struct state_link, jlink); - - IFDEBUG(10) - printf("lnkState_isConn(state@%p)\n", slink); - - return !! slink->state; -} - static int lnkState_getDBFtype(const struct link *plink) { state_link *slink = CONTAINER(plink->value.json.jlink, @@ -273,10 +262,10 @@ static long lnkState_putValue(struct link *plink, short dbrType, /************************* Interface Tables *************************/ static lset lnkState_lset = { - 0, 1, /* not Constant, Volatile */ + 0, 0, /* not constant, always connected */ lnkState_open, lnkState_remove, NULL, NULL, NULL, - lnkState_isConn, lnkState_getDBFtype, lnkState_getElements, + NULL, lnkState_getDBFtype, lnkState_getElements, lnkState_getValue, NULL, NULL, NULL, NULL, NULL, From 7cef334b64899bb1b3040d6875fb723ca9da9578 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 25 Apr 2018 21:42:44 -0500 Subject: [PATCH 13/29] Added C++ extern "C" wrapper to dbState.h --- src/ioc/db/dbState.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/ioc/db/dbState.h b/src/ioc/db/dbState.h index abd23259e..c7cd81c52 100644 --- a/src/ioc/db/dbState.h +++ b/src/ioc/db/dbState.h @@ -15,6 +15,10 @@ #include "shareLib.h" +#ifdef __cplusplus +extern "C" { +#endif + /** @file dbState.h * @brief Generic IOC state facility * @@ -89,4 +93,9 @@ epicsShareFunc void dbStateShow(dbStateId id, unsigned int level); */ epicsShareFunc void dbStateShowAll(unsigned int level); + +#ifdef __cplusplus +} +#endif + #endif // INCdbStateH From 877d38e79a5653cd530b0dfedb469387cdce5141 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Wed, 2 May 2018 00:06:12 -0500 Subject: [PATCH 14/29] Added Doxygen annotations for the Link Support API Haven't actually tried processing them yet though. --- src/ioc/db/dbLink.h | 298 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 291 insertions(+), 7 deletions(-) diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h index d6f39ca36..f9ea5e2ff 100644 --- a/src/ioc/db/dbLink.h +++ b/src/ioc/db/dbLink.h @@ -27,54 +27,337 @@ extern "C" { struct dbLocker; +/** @file dbLink.h + * @brief Link Support API + * + * Link support run-time API, all link types provide an lset which is used by + * the IOC database to control and operate the link. This file also declares the + * dbLink routines that IOC, record and device code can call to perform link + * operations. + */ + +/** @brief callback routine for locked link operations + * + * Called by the lset::doLocked method to permit multiple link operations + * while the link instance is locked. + * + * @param plink the link + * @param priv context for the callback routine + */ typedef long (*dbLinkUserCallback)(struct link *plink, void *priv); +/** @brief Link Support Entry Table + * + * This structure provides information about and methods for an individual link + * type. A pointer to this structure is included in every link's lset field, and + * is used to perform operations on the link. For JSON links the pointer is + * obtained by calling pjlink->pif->get_lset() at link initialization time, + * immediately before calling dbLinkOpen() to activate the link. + */ typedef struct lset { /* Characteristics of the link type */ - const unsigned isConstant:1; /* 1 means value doesn't change */ - const unsigned isVolatile:1; /* 0 means link always connected */ - /* Activation */ + /** @brief link constancy + * + * 1 means this is a constant link type whose value doesn't change. + * The link's value will be obtained using one of the methods loadScalar, + * loadLS or loadArray. + */ + const unsigned isConstant:1; + + /** @brief link volatility + * + * 0 means the link is always connected. + */ + const unsigned isVolatile:1; + + /** @brief activate link + * + * Optional, called whenever a JSON link is initialized or added at runtime. + * + * @param plink the link + */ void (*openLink)(struct link *plink); - /* Destructor */ + /** @brief deactivate link + * + * Optional, called whenever a link address is changed at runtime, or the + * IOC is shutting down. + * + * @param locker + * @param plink the link + */ void (*removeLink)(struct dbLocker *locker, struct link *plink); - /* Const init, data type hinting */ + /* Constant link initialization and data type hinting */ + + /** @brief load constant scalar from link type + * + * Usually called during IOC initialization, constant link types must copy a + * scalar value of the indicated data type to the buffer provided and return + * 0. A non-constant link type can use this method call as an early hint + * that subsequent calls to dbGetLink() will request scalar data of the + * indicated type, although the type might change. + * + * @param plink the link + * @param dbrType data type code + * @param pbuffer where to put the value + * @returns 0 if a value was loaded, non-zero otherwise + */ long (*loadScalar)(struct link *plink, short dbrType, void *pbuffer); + + /** @brief load constant long string from link type + * + * Usually called during IOC initialization, constant link types must copy a + * nil-terminated string up to size characters long to the buffer provided, + * and write the length of that string to the plen location. A non-constant + * link type can use this as an early hint that subsequent calls to + * dbGetLink() will request long string data, although this might change. + * + * @param plink the link + * @param pbuffer where to put the string + * @param size length of pbuffer in chars + * @param plen set to number of chars written + * @returns status value + */ long (*loadLS)(struct link *plink, char *pbuffer, epicsUInt32 size, epicsUInt32 *plen); + + /** @brief load constant array from link type + * + * Usually called during IOC initialization, constant link types must copy + * an array value of the indicated data type to the buffer provided, update + * the pnRequest location to indicate how many elements were loaded, and + * return 0. A non-constant link type can use this method call as an early + * hint that subsequent calls to dbGetLink() will request array data of the + * indicated type and max size, although the request might change. + * + * @param plink the link + * @param dbrType data type code + * @param pbuffer where to put the value + * @param pnRequest Max elements on entry, actual on exit + * @returns 0 if elements were loaded, non-zero otherwise + */ long (*loadArray)(struct link *plink, short dbrType, void *pbuffer, long *pnRequest); /* Metadata */ + + /** @brief return link connection status + * + * Return an indication whether this link is connected or not. This routine + * is polled by the calcout and some external record types. Not required for + * non-volatile link types, which are by definition always connected. + * + * @param plink the link + * @returns 1 if connected, 0 if disconnected + */ int (*isConnected)(const struct link *plink); + + /** @brief get data type of link destination + * + * Called on both input and output links by long string support code to + * decide whether to use DBR_CHAR/DBR_UCHAR or DBR_STRING for a subsequent + * dbPutLink() or dbGetLink() call. Optional, but if not provided long + * strings cannot be transported over this link type, and no warning or + * error will appear to explain why. Not required for constant link types. + * + * @param plink the link + * @returns DBF_* type code, or -1 on error/disconnected link + */ int (*getDBFtype)(const struct link *plink); - long (*getElements)(const struct link *plink, long *nelements); /* Get data */ + + /** @brief get array size of an input link + * + * Called on input links by the compress record type for memory allocation + * purposes, before using the dbGetLink() routine to fetch the actual + * array data. + * + * @param plink the link + * @param pnElements where to put the answer + * @returns status value + */ + long (*getElements)(const struct link *plink, long *pnElements); + + /** @brief get value from an input link + * + * Called to fetch data from the link, which must be converted into the + * given data type and placed in the buffer indicated. The actual number of + * elements retrieved should be updated in the pnRequest location. If this + * method returns an error status value, the link's record will be placed + * into an Invalid severity / Link Alarm state by the dbGetLink() routine + * that calls this method. + * + * @param plink the link + * @param dbrType data type code + * @param pbuffer where to put the value + * @param pnRequest max elements on entry, actual on exit + * @returns status value + */ long (*getValue)(struct link *plink, short dbrType, void *pbuffer, long *pnRequest); + + /** @brief get the control range for an output link + * + * Called to fetch the control range for the link target, as a pair of + * double values for the lowest and highest values that the target will + * accept. This method is not used at all by the IOC or built-in record + * types, although external record types may require it. + * + * @param plink the link + * @param lo lowest accepted value + * @param hi highest accepted value + * @returns status value + */ long (*getControlLimits)(const struct link *plink, double *lo, double *hi); + + /** @brief get the display range from an input link + * + * Called to fetch the display range for an input link target, as a pair of + * double values for the lowest and highest values that the PV expects to + * return. This method is used by several built-in record types to obtain + * the display range for their generic input links. + * + * @param plink the link + * @param lo lowest accepted value + * @param hi highest accepted value + * @returns status value + */ long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi); + + /** @brief get the alarm limits from an input link + * + * Called to fetch the alarm limits for an input link target, as four + * double values for the warning and alarm levels that the PV checks its + * value against. This method is used by several built-in record types to + * obtain the alarm limits for their generic input links. + * + * @param plink the link + * @param lolo low alarm value + * @param lo low warning value + * @param hi high warning value + * @param hihi high alarm value + * @returns status value + */ long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo, double *hi, double *hihi); + + /** @brief get the precision from an input link + * + * Called to fetch the precision for an input link target. This method is + * used by several built-in record types to obtain the precision for their + * generic input links. + * + * @param plink the link + * @param precision where to put the answer + * @returns status value + */ long (*getPrecision)(const struct link *plink, short *precision); + + /** @brief get the units string from an input link + * + * Called to fetch the units string for an input link target. This method is + * used by several built-in record types to obtain the units string for + * their generic input links. + * + * @param plink the link + * @param units where to put the answer + * @param unitsSize buffer size for the answer + * @returns status value + */ long (*getUnits)(const struct link *plink, char *units, int unitsSize); + + /** @brief get the alarm condition from an input link + * + * Called to fetch the alarm status and severity for an input link target. + * Either status or severity pointers may be NULL when that value is not + * needed by the calling code. This method is used by several built-in + * record types to obtain the alarm condition for their generic input links. + * + * @param plink the link + * @param status where to put the alarm status (or NULL) + * @param severity where to put the severity (or NULL) + * @returns status value + */ long (*getAlarm)(const struct link *plink, epicsEnum16 *status, epicsEnum16 *severity); + + /** @brief get the time-stamp from an input link + * + * Called to fetch the time-stamp for an input link target. This method is + * used by many built-in device supports to obtain the precision for their + * generic input links. + * + * @param plink the link + * @param pstamp where to put the answer + * @returns status value + */ long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp); /* Put data */ + + /** @brief put a value to an output link + * + * Called to send nRequest elements of type dbrType found at pbuffer to an + * output link target. + * + * @param plink the link + * @param dbrType data type code + * @param pbuffer where to put the value + * @param nRequest number of elements to send + * @returns status value + */ long (*putValue)(struct link *plink, short dbrType, const void *pbuffer, long nRequest); + + /** @brief put a value to an output link with asynchronous completion + * + * Called to send nRequest elements of type dbrType found at pbuffer to an + * output link target. If the return status is zero, the link type will + * later indicate the put has completed by calling dbLinkAsyncComplete() + * from a background thread, which will be used to continue the record + * process operation from where it left off. + * + * @param plink the link + * @param dbrType data type code + * @param pbuffer where to put the value + * @param nRequest number of elements to send + * @returns status value + */ long (*putAsync)(struct link *plink, short dbrType, const void *pbuffer, long nRequest); /* Process */ + + /** @brief trigger processing of a forward link + * + * Called to trigger processing of the record pointed to by a forward link. + * This routine is optional, but if not provided no warning message will be + * shown when called by dbScanFwdLink(). JSON link types that do not support + * this operation should return NULL from their jlif::alloc_jlink() method + * if it gets called with a dbfType of DBF_FWDLINK. + * + * @param plink the link + */ void (*scanForward)(struct link *plink); /* Atomicity */ + + /** @brief execute a callback routine with link locked + * + * Called on an input link when multiple link attributes need to be fetched + * in an atomic fashion. The link type must call the callback routine and + * prevent any background I/O from updating any cached link data until that + * routine returns. This method is used by most input device support to + * fetch the timestamp along with the value when the record's TSE field is + * set to epicsTimeEventDeviceTime. + * + * @param plink the link + * @param rtn routine to execute + * @returns status value + */ long (*doLocked)(struct link *plink, dbLinkUserCallback rtn, void *priv); } lset; @@ -97,9 +380,10 @@ epicsShareFunc long dbLoadLink(struct link *plink, short dbrType, epicsShareFunc long dbLoadLinkArray(struct link *, short dbrType, void *pbuffer, long *pnRequest); -epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements); +epicsShareFunc long dbGetNelements(const struct link *plink, long *pnElements); epicsShareFunc int dbIsLinkConnected(const struct link *plink); /* 0 or 1 */ epicsShareFunc int dbGetLinkDBFtype(const struct link *plink); + epicsShareFunc long dbGetLink(struct link *, short dbrType, void *pbuffer, long *options, long *nRequest); epicsShareFunc long dbGetControlLimits(const struct link *plink, double *low, From 3b89515664bb140f6f05089da98aabaa9d4dad1b Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 14 May 2018 23:43:13 -0500 Subject: [PATCH 15/29] dbJLink: Pass the correct dbfType to child links API change for link types that support child links, they must now indicate whether a child link is an input, output or forward link by the return value from their parse_start_map() method. The original jlif_key_child_link enumeration has been replaced by 3 new values: jlif_key_child_inlink, jlif_key_child_outlink and jlif_key_child_fwdlink Previously we were passing the dbfType of the record link field to all child links within it, which was wrong. --- src/ioc/db/dbJLink.c | 68 +++++++++++++++++++++++++----------------- src/ioc/db/dbJLink.h | 7 +++-- src/std/link/lnkCalc.c | 8 +++-- 3 files changed, 50 insertions(+), 33 deletions(-) diff --git a/src/ioc/db/dbJLink.c b/src/ioc/db/dbJLink.c index 10a4e62d2..a5fd2bed6 100644 --- a/src/ioc/db/dbJLink.c +++ b/src/ioc/db/dbJLink.c @@ -37,7 +37,6 @@ typedef struct parseContext { jlink *product; short dbfType; short jsonDepth; - unsigned key_is_link:1; } parseContext; #define CALL_OR_STOP(routine) !(routine) ? jlif_stop : (routine) @@ -47,8 +46,8 @@ static int dbjl_return(parseContext *parser, jlif_result result) { IFDEBUG(10) { printf("dbjl_return(%s@%p, %d)\t", pjlink ? pjlink->pif->name : "", pjlink, result); - printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n", - parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link); + printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", + parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } if (result == jlif_stop && pjlink) { @@ -73,8 +72,8 @@ static int dbjl_value(parseContext *parser, jlif_result result) { IFDEBUG(10) { printf("dbjl_value(%s@%p, %d)\t", pjlink ? pjlink->pif->name : "", pjlink, result); - printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n", - parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link); + printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", + parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } if (result == jlif_stop || pjlink->parseDepth > 0) @@ -163,29 +162,45 @@ static int dbjl_start_map(void *ctx) { if (!pjlink) { IFDEBUG(10) { printf("dbjl_start_map(NULL)\t"); - printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n", - parser->jsonDepth, parser->key_is_link); + printf(" jsonDepth=%d, parseDepth=00, dbfType=%d\n", + parser->jsonDepth, parser->dbfType); } assert(parser->jsonDepth == 0); parser->jsonDepth++; - parser->key_is_link = 1; return jlif_continue; /* Opening '{' */ } IFDEBUG(10) { printf("dbjl_start_map(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink); - printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n", - parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link); + printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", + parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } pjlink->parseDepth++; parser->jsonDepth++; result = CALL_OR_STOP(pjlink->pif->parse_start_map)(pjlink); - if (result == jlif_key_child_link) { - parser->key_is_link = 1; + switch (result) { + case jlif_key_child_inlink: + parser->dbfType = DBF_INLINK; result = jlif_continue; + break; + case jlif_key_child_outlink: + parser->dbfType = DBF_OUTLINK; + result = jlif_continue; + break; + case jlif_key_child_fwdlink: + parser->dbfType = DBF_FWDLINK; + result = jlif_continue; + break; + case jlif_continue: + break; + default: + errlogPrintf("dbJLinkInit: Bad return %d from '%s'::parse_start_map()\n", + result, pjlink->pif->name); + result = jlif_stop; + break; } IFDEBUG(10) @@ -201,7 +216,7 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) { linkSup *linkSup; jlif *pjlif; - if (!parser->key_is_link) { + if (parser->dbfType == 0) { if (!pjlink) { errlogPrintf("dbJLinkInit: Illegal second link key '%.*s'\n", (int) len, key); @@ -211,8 +226,8 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) { IFDEBUG(10) { printf("dbjl_map_key(%s@%p, \"%.*s\")\t", pjlink->pif->name, pjlink, (int) len, key); - printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n", - parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link); + printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", + parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } assert(pjlink->parseDepth > 0); @@ -223,8 +238,8 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) { IFDEBUG(10) { printf("dbjl_map_key(NULL, \"%.*s\")\t", (int) len, key); - printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n", - parser->jsonDepth, parser->key_is_link); + printf(" jsonDepth=%d, parseDepth=00, dbfType=%d\n", + parser->jsonDepth, parser->dbfType); } link_name = dbmfStrndup((const char *) key, len); @@ -264,7 +279,7 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) { pjlink->parent = NULL; parser->pjlink = pjlink; - parser->key_is_link = 0; + parser->dbfType = 0; dbmfFree(link_name); @@ -282,9 +297,9 @@ static int dbjl_end_map(void *ctx) { IFDEBUG(10) { printf("dbjl_end_map(%s@%p)\t", pjlink ? pjlink->pif->name : "NULL", pjlink); - printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n", + printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, - parser->key_is_link); + parser->dbfType); } parser->jsonDepth--; @@ -306,8 +321,8 @@ static int dbjl_start_array(void *ctx) { IFDEBUG(10) { printf("dbjl_start_array(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink); - printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n", - parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link); + printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", + parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } assert(pjlink); @@ -324,8 +339,8 @@ static int dbjl_end_array(void *ctx) { IFDEBUG(10) { printf("dbjl_end_array(%s@%p)\t", pjlink ? pjlink->pif->name : "", pjlink); - printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n", - parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->key_is_link); + printf(" jsonDepth=%d, parseDepth=%d, dbfType=%d\n", + parser->jsonDepth, pjlink ? pjlink->parseDepth : 0, parser->dbfType); } assert(pjlink); @@ -355,15 +370,14 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType, parser->product = NULL; parser->dbfType = dbfType; parser->jsonDepth = 0; - parser->key_is_link = 0; IFDEBUG(10) printf("dbJLinkInit(\"%.*s\", %d, %p)\n", (int) jlen, json, dbfType, ppjlink); IFDEBUG(10) - printf("dbJLinkInit: jsonDepth=%d, key_is_link=%d\n", - parser->jsonDepth, parser->key_is_link); + printf("dbJLinkInit: jsonDepth=%d, dbfType=%d\n", + parser->jsonDepth, parser->dbfType); yajl_set_default_alloc_funcs(&dbjl_allocs); yh = yajl_alloc(&dbjl_callbacks, &dbjl_allocs, parser); diff --git a/src/ioc/db/dbJLink.h b/src/ioc/db/dbJLink.h index 954b69be5..a94d10ab8 100644 --- a/src/ioc/db/dbJLink.h +++ b/src/ioc/db/dbJLink.h @@ -24,7 +24,7 @@ typedef enum { typedef enum { jlif_key_stop = jlif_stop, jlif_key_continue = jlif_continue, - jlif_key_child_link + jlif_key_child_inlink, jlif_key_child_outlink, jlif_key_child_fwdlink } jlif_key_result; struct link; @@ -71,8 +71,9 @@ typedef struct jlif { /* 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). + /* Optional, parser saw an open-brace '{'. Return jlif_key_child_inlink, + * jlif_key_child_outlink, or jlif_key_child_fwdlink to expect a child + * link next (extra key/value pairs may follow) */ jlif_result (*parse_map_key)(jlink *, const char *key, size_t len); diff --git a/src/std/link/lnkCalc.c b/src/std/link/lnkCalc.c index de76948a9..987d3bd06 100644 --- a/src/std/link/lnkCalc.c +++ b/src/std/link/lnkCalc.c @@ -254,8 +254,10 @@ static jlif_key_result lnkCalc_start_map(jlink *pjlink) IFDEBUG(10) printf("lnkCalc_start_map(calc@%p)\n", clink); - if (clink->pstate == ps_args || clink->pstate == ps_out) - return jlif_key_child_link; + if (clink->pstate == ps_args) + return jlif_key_child_inlink; + if (clink->pstate == ps_out) + return jlif_key_child_outlink; if (clink->pstate != ps_init) { errlogPrintf("lnkCalc: Unexpected map\n"); @@ -463,7 +465,7 @@ static void lnkCalc_report(const jlink *pjlink, int level, int indent) } } -long lnkCalc_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx) +static long lnkCalc_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx) { calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); int i; From fd30989f63f3afe2e773441725d44a735901a1fc Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 1 Jun 2018 23:23:54 -0500 Subject: [PATCH 16/29] Add jlif::start_child() method --- src/ioc/db/dbJLink.c | 21 +++++++++++++-------- src/ioc/db/dbJLink.h | 9 ++++++++- src/ioc/db/test/jlinkz.c | 3 ++- src/ioc/db/test/xLink.c | 5 ++--- src/std/link/lnkCalc.c | 2 +- src/std/link/lnkConst.c | 2 +- src/std/link/lnkState.c | 2 +- 7 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/ioc/db/dbJLink.c b/src/ioc/db/dbJLink.c index a5fd2bed6..df0c2d822 100644 --- a/src/ioc/db/dbJLink.c +++ b/src/ioc/db/dbJLink.c @@ -215,6 +215,7 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) { char *link_name; linkSup *linkSup; jlif *pjlif; + jlink *child; if (parser->dbfType == 0) { if (!pjlink) { @@ -260,31 +261,35 @@ static int dbjl_map_key(void *ctx, const unsigned char *key, size_t len) { return dbjl_return(parser, jlif_stop); } - pjlink = pjlif->alloc_jlink(parser->dbfType); - if (!pjlink) { + child = pjlif->alloc_jlink(parser->dbfType); + if (!child) { errlogPrintf("dbJLinkInit: Link type '%s' allocation failed. \n", link_name); dbmfFree(link_name); return dbjl_return(parser, jlif_stop); } - pjlink->pif = pjlif; - pjlink->parseDepth = 0; + child->pif = pjlif; + child->parseDepth = 0; + child->debug = 0; if (parser->pjlink) { /* We're starting a child link, save its parent */ - pjlink->parent = parser->pjlink; + child->parent = pjlink; + + if (pjlink->pif->start_child) + pjlink->pif->start_child(pjlink, child); } else - pjlink->parent = NULL; + child->parent = NULL; - parser->pjlink = pjlink; + parser->pjlink = child; parser->dbfType = 0; dbmfFree(link_name); IFDEBUG(8) - printf("dbjl_map_key: New %s@%p\n", pjlink ? pjlink->pif->name : "", pjlink); + printf("dbjl_map_key: New %s@%p\n", child ? child->pif->name : "", child); return jlif_continue; } diff --git a/src/ioc/db/dbJLink.h b/src/ioc/db/dbJLink.h index a94d10ab8..54c793175 100644 --- a/src/ioc/db/dbJLink.h +++ b/src/ioc/db/dbJLink.h @@ -90,7 +90,8 @@ typedef struct jlif { 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 */ + * the child link has finished parsing successfully + */ struct lset* (*get_lset)(const jlink *); /* Required, return lset for this link instance */ @@ -107,6 +108,12 @@ typedef struct jlif { * Stop immediately and return status if non-zero. */ + void (*start_child)(jlink *parent, jlink *child); + /* Optional, called with pointer to the new child link after + * parse_start_map() returned a jlif_key_child_link value and + * the child link has been allocated (but not parsed yet) + */ + /* Link types must NOT extend this table with their own routines, * this space is reserved for extensions to the jlink interface. */ diff --git a/src/ioc/db/test/jlinkz.c b/src/ioc/db/test/jlinkz.c index dd6919ffb..fc98ff3b3 100644 --- a/src/ioc/db/test/jlinkz.c +++ b/src/ioc/db/test/jlinkz.c @@ -246,7 +246,8 @@ static jlif jlifZ = { NULL, /* end child */ &z_lset, NULL, /* report */ - NULL /* map child */ + NULL, /* map child */ + NULL /* start child */ }; epicsExportAddress(jlif, jlifZ); diff --git a/src/ioc/db/test/xLink.c b/src/ioc/db/test/xLink.c index 12175f44e..2d7c3c043 100644 --- a/src/ioc/db/test/xLink.c +++ b/src/ioc/db/test/xLink.c @@ -2,7 +2,7 @@ * Copyright (c) 2016 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. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* xLink.c */ @@ -82,7 +82,6 @@ static jlif xlinkIf = { NULL, NULL, NULL, NULL, NULL, NULL, xlink_get_lset, - NULL, NULL + NULL, NULL, NULL }; epicsExportAddress(jlif, xlinkIf); - diff --git a/src/std/link/lnkCalc.c b/src/std/link/lnkCalc.c index 987d3bd06..6b68ac990 100644 --- a/src/std/link/lnkCalc.c +++ b/src/std/link/lnkCalc.c @@ -847,6 +847,6 @@ static jlif lnkCalcIf = { lnkCalc_start_map, lnkCalc_map_key, lnkCalc_end_map, lnkCalc_start_array, lnkCalc_end_array, lnkCalc_end_child, lnkCalc_get_lset, - lnkCalc_report, lnkCalc_map_children + lnkCalc_report, lnkCalc_map_children, NULL }; epicsExportAddress(jlif, lnkCalcIf); diff --git a/src/std/link/lnkConst.c b/src/std/link/lnkConst.c index ee93830ff..3b9392cfa 100644 --- a/src/std/link/lnkConst.c +++ b/src/std/link/lnkConst.c @@ -631,6 +631,6 @@ static jlif lnkConstIf = { NULL, NULL, NULL, lnkConst_start_array, lnkConst_end_array, NULL, lnkConst_get_lset, - lnkConst_report, NULL + lnkConst_report, NULL, NULL }; epicsExportAddress(jlif, lnkConstIf); diff --git a/src/std/link/lnkState.c b/src/std/link/lnkState.c index 8792a1f64..518a37029 100644 --- a/src/std/link/lnkState.c +++ b/src/std/link/lnkState.c @@ -280,6 +280,6 @@ static jlif lnkStateIf = { NULL, NULL, NULL, NULL, NULL, NULL, lnkState_get_lset, - lnkState_report, NULL + lnkState_report, NULL, NULL }; epicsExportAddress(jlif, lnkStateIf); From f2ceb3bbbf51ffb84d76021dfa1005381c97301f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 1 Jun 2018 23:26:53 -0500 Subject: [PATCH 17/29] dbJLink: Add jlif result enum state name strings --- src/ioc/db/dbJLink.c | 18 ++++++++++++++++++ src/ioc/db/dbJLink.h | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/src/ioc/db/dbJLink.c b/src/ioc/db/dbJLink.c index df0c2d822..1c448dd4d 100644 --- a/src/ioc/db/dbJLink.c +++ b/src/ioc/db/dbJLink.c @@ -39,6 +39,19 @@ typedef struct parseContext { short jsonDepth; } parseContext; +epicsShareDef const char *jlif_result_name[2] = { + "jlif_stop", + "jlif_continue", +}; + +epicsShareDef const char *jlif_key_result_name[5] = { + "jlif_key_stop", + "jlif_key_continue", + "jlif_key_child_inlink", + "jlif_key_child_outlink", + "jlif_key_child_fwdlink" +}; + #define CALL_OR_STOP(routine) !(routine) ? jlif_stop : (routine) static int dbjl_return(parseContext *parser, jlif_result result) { @@ -423,6 +436,11 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType, } yajl_free(yh); + + IFDEBUG(10) + printf("dbJLinkInit: returning status=0x%lx\n\n", + status); + return status; } diff --git a/src/ioc/db/dbJLink.h b/src/ioc/db/dbJLink.h index 54c793175..7080ed7cb 100644 --- a/src/ioc/db/dbJLink.h +++ b/src/ioc/db/dbJLink.h @@ -21,12 +21,16 @@ typedef enum { jlif_continue = 1 } jlif_result; +epicsShareExtern const char *jlif_result_name[2]; + typedef enum { jlif_key_stop = jlif_stop, jlif_key_continue = jlif_continue, jlif_key_child_inlink, jlif_key_child_outlink, jlif_key_child_fwdlink } jlif_key_result; +epicsShareExtern const char *jlif_key_result_name[5]; + struct link; struct lset; struct jlif; From 8cdcaf5a875de59f90d6e42d22b1a4ca88242ca4 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 1 Jun 2018 23:32:48 -0500 Subject: [PATCH 18/29] dbJLink: Restore the jlink::debug flag, add debug and trace link types --- src/ioc/db/dbJLink.h | 1 + src/std/link/Makefile | 1 + src/std/link/links.dbd.pod | 36 ++ src/std/link/lnkDebug.c | 1044 ++++++++++++++++++++++++++++++++++++ 4 files changed, 1082 insertions(+) create mode 100644 src/std/link/lnkDebug.c diff --git a/src/ioc/db/dbJLink.h b/src/ioc/db/dbJLink.h index 7080ed7cb..bd1a6c8a2 100644 --- a/src/ioc/db/dbJLink.h +++ b/src/ioc/db/dbJLink.h @@ -39,6 +39,7 @@ typedef struct jlink { struct jlif *pif; /* Link methods */ struct jlink *parent; /* NULL for top-level links */ int parseDepth; /* Used by parser, unused afterwards */ + unsigned debug:1; /* Set to request debug output to console */ /* Link types extend or embed this structure for private storage */ } jlink; diff --git a/src/std/link/Makefile b/src/std/link/Makefile index 5ad9777a8..9b6abd705 100644 --- a/src/std/link/Makefile +++ b/src/std/link/Makefile @@ -14,5 +14,6 @@ DBD += links.dbd dbRecStd_SRCS += lnkConst.c dbRecStd_SRCS += lnkCalc.c dbRecStd_SRCS += lnkState.c +dbRecStd_SRCS += lnkDebug.c HTMLS += links.html diff --git a/src/std/link/links.dbd.pod b/src/std/link/links.dbd.pod index 6b48c13fb..bf13bf7a9 100644 --- a/src/std/link/links.dbd.pod +++ b/src/std/link/links.dbd.pod @@ -15,6 +15,10 @@ The following additional link types are available in this release: =item * L +=item * L + +=item * L + =back =head2 Using JSON Links @@ -200,3 +204,35 @@ link is initialized if it doesn't already exist. {state:"!simEnable"} =cut + + +link(debug, lnkDebugIf) +variable(lnkDebug_debug, int) + +=head3 Debug Link C<"debug"> + +The debug link type exists to enable debugging of other link types; it provides +no functionality itself other than to turn on the debug flag for the child link +that is its only parameter and pass all link operations down to that link. + +=head4 Example + + {debug:{state:"redBeam"}} + +=cut + + +link(trace, lnkTraceIf) + +=head3 Trace Link C<"trace"> + +The trace link type is a relative of the debug link type that also traces the +operation of its child link. At creation it turns on the debug flag of its child +link, then it prints the method arguments and return values of all link +operations before / after passing control down to the child link. + +=head4 Example + + {trace:{state:"redBeam"}} + +=cut diff --git a/src/std/link/lnkDebug.c b/src/std/link/lnkDebug.c new file mode 100644 index 000000000..9ede69cb2 --- /dev/null +++ b/src/std/link/lnkDebug.c @@ -0,0 +1,1044 @@ +/*************************************************************************\ +* Copyright (c) 2018 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. +\*************************************************************************/ +/* lnkDebug.c */ + +/* Usage + * {debug:{...:...}} + */ + +#include +#include +#include + +#include "alarm.h" +#include "dbDefs.h" +#include "dbAccessDefs.h" +#include "dbLink.h" +#include "dbJLink.h" +#include "dbStaticLib.h" +#include "errlog.h" +#include "epicsTime.h" + +#include "epicsExport.h" + +/* This is for debugging the debug link-type */ +int lnkDebug_debug; +epicsExportAddress(int, lnkDebug_debug); + +#define IFDEBUG(n) if (lnkDebug_debug >= (n)) + +typedef struct debug_link { + jlink jlink; /* embedded object */ + short dbfType; + unsigned trace:1; + const jlif *child_jlif; + const lset *child_lset; + jlif jlif; + lset lset; + struct link child_link; +} debug_link; + + +/********************* Delegating jlif Routines *********************/ + +static void delegate_free(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + struct link *plink = &dlink->child_link; + + if (dlink->trace) + printf("Link trace: Calling %s::free_jlink(%p)\n", + pif->name, pjlink); + + pif->free_jlink(pjlink); + plink->type = 0; + plink->value.json.jlink = NULL; + + if (dlink->trace) + printf("Link trace: %s::free_jlink(%p) returned\n", + pif->name, pjlink); +} + +static jlif_result delegate_null(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_null(%p)\n", + pif->name, pjlink); + + res = pif->parse_null(pjlink); + + if (dlink->trace) + printf("Link trace: %s::parse_null(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_boolean(jlink *pjlink, int val) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_boolean(%p, %d)\n", + pif->name, pjlink, val); + + res = pif->parse_boolean(pjlink, val); + + if (dlink->trace) + printf("Link trace: %s::parse_boolean(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_integer(jlink *pjlink, long long num) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_integer(%p, %lld)\n", + pif->name, pjlink, num); + + res = pif->parse_integer(pjlink, num); + + if (dlink->trace) + printf("Link trace: %s::parse_integer(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_double(jlink *pjlink, double num) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_double(%p, %g)\n", + pif->name, pjlink, num); + + res = pif->parse_double(pjlink, num); + + if (dlink->trace) + printf("Link trace: %s::parse_double(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_string(jlink *pjlink, const char *val, size_t len) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_string(%p, \"%.*s\")\n", + pif->name, pjlink, (int) len, val); + + res = pif->parse_string(pjlink, val, len); + + if (dlink->trace) + printf("Link trace: %s::parse_string(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_key_result delegate_start_map(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_key_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_start_map(%p)\n", + pif->name, pjlink); + + res = pif->parse_start_map(pjlink); + + if (dlink->trace) + printf("Link trace: %s::parse_start_map(%p) returned %s\n", + pif->name, pjlink, jlif_key_result_name[res]); + + return res; +} + +static jlif_result delegate_map_key(jlink *pjlink, const char *key, size_t len) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_map_key(%p, \"%.*s\")\n", + pif->name, pjlink, (int) len, key); + + res = pif->parse_map_key(pjlink, key, len); + + if (dlink->trace) + printf("Link trace: %s::parse_map_key(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_end_map(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_end_map(%p)\n", + pif->name, pjlink); + + res = pif->parse_end_map(pjlink); + + if (dlink->trace) + printf("Link trace: %s::parse_end_map(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_start_array(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_(%p)\n", + pif->name, pjlink); + + res = pif->parse_start_array(pjlink); + + if (dlink->trace) + printf("Link trace: %s::parse_start_array(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static jlif_result delegate_end_array(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + jlif_result res; + + if (dlink->trace) + printf("Link trace: Calling %s::parse_end_array(%p)\n", + pif->name, pjlink); + + res = pif->parse_end_array(pjlink); + + if (dlink->trace) + printf("Link trace: %s::parse_end_array(%p) returned %s\n", + pif->name, pjlink, jlif_result_name[res]); + + return res; +} + +static void delegate_start_child(jlink *pjlink, jlink *child) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + + if (dlink->trace) + printf("Link trace: Calling %s::start_child(%p, %p)\n", + pif->name, pjlink, child); + + pif->start_child(pjlink, child); + + if (dlink->trace) + printf("Link trace: %s::start_child(%p) returned\n", + pif->name, pjlink); +} + +static void delegate_end_child(jlink *pjlink, jlink *child) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + + if (dlink->trace) + printf("Link trace: Calling %s::end_child(%p, %p)\n", + pif->name, pjlink, child); + + pif->end_child(pjlink, child); + + if (dlink->trace) + printf("Link trace: %s::end_child(%p) returned\n", + pif->name, pjlink); +} + +static struct lset* delegate_get_lset(const jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + + if (dlink->trace) + printf("Link trace: NOT calling %s::get_lset(%p)\n", + dlink->child_jlif->name, pjlink); + + return &dlink->lset; +} + +static void delegate_report(const jlink *pjlink, int level, int indent) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + + if (dlink->trace) + printf("Link trace: Calling %s::report(%p, %d, %d)\n", + pif->name, pjlink, level, indent); + + pif->report(pjlink, level, indent); + + if (dlink->trace) + printf("Link trace: %s::report(%p) returned\n", + pif->name, pjlink); +} + +static long delegate_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx) +{ + debug_link *dlink = CONTAINER(pjlink->parent, struct debug_link, jlink); + const jlif *pif = dlink->child_jlif; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::map_children(%p, %p, %p)\n", + pif->name, pjlink, rtn, ctx); + + res = pif->map_children(pjlink, rtn, ctx); + + if (dlink->trace) + printf("Link trace: %s::map_children(%p) returned %ld\n", + pif->name, pjlink, res); + + return res; +} + + +/********************* Delegating lset Routines *********************/ + +static void delegate_openLink(struct link *plink) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + + if (dlink->trace) + printf("Link trace: Calling %s::openLink(%p = jlink %p)\n", + dlink->child_jlif->name, clink, clink->value.json.jlink); + + clset->openLink(clink); + + if (dlink->trace) + printf("Link trace: %s::openLink(%p) returned\n", + dlink->child_jlif->name, clink); +} + +static void delegate_removeLink(struct dbLocker *locker, struct link *plink) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + + if (dlink->trace) + printf("Link trace: Calling %s::removeLink(%p, %p)\n", + dlink->child_jlif->name, locker, clink); + + clset->removeLink(locker, clink); + + if (dlink->trace) + printf("Link trace: %s::removeLink(%p) returned\n", + dlink->child_jlif->name, clink); +} + +static long delegate_loadScalar(struct link *plink, short dbrType, void *pbuffer) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::loadScalar(%p, %s, %p)\n", + dlink->child_jlif->name, clink, + dbGetFieldTypeString(dbrType), pbuffer); + + res = clset->loadScalar(clink, dbrType, pbuffer); + + if (dlink->trace) + printf("Link trace: %s::loadScalar(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + + return res; +} + +static long delegate_loadLS(struct link *plink, char *pbuffer, epicsUInt32 size, + epicsUInt32 *plen) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::loadLS(%p, %p, %u)\n", + dlink->child_jlif->name, clink, pbuffer, size); + + res = clset->loadLS(clink, pbuffer, size, plen); + + if (dlink->trace) { + printf("Link trace: %s::loadLS(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Loaded: %u byte(s) \"%s\"\n", *plen, pbuffer); + } + + return res; +} + +static long delegate_loadArray(struct link *plink, short dbrType, void *pbuffer, + long *pnRequest) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::loadArray(%p, %s, %p, %ld)\n", + dlink->child_jlif->name, clink, + dbGetFieldTypeString(dbrType), pbuffer, pnRequest ? *pnRequest : 0l); + + res = clset->loadArray(clink, dbrType, pbuffer, pnRequest); + + if (dlink->trace) { + printf("Link trace: %s::loadArray(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Loaded: %ld element(s)\n", pnRequest ? *pnRequest : 1l); + } + + return res; +} + +static int delegate_isConnected(const struct link *plink) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + int res; + + if (dlink->trace) + printf("Link trace: Calling %s::isConnected(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->isConnected(clink); + + if (dlink->trace) + printf("Link trace: %s::isConnected(%p) returned %d (%s)\n", + dlink->child_jlif->name, clink, res, + res == 0 ? "No" : res == 1 ? "Yes" : "Bad value"); + + return res; +} + +static int delegate_getDBFtype(const struct link *plink) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + int res; + + if (dlink->trace) + printf("Link trace: Calling %s::getDBFtype(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getDBFtype(clink); + + if (dlink->trace) + printf("Link trace: %s::getDBFtype(%p) returned %d (%s)\n", + dlink->child_jlif->name, clink, res, + res == -1 ? "Link disconnected" : dbGetFieldTypeString(res)); + + return res; +} + +static long delegate_getElements(const struct link *plink, long *pnElements) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getElements(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getElements(clink, pnElements); + + if (dlink->trace) { + printf("Link trace: %s::getElements(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Result: %ld element(s)\n", *pnElements); + } + + return res; +} + +static long delegate_getValue(struct link *plink, short dbrType, void *pbuffer, + long *pnRequest) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getValue(%p, %s, %p, %ld)\n", + dlink->child_jlif->name, clink, + dbGetFieldTypeString(dbrType), pbuffer, pnRequest ? *pnRequest : 0l); + + res = clset->getValue(clink, dbrType, pbuffer, pnRequest); + + if (dlink->trace) { + printf("Link trace: %s::getValue(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: %ld element(s)\n", pnRequest ? *pnRequest : 1l); + } + + return res; +} + +static long delegate_getControlLimits(const struct link *plink, + double *lo, double *hi) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getControlLimits(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getControlLimits(clink, lo, hi); + + if (dlink->trace) { + printf("Link trace: %s::getControlLimits(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: Lo = %g, Hi = %g\n", *lo, *hi); + } + + return res; +} + +static long delegate_getGraphicLimits(const struct link *plink, + double *lo, double *hi) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getGraphicLimits(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getGraphicLimits(clink, lo, hi); + + if (dlink->trace) { + printf("Link trace: %s::getGraphicLimits(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: Lo = %g, Hi = %g\n", *lo, *hi); + } + + return res; +} + +static long delegate_getAlarmLimits(const struct link *plink, + double *lolo, double *lo, double *hi, double *hihi) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getAlarmLimits(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getAlarmLimits(clink, lolo, lo, hi, hihi); + + if (dlink->trace) { + printf("Link trace: %s::getAlarmLimits(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: Lolo = %g, Lo = %g, Hi = %g, Hihi = %g\n", + *lolo, *lo, *hi, *hihi); + } + + return res; +} + +static long delegate_getPrecision(const struct link *plink, short *precision) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getPrecision(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getPrecision(clink, precision); + + if (dlink->trace) { + printf("Link trace: %s::getPrecision(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: prec = %d\n", *precision); + } + + return res; +} + +static long delegate_getUnits(const struct link *plink, char *units, int unitsSize) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getUnits(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getUnits(clink, units, unitsSize); + + if (dlink->trace) { + printf("Link trace: %s::getUnits(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got: Units = '%s'\n", units); + } + + return res; +} + +static long delegate_getAlarm(const struct link *plink, epicsEnum16 *stat, + epicsEnum16 *sevr) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getAlarm(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getAlarm(clink, stat, sevr); + + if (dlink->trace) { + printf("Link trace: %s::getAlarm(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) + printf(" Got:%s%s%s%s\n", + stat ? " Status = " : "", + stat && (*stat < ALARM_NSEV) ? + epicsAlarmConditionStrings[*stat] : "Bad-status", + sevr ? " Severity = " : "", + sevr && (*sevr < ALARM_NSTATUS) ? + epicsAlarmSeverityStrings[*sevr] : "Bad-severity" + ); + } + + return res; +} + +static long delegate_getTimeStamp(const struct link *plink, epicsTimeStamp *pstamp) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::getTimeStamp(%p)\n", + dlink->child_jlif->name, clink); + + res = clset->getTimeStamp(clink, pstamp); + + if (dlink->trace) { + printf("Link trace: %s::getTimeStamp(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + if (res == 0) { + char timeStr[32]; + + epicsTimeToStrftime(timeStr, sizeof(timeStr), + "%Y-%m-%d %H:%M:%S.%09f", pstamp); + printf(" Got: Timestamp = '%s'\n", timeStr); + } + } + + return res; +} + +static long delegate_putValue(struct link *plink, short dbrType, + const void *pbuffer, long nRequest) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::putValue(%p, %s, %p, %ld)\n", + dlink->child_jlif->name, clink, + dbGetFieldTypeString(dbrType), pbuffer, nRequest); + + res = clset->putValue(clink, dbrType, pbuffer, nRequest); + + if (dlink->trace) + printf("Link trace: %s::putValue(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + + return res; +} + +static long delegate_putAsync(struct link *plink, short dbrType, + const void *pbuffer, long nRequest) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::putAsync(%p, %s, %p, %ld)\n", + dlink->child_jlif->name, clink, + dbGetFieldTypeString(dbrType), pbuffer, nRequest); + + res = clset->putAsync(clink, dbrType, pbuffer, nRequest); + + if (dlink->trace) + printf("Link trace: %s::putAsync(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + + return res; +} + +static void delegate_scanForward(struct link *plink) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + + if (dlink->trace) + printf("Link trace: Calling %s::scanForward(%p)\n", + dlink->child_jlif->name, clink); + + clset->scanForward(clink); + + if (dlink->trace) + printf("Link trace: %s::scanForward(%p) returned\n", + dlink->child_jlif->name, clink); +} + +static long delegate_doLocked(struct link *plink, dbLinkUserCallback rtn, void *priv) +{ + debug_link *dlink = CONTAINER(plink->value.json.jlink, + struct debug_link, jlink); + struct link *clink = &dlink->child_link; + const lset *clset = dlink->child_lset; + long res; + + if (dlink->trace) + printf("Link trace: Calling %s::doLocked(%p, %p, %p)\n", + dlink->child_jlif->name, clink, rtn, priv); + + res = clset->doLocked(clink, rtn, priv); + + if (dlink->trace) + printf("Link trace: %s::doLocked(%p) returned %ld (0x%lx)\n", + dlink->child_jlif->name, clink, res, res); + + return res; +} + + +/************************ Debug jlif Routines ***********************/ + +static jlink* lnkDebug_alloc(short dbfType) +{ + debug_link *dlink; + + IFDEBUG(10) + printf("lnkDebug_alloc(%s)\n", dbGetFieldTypeString(dbfType)); + + dlink = calloc(1, sizeof(struct debug_link)); + if (!dlink) { + errlogPrintf("lnkDebug: calloc() failed.\n"); + return NULL; + } + + dlink->dbfType = dbfType; + + IFDEBUG(10) + printf("lnkDebug_alloc -> debug@%p\n", dlink); + + return &dlink->jlink; +} + +static jlink* lnkTrace_alloc(short dbfType) +{ + debug_link *dlink; + + IFDEBUG(10) + printf("lnkTrace_alloc(%s)\n", dbGetFieldTypeString(dbfType)); + + dlink = calloc(1, sizeof(struct debug_link)); + if (!dlink) { + errlogPrintf("lnkTrace: calloc() failed.\n"); + return NULL; + } + + dlink->dbfType = dbfType; + dlink->trace = 1; + + IFDEBUG(10) + printf("lnkTrace_alloc -> debug@%p\n", dlink); + + return &dlink->jlink; +} + +static void lnkDebug_free(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_free(debug@%p)\n", dlink); + + dbJLinkFree(dlink->child_link.value.json.jlink); + + free(dlink); +} + +static jlif_key_result lnkDebug_start_map(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_start_map(debug@%p)\n", dlink); + + switch (dlink->dbfType) { + case DBF_INLINK: + return jlif_key_child_inlink; + case DBF_OUTLINK: + return jlif_key_child_outlink; + case DBF_FWDLINK: + return jlif_key_child_outlink; + } + return jlif_stop; +} + +static jlif_result lnkDebug_end_map(jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_end_map(debug@%p)\n", dlink); + + return jlif_continue; +} + +static void lnkDebug_start_child(jlink *parent, jlink *child) +{ + debug_link *dlink = CONTAINER(parent, struct debug_link, jlink); + const jlif *pif = child->pif; + const jlif delegate_jlif = { + pif->name, + pif->alloc_jlink, /* Never used */ + delegate_free, + pif->parse_null ? delegate_null : NULL, + pif->parse_boolean ? delegate_boolean : NULL, + pif->parse_integer ? delegate_integer : NULL, + pif->parse_double ? delegate_double : NULL, + pif->parse_string ? delegate_string : NULL, + pif->parse_start_map ? delegate_start_map : NULL, + pif->parse_map_key ? delegate_map_key : NULL, + pif->parse_end_map ? delegate_end_map : NULL, + pif->parse_start_array ? delegate_start_array : NULL, + pif->parse_end_array ? delegate_end_array : NULL, + pif->end_child ? delegate_end_child : NULL, + delegate_get_lset, + pif->report ? delegate_report : NULL, + pif->map_children ? delegate_map_children : NULL, + pif->start_child ? delegate_start_child : NULL + }; + + IFDEBUG(10) + printf("lnkDebug_start_child(debug@%p, %s@%p)\n", dlink, + child->pif->name, child); + + dlink->child_jlif = pif; + memcpy(&dlink->jlif, &delegate_jlif, sizeof(jlif)); + + child->debug = 1; + child->pif = &dlink->jlif; /* Replace Child's interface table */ + + IFDEBUG(15) + printf("lnkDebug_start_child: pif %p => %p\n", pif, child->pif); + + if (dlink->trace) + printf("Link trace: %s::alloc_jlink(%s) returned %p\n", + pif->name, dbGetFieldTypeString(dlink->dbfType), child); +} + +static void lnkDebug_end_child(jlink *parent, jlink *child) +{ + debug_link *dlink = CONTAINER(parent, struct debug_link, jlink); + struct link *plink = &dlink->child_link; + const lset *plset = dlink->child_jlif->get_lset(child); + lset delegate_lset = { + plset->isConstant, + plset->isVolatile, + plset->openLink ? delegate_openLink : NULL, + plset->removeLink ? delegate_removeLink : NULL, + plset->loadScalar ? delegate_loadScalar : NULL, + plset->loadLS ? delegate_loadLS : NULL, + plset->loadArray ? delegate_loadArray : NULL, + plset->isConnected ? delegate_isConnected : NULL, + plset->getDBFtype ? delegate_getDBFtype : NULL, + plset->getElements ? delegate_getElements : NULL, + plset->getValue ? delegate_getValue : NULL, + plset->getControlLimits ? delegate_getControlLimits : NULL, + plset->getGraphicLimits ? delegate_getGraphicLimits : NULL, + plset->getAlarmLimits ? delegate_getAlarmLimits : NULL, + plset->getPrecision ? delegate_getPrecision : NULL, + plset->getUnits ? delegate_getUnits : NULL, + plset->getAlarm ? delegate_getAlarm : NULL, + plset->getTimeStamp ? delegate_getTimeStamp : NULL, + plset->putValue ? delegate_putValue : NULL, + plset->putAsync ? delegate_putAsync : NULL, + plset->scanForward ? delegate_scanForward : NULL, + plset->doLocked ? delegate_doLocked : NULL + }; + + IFDEBUG(10) + printf("lnkDebug_end_child(debug@%p, %s@%p)\n", dlink, + child->pif->name, child); + + plink->type = JSON_LINK; + plink->value.json.string = NULL; + plink->value.json.jlink = child; + + dlink->child_lset = plset; + memcpy(&dlink->lset, &delegate_lset, sizeof(lset)); + + IFDEBUG(15) + printf("lnkDebug_end_child: lset %p => %p\n", plset, &dlink->lset); +} + +static struct lset* lnkDebug_get_lset(const jlink *pjlink) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_get_lset(debug@%p)\n", pjlink); + + return &dlink->lset; +} + +static void lnkDebug_report(const jlink *pjlink, int level, int indent) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_report(debug@%p)\n", dlink); + + if (dlink->trace) + printf("%*s'trace':\n", indent, ""); + else + printf("%*s'debug':\n", indent, ""); + + if (dlink->child_link.type == JSON_LINK) { + dbJLinkReport(dlink->child_link.value.json.jlink, level, indent + 2); + } +} + +long lnkDebug_map_children(jlink *pjlink, jlink_map_fn rtn, void *ctx) +{ + debug_link *dlink = CONTAINER(pjlink, struct debug_link, jlink); + + IFDEBUG(10) + printf("lnkDebug_map_children(debug@%p)\n", dlink); + + if (dlink->child_link.type == JSON_LINK) { + return dbJLinkMapChildren(&dlink->child_link, rtn, ctx); + } + return 0; +} + + +/************************* Interface Tables *************************/ + +static jlif lnkDebugIf = { + "debug", lnkDebug_alloc, lnkDebug_free, + NULL, NULL, NULL, NULL, NULL, + lnkDebug_start_map, NULL, lnkDebug_end_map, + NULL, NULL, lnkDebug_end_child, lnkDebug_get_lset, + lnkDebug_report, lnkDebug_map_children, lnkDebug_start_child +}; +epicsExportAddress(jlif, lnkDebugIf); + +static jlif lnkTraceIf = { + "trace", lnkTrace_alloc, lnkDebug_free, + NULL, NULL, NULL, NULL, NULL, + lnkDebug_start_map, NULL, lnkDebug_end_map, + NULL, NULL, lnkDebug_end_child, lnkDebug_get_lset, + lnkDebug_report, lnkDebug_map_children, lnkDebug_start_child +}; +epicsExportAddress(jlif, lnkTraceIf); From 89b9e240b0c81938629037803bd08e5818d86884 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 1 Jun 2018 23:43:39 -0500 Subject: [PATCH 19/29] Link types: Remove debug tracing now provided by debug link type --- src/std/link/links.dbd.pod | 3 -- src/std/link/lnkCalc.c | 88 ------------------------------- src/std/link/lnkConst.c | 104 +++++++------------------------------ src/std/link/lnkState.c | 59 +-------------------- 4 files changed, 22 insertions(+), 232 deletions(-) diff --git a/src/std/link/links.dbd.pod b/src/std/link/links.dbd.pod index bf13bf7a9..b5ed582f2 100644 --- a/src/std/link/links.dbd.pod +++ b/src/std/link/links.dbd.pod @@ -39,7 +39,6 @@ database file syntax. link(const, lnkConstIf) -variable(lnkConst_debug, int) =head3 Constant Link C<"const"> @@ -80,7 +79,6 @@ converted to the desired double value at initialization, for example: link(calc, lnkCalcIf) -variable(lnkCalc_debug, int) =head3 Calculation Link C<"calc"> @@ -169,7 +167,6 @@ atomically with the value of the input argument. link(state, lnkStateIf) -variable(lnkState_debug, int) =head3 dbState Link C<"state"> diff --git a/src/std/link/lnkCalc.c b/src/std/link/lnkCalc.c index 6b68ac990..fa1a363fe 100644 --- a/src/std/link/lnkCalc.c +++ b/src/std/link/lnkCalc.c @@ -37,11 +37,6 @@ typedef long (*FASTCONVERT)(); -int lnkCalc_debug; -epicsExportAddress(int, lnkCalc_debug); - -#define IFDEBUG(n) if (lnkCalc_debug >= (n)) - typedef struct calc_link { jlink jlink; /* embedded object */ int nArgs; @@ -82,9 +77,6 @@ static jlink* lnkCalc_alloc(short dbfType) { calc_link *clink; - IFDEBUG(10) - printf("lnkCalc_alloc(%d)\n", dbfType); - if (dbfType == DBF_FWDLINK) { errlogPrintf("lnkCalc: No support for forward links\n"); return NULL; @@ -102,9 +94,6 @@ static jlink* lnkCalc_alloc(short dbfType) clink->prec = 15; /* standard value for a double */ clink->tinp = -1; - IFDEBUG(10) - printf("lnkCalc_alloc -> calc@%p\n", clink); - return &clink->jlink; } @@ -113,9 +102,6 @@ static void lnkCalc_free(jlink *pjlink) calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); int i; - IFDEBUG(10) - printf("lnkCalc_free(calc@%p)\n", clink); - for (i = 0; i < clink->nArgs; i++) dbJLinkFree(clink->inp[i].value.json.jlink); @@ -135,9 +121,6 @@ static jlif_result lnkCalc_integer(jlink *pjlink, long long num) { calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_integer(calc@%p, %lld)\n", clink, num); - if (clink->pstate == ps_prec) { clink->prec = num; return jlif_continue; @@ -163,9 +146,6 @@ static jlif_result lnkCalc_double(jlink *pjlink, double num) { calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_double(calc@%p, %g)\n", clink, num); - if (clink->pstate != ps_args) { return jlif_stop; errlogPrintf("lnkCalc: Unexpected double %g\n", num); @@ -188,9 +168,6 @@ static jlif_result lnkCalc_string(jlink *pjlink, const char *val, size_t len) char *inbuf, *postbuf; short err; - IFDEBUG(10) - printf("lnkCalc_string(calc@%p, \"%.*s\")\n", clink, (int) len, val); - if (clink->pstate == ps_units) { clink->units = epicsStrnDup(val, len); return jlif_continue; @@ -251,9 +228,6 @@ static jlif_key_result lnkCalc_start_map(jlink *pjlink) { calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_start_map(calc@%p)\n", clink); - if (clink->pstate == ps_args) return jlif_key_child_inlink; if (clink->pstate == ps_out) @@ -271,9 +245,6 @@ 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) - printf("lnkCalc_map_key(calc@%p, \"%.*s\")\n", pjlink, (int) len, key); - /* FIXME: These errors messages are wrong when a key is duplicated. * The key is known, we just don't allow it more than once. */ @@ -326,9 +297,6 @@ static jlif_result lnkCalc_end_map(jlink *pjlink) { calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_end_map(calc@%p)\n", clink); - if (clink->pstate == ps_error) return jlif_stop; else if (clink->dbfType == DBF_INLINK && @@ -349,9 +317,6 @@ static jlif_result lnkCalc_start_array(jlink *pjlink) { calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_start_array(calc@%p)\n", clink); - if (clink->pstate != ps_args) { errlogPrintf("lnkCalc: Unexpected array\n"); return jlif_stop; @@ -364,9 +329,6 @@ static jlif_result lnkCalc_end_array(jlink *pjlink) { calc_link *clink = CONTAINER(pjlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_end_array(calc@%p)\n", clink); - if (clink->pstate == ps_error) return jlif_stop; @@ -406,9 +368,6 @@ errOut: static struct lset* lnkCalc_get_lset(const jlink *pjlink) { - IFDEBUG(10) - printf("lnkCalc_get_lset(calc@%p)\n", pjlink); - return &lnkCalc_lset; } @@ -417,9 +376,6 @@ 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); - printf("%*s'calc': \"%s\" = %.*g %s\n", indent, "", clink->expr, clink->prec, clink->val, clink->units ? clink->units : ""); @@ -470,9 +426,6 @@ static 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); @@ -495,9 +448,6 @@ static void lnkCalc_open(struct link *plink) struct calc_link, jlink); int i; - IFDEBUG(10) - printf("lnkCalc_open(calc@%p)\n", clink); - for (i = 0; i < clink->nArgs; i++) { struct link *child = &clink->inp[i]; @@ -517,9 +467,6 @@ static void lnkCalc_remove(struct dbLocker *locker, struct link *plink) struct calc_link, jlink); int i; - IFDEBUG(10) - printf("lnkCalc_remove(calc@%p)\n", clink); - for (i = 0; i < clink->nArgs; i++) { struct link *child = &clink->inp[i]; @@ -548,9 +495,6 @@ static int lnkCalc_isConn(const struct link *plink) int connected = 1; int i; - IFDEBUG(10) - printf("lnkCalc_isConn(calc@%p)\n", clink); - for (i = 0; i < clink->nArgs; i++) { struct link *child = &clink->inp[i]; @@ -572,24 +516,11 @@ static int lnkCalc_isConn(const struct link *plink) static int lnkCalc_getDBFtype(const struct link *plink) { - calc_link *clink = CONTAINER(plink->value.json.jlink, - struct calc_link, jlink); - - IFDEBUG(10) - printf("lnkCalc_getDBFtype(calc@%p)\n", clink); - return DBF_DOUBLE; } static long lnkCalc_getElements(const struct link *plink, long *nelements) { - calc_link *clink = CONTAINER(plink->value.json.jlink, - struct calc_link, jlink); - - IFDEBUG(10) - printf("lnkCalc_getElements(calc@%p, (%ld))\n", - clink, *nelements); - *nelements = 1; return 0; } @@ -622,10 +553,6 @@ static long lnkCalc_getValue(struct link *plink, short dbrType, void *pbuffer, long status; FASTCONVERT conv = dbFastPutConvertRoutine[DBR_DOUBLE][dbrType]; - IFDEBUG(10) - printf("lnkCalc_getValue(calc@%p, %d, ...)\n", - clink, dbrType); - /* Any link errors will trigger a LINK/INVALID alarm in the child link */ for (i = 0; i < clink->nArgs; i++) { struct link *child = &clink->inp[i]; @@ -697,9 +624,6 @@ static long lnkCalc_putValue(struct link *plink, short dbrType, long status; FASTCONVERT conv = dbFastGetConvertRoutine[dbrType][DBR_DOUBLE]; - IFDEBUG(10) - printf("lnkCalc_putValue(calc@%p, %d, ...)\n", clink, dbrType); - /* Any link errors will trigger a LINK/INVALID alarm in the child link */ for (i = 0; i < clink->nArgs; i++) { struct link *child = &clink->inp[i]; @@ -763,9 +687,6 @@ static long lnkCalc_getPrecision(const struct link *plink, short *precision) calc_link *clink = CONTAINER(plink->value.json.jlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_getPrecision(calc@%p)\n", clink); - *precision = clink->prec; return 0; } @@ -775,9 +696,6 @@ static long lnkCalc_getUnits(const struct link *plink, char *units, int len) calc_link *clink = CONTAINER(plink->value.json.jlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_getUnits(calc@%p)\n", clink); - if (clink->units) { strncpy(units, clink->units, --len); units[len] = '\0'; @@ -793,9 +711,6 @@ static long lnkCalc_getAlarm(const struct link *plink, epicsEnum16 *status, calc_link *clink = CONTAINER(plink->value.json.jlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_getAlarm(calc@%p)\n", clink); - if (status) *status = clink->stat; if (severity) @@ -809,9 +724,6 @@ static long lnkCalc_getTimestamp(const struct link *plink, epicsTimeStamp *pstam calc_link *clink = CONTAINER(plink->value.json.jlink, struct calc_link, jlink); - IFDEBUG(10) - printf("lnkCalc_getTimestamp(calc@%p)\n", clink); - if (clink->tinp >= 0) { *pstamp = clink->time; return 0; diff --git a/src/std/link/lnkConst.c b/src/std/link/lnkConst.c index 3b9392cfa..cb948fcb3 100644 --- a/src/std/link/lnkConst.c +++ b/src/std/link/lnkConst.c @@ -22,11 +22,6 @@ #include "epicsExport.h" -int lnkConst_debug; -epicsExportAddress(int, lnkConst_debug); - -#define IFDEBUG(n) if (lnkConst_debug >= (n)) - typedef long (*FASTCONVERT)(); typedef struct const_link { @@ -53,9 +48,6 @@ static jlink* lnkConst_alloc(short dbfType) { const_link *clink; - IFDEBUG(10) - printf("lnkConst_alloc(%d)\n", dbfType); - if (dbfType != DBF_INLINK) { errlogPrintf("lnkConst: Only works with input links\n"); return NULL; @@ -71,9 +63,6 @@ static jlink* lnkConst_alloc(short dbfType) clink->nElems = 0; clink->value.pmem = NULL; - IFDEBUG(10) - printf("lnkConst_alloc -> const@%p\n", clink); - return &clink->jlink; } @@ -81,9 +70,6 @@ static void lnkConst_free(jlink *pjlink) { const_link *clink = CONTAINER(pjlink, const_link, jlink); - IFDEBUG(10) - printf("lnkConst_free(const@%p) type=%d\n", pjlink, clink->type); - switch (clink->type) { int i; case ac40: @@ -109,16 +95,13 @@ static jlif_result lnkConst_integer(jlink *pjlink, long long num) const_link *clink = CONTAINER(pjlink, const_link, jlink); int newElems = clink->nElems + 1; - IFDEBUG(10) - printf("lnkConst_integer(const@%p, %lld)\n", pjlink, num); - switch (clink->type) { void *buf; case s0: clink->type = si64; clink->value.scalar_integer = num; - IFDEBUG(12) + if (pjlink->debug) printf(" si64 := %lld\n", num); break; @@ -132,7 +115,7 @@ static jlif_result lnkConst_integer(jlink *pjlink, long long num) clink->value.pmem = buf; clink->value.pintegers[clink->nElems] = num; - IFDEBUG(12) + if (pjlink->debug) printf(" ai64 += %lld\n", num); break; @@ -143,7 +126,7 @@ static jlif_result lnkConst_integer(jlink *pjlink, long long num) clink->value.pmem = buf; clink->value.pdoubles[clink->nElems] = num; - IFDEBUG(12) + if (pjlink->debug) printf(" af64 += %lld\n", num); break; @@ -160,9 +143,6 @@ static jlif_result lnkConst_integer(jlink *pjlink, long long num) static jlif_result lnkConst_boolean(jlink *pjlink, int val) { - IFDEBUG(10) - printf("lnkConst_boolean(const@%p, %d)\n", pjlink, val); - return lnkConst_integer(pjlink, val); } @@ -171,9 +151,6 @@ static jlif_result lnkConst_double(jlink *pjlink, double num) const_link *clink = CONTAINER(pjlink, const_link, jlink); int newElems = clink->nElems + 1; - IFDEBUG(10) - printf("lnkConst_double(const@%p, %g)\n", pjlink, num); - switch (clink->type) { epicsFloat64 *f64buf; int i; @@ -225,9 +202,6 @@ static jlif_result lnkConst_string(jlink *pjlink, const char *val, size_t len) const_link *clink = CONTAINER(pjlink, const_link, jlink); int newElems = clink->nElems + 1; - IFDEBUG(10) - printf("lnkConst_string(const@%p, \"%.*s\")\n", clink, (int) len, val); - switch (clink->type) { char **vec, *str; @@ -275,9 +249,6 @@ static jlif_result lnkConst_start_array(jlink *pjlink) { const_link *clink = CONTAINER(pjlink, const_link, jlink); - IFDEBUG(10) - printf("lnkConst_start_array(const@%p)\n", pjlink); - if (clink->type != s0) { errlogPrintf("lnkConst: Embedded array value\n"); return jlif_stop; @@ -289,17 +260,11 @@ static jlif_result lnkConst_start_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) { - IFDEBUG(10) - printf("lnkConst_get_lset(const@%p)\n", pjlink); - return &lnkConst_lset; } @@ -327,9 +292,6 @@ static void lnkConst_report(const jlink *pjlink, int level, int indent) }; const char * const dtype = type_names[clink->type & 3]; - IFDEBUG(10) - printf("lnkConst_report(const@%p)\n", clink); - if (clink->type > a0) { const char * const plural = clink->nElems > 1 ? "s" : ""; @@ -391,11 +353,6 @@ static void lnkConst_report(const jlink *pjlink, int level, int indent) static void lnkConst_remove(struct dbLocker *locker, struct link *plink) { - const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink); - - IFDEBUG(10) - printf("lnkConst_remove(const@%p)\n", clink); - lnkConst_free(plink->value.json.jlink); } @@ -404,55 +361,51 @@ static long lnkConst_loadScalar(struct link *plink, short dbrType, void *pbuffer const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink); long status; - IFDEBUG(10) - printf("lnkConst_loadScalar(const@%p, %d, %p)\n", - clink, dbrType, pbuffer); - switch (clink->type) { case si64: - IFDEBUG(12) + if (clink->jlink.debug) printf(" si64 %lld\n", clink->value.scalar_integer); status = dbFastPutConvertRoutine[DBF_INT64][dbrType] (&clink->value.scalar_integer, pbuffer, NULL); break; case sf64: - IFDEBUG(12) + if (clink->jlink.debug) printf(" sf64 %g\n", clink->value.scalar_double); status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType] (&clink->value.scalar_double, pbuffer, NULL); break; case sc40: - IFDEBUG(12) + if (clink->jlink.debug) printf(" sc40 '%s'\n", clink->value.scalar_string); status = dbFastPutConvertRoutine[DBF_STRING][dbrType] (clink->value.scalar_string, pbuffer, NULL); break; case ai64: - IFDEBUG(12) + if (clink->jlink.debug) printf(" ai64 [%lld, ...]\n", clink->value.pintegers[0]); status = dbFastPutConvertRoutine[DBF_INT64][dbrType] (clink->value.pintegers, pbuffer, NULL); break; case af64: - IFDEBUG(12) + if (clink->jlink.debug) printf(" af64 [%g, ...]\n", clink->value.pdoubles[0]); status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType] (clink->value.pdoubles, pbuffer, NULL); break; case ac40: - IFDEBUG(12) + if (clink->jlink.debug) printf(" ac40 ['%s', ...]\n", clink->value.pstrings[0]); status = dbFastPutConvertRoutine[DBF_STRING][dbrType] (clink->value.pstrings[0], pbuffer, NULL); break; default: - IFDEBUG(12) + if (clink->jlink.debug) printf(" Bad type %d\n", clink->type); status = S_db_badField; break; @@ -467,27 +420,23 @@ static long lnkConst_loadLS(struct link *plink, char *pbuffer, epicsUInt32 size, const_link *clink = CONTAINER(plink->value.json.jlink, const_link, jlink); const char *pstr; - IFDEBUG(10) - printf("lnkConst_loadLS(const@%p, %p, %d, %d)\n", - clink, pbuffer, size, *plen); - if(!size) return 0; switch (clink->type) { case sc40: - IFDEBUG(12) + if (clink->jlink.debug) printf(" sc40 '%s'\n", clink->value.scalar_string); pstr = clink->value.scalar_string; break; case ac40: - IFDEBUG(12) + if (clink->jlink.debug) printf(" ac40 ['%s', ...]\n", clink->value.pstrings[0]); pstr = clink->value.pstrings[0]; break; default: - IFDEBUG(12) + if (clink->jlink.debug) printf(" Bad type %d\n", clink->type); return S_db_badField; } @@ -508,10 +457,6 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer, FASTCONVERT conv; long status; - IFDEBUG(10) - printf("lnkConst_loadArray(const@%p, %d, %p, (%ld))\n", - clink, dbrType, pbuffer, *pnReq); - if (nElems > *pnReq) nElems = *pnReq; @@ -519,28 +464,28 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer, int i; case si64: - IFDEBUG(12) + if (clink->jlink.debug) printf(" si64 %lld\n", clink->value.scalar_integer); status = dbFastPutConvertRoutine[DBF_INT64][dbrType] (&clink->value.scalar_integer, pdest, NULL); break; case sf64: - IFDEBUG(12) + if (clink->jlink.debug) printf(" sf64 %g\n", clink->value.scalar_double); status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType] (&clink->value.scalar_double, pdest, NULL); break; case sc40: - IFDEBUG(12) + if (clink->jlink.debug) printf(" sc40 '%s'\n", clink->value.scalar_string); status = dbFastPutConvertRoutine[DBF_STRING][dbrType] (clink->value.scalar_string, pbuffer, NULL); break; case ai64: - IFDEBUG(12) + if (clink->jlink.debug) printf(" ai64 [%lld, ...]\n", clink->value.pintegers[0]); conv = dbFastPutConvertRoutine[DBF_INT64][dbrType]; for (i = 0; i < nElems; i++) { @@ -551,7 +496,7 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer, break; case af64: - IFDEBUG(12) + if (clink->jlink.debug) printf(" af64 [%g, ...]\n", clink->value.pdoubles[0]); conv = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType]; for (i = 0; i < nElems; i++) { @@ -562,7 +507,7 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer, break; case ac40: - IFDEBUG(12) + if (clink->jlink.debug) printf(" ac40 ['%s', ...]\n", clink->value.pstrings[0]); conv = dbFastPutConvertRoutine[DBF_STRING][dbrType]; for (i = 0; i < nElems; i++) { @@ -573,7 +518,7 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer, break; default: - IFDEBUG(12) + if (clink->jlink.debug) printf(" Bad type %d\n", clink->type); status = S_db_badField; } @@ -583,10 +528,6 @@ static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer, static long lnkConst_getNelements(const struct link *plink, long *nelements) { - IFDEBUG(10) - printf("lnkConst_getNelements(const@%p, (%ld))\n", - plink->value.json.jlink, *nelements); - *nelements = 0; return 0; } @@ -594,11 +535,6 @@ static long lnkConst_getNelements(const struct link *plink, long *nelements) static long lnkConst_getValue(struct link *plink, short dbrType, void *pbuffer, long *pnRequest) { - IFDEBUG(10) - printf("lnkConst_getValue(const@%p, %d, %p, ... (%ld))\n", - plink->value.json.jlink, dbrType, pbuffer, - pnRequest ? *pnRequest : 0); - if (pnRequest) *pnRequest = 0; return 0; diff --git a/src/std/link/lnkState.c b/src/std/link/lnkState.c index 518a37029..b8791bd3b 100644 --- a/src/std/link/lnkState.c +++ b/src/std/link/lnkState.c @@ -35,11 +35,6 @@ typedef long (*FASTCONVERT)(); -int lnkState_debug; -epicsExportAddress(int, lnkState_debug); - -#define IFDEBUG(n) if (lnkState_debug >= (n)) - typedef struct state_link { jlink jlink; /* embedded object */ char *name; @@ -57,9 +52,6 @@ static jlink* lnkState_alloc(short dbfType) { state_link *slink; - IFDEBUG(10) - printf("lnkState_alloc(%d)\n", dbfType); - if (dbfType == DBF_FWDLINK) { errlogPrintf("lnkState: DBF_FWDLINK not supported\n"); return NULL; @@ -76,9 +68,6 @@ static jlink* lnkState_alloc(short dbfType) slink->invert = 0; slink->val = 0; - IFDEBUG(10) - printf("lnkState_alloc -> state@%p\n", slink); - return &slink->jlink; } @@ -86,9 +75,6 @@ static void lnkState_free(jlink *pjlink) { state_link *slink = CONTAINER(pjlink, struct state_link, jlink); - IFDEBUG(10) - printf("lnkState_free(state@%p)\n", slink); - free(slink->name); free(slink); } @@ -97,9 +83,6 @@ static jlif_result lnkState_string(jlink *pjlink, const char *val, size_t len) { state_link *slink = CONTAINER(pjlink, struct state_link, jlink); - IFDEBUG(10) - printf("lnkState_string(state@%p, \"%.*s\")\n", slink, (int) len, val); - if (len > 1 && val[0] == '!') { slink->invert = 1; val++; len--; @@ -111,9 +94,6 @@ static jlif_result lnkState_string(jlink *pjlink, const char *val, size_t len) static struct lset* lnkState_get_lset(const jlink *pjlink) { - IFDEBUG(10) - printf("lnkState_get_lset(state@%p)\n", pjlink); - return &lnkState_lset; } @@ -121,9 +101,6 @@ static void lnkState_report(const jlink *pjlink, int level, int indent) { state_link *slink = CONTAINER(pjlink, struct state_link, jlink); - IFDEBUG(10) - printf("lnkState_report(state@%p)\n", slink); - printf("%*s'state': \"%s\" = %s%s\n", indent, "", slink->name, slink->invert ? "! " : "", slink->val ? "TRUE" : "FALSE"); } @@ -135,9 +112,6 @@ static void lnkState_open(struct link *plink) state_link *slink = CONTAINER(plink->value.json.jlink, struct state_link, jlink); - IFDEBUG(10) - printf("lnkState_open(state@%p)\n", slink); - slink->state = dbStateCreate(slink->name); } @@ -146,9 +120,6 @@ static void lnkState_remove(struct dbLocker *locker, struct link *plink) state_link *slink = CONTAINER(plink->value.json.jlink, struct state_link, jlink); - IFDEBUG(10) - printf("lnkState_remove(state@%p)\n", slink); - free(slink->name); free(slink); @@ -157,24 +128,11 @@ static void lnkState_remove(struct dbLocker *locker, struct link *plink) static int lnkState_getDBFtype(const struct link *plink) { - state_link *slink = CONTAINER(plink->value.json.jlink, - struct state_link, jlink); - - IFDEBUG(10) - printf("lnkState_getDBFtype(state@%p)\n", slink); - return DBF_SHORT; } static long lnkState_getElements(const struct link *plink, long *nelements) { - state_link *slink = CONTAINER(plink->value.json.jlink, - struct state_link, jlink); - - IFDEBUG(10) - printf("lnkState_getElements(state@%p, (%ld))\n", - slink, *nelements); - *nelements = 1; return 0; } @@ -184,19 +142,10 @@ static long lnkState_getValue(struct link *plink, short dbrType, void *pbuffer, { state_link *slink = CONTAINER(plink->value.json.jlink, struct state_link, jlink); - long status; - short flag; FASTCONVERT conv = dbFastPutConvertRoutine[DBR_SHORT][dbrType]; - IFDEBUG(10) - printf("lnkState_getValue(state@%p, %d, ...)\n", - slink, dbrType); - - flag = dbStateGet(slink->state); - slink->val = slink->invert ^ flag; - status = conv(&slink->val, pbuffer, NULL); - - return status; + slink->val = slink->invert ^ dbStateGet(slink->state); + return conv(&slink->val, pbuffer, NULL); } static long lnkState_putValue(struct link *plink, short dbrType, @@ -207,10 +156,6 @@ static long lnkState_putValue(struct link *plink, short dbrType, short val; const char *pstr; - IFDEBUG(10) - printf("lnkState_putValue(state@%p, %d, ...)\n", - slink, dbrType); - if (nRequest == 0) return 0; From 5796f717efd9c0fc35a7272a151d50c63e61b359 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 2 Jun 2018 00:40:41 -0500 Subject: [PATCH 20/29] Update Release Notes with link-updates changes --- documentation/RELEASE_NOTES.html | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index f5936bcc2..8be6a340e 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -17,6 +17,38 @@ --> +

Link type enhancements

+ +

This release adds three new link types: "state", "debug" and "trace". The +"state" link type gets and puts boolean values from/to the dbState library that +was added in the 3.15.1 release. The "debug" link type sets the +jlink::debug flag in its child link, while the "trace" link type +also causes the arguments and return values for all calls to the child link's +jlif and lset routines to be printed on stdout. The debug flag can no longer be +set using an info tag. The addition of the "trace" link type has allowed over +200 lines of conditional diagnostic printf() calls to be removed from the other +link types.

+ +

The "calc" link type can now be used for output links as well as input links. +This allows modification of the output value and even combining it with values +from other input links. See the separate JSON Link types document for +details.

+ +

A new start_child() method was added to the end of the jlif +interface table.

+ +

The lset methods have now been properly documented in the +dbLink.h header file using Doxygen annotations, although we do not run Doxygen +on the source tree yet to generate API documentation.

+ +

Link types that utilize child links must now indicate whether the child will +be used for input, output or forward linking by the return value from its +parse_start_map() method. The jlif_key_result enum now +contains 3 values jlif_key_child_inlink, +jlif_key_child_outlink and jlif_key_child_fwdlink +instead of the single jlif_key_child_link that was previously used +for this.

+

Restore use of ledlib for VxWorks command editing

The epicsReadline refactoring work described below unfortunately disabled the From c6c25ab43d7d0ffd87077b37fbacc2b4154ae5f5 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 2 Jun 2018 23:28:20 -0500 Subject: [PATCH 21/29] New src/std/link/test directory with tests for the state link type --- src/Makefile | 6 +- src/std/link/test/Makefile | 53 +++++++++++ src/std/link/test/epicsRunLinkTests.c | 28 ++++++ src/std/link/test/ioRecord.c | 22 +++++ src/std/link/test/ioRecord.db | 1 + src/std/link/test/ioRecord.dbd | 14 +++ src/std/link/test/lnkStateTest.c | 130 ++++++++++++++++++++++++++ src/std/link/test/rtemsTestHarness.c | 14 +++ 8 files changed, 266 insertions(+), 2 deletions(-) create mode 100644 src/std/link/test/Makefile create mode 100644 src/std/link/test/epicsRunLinkTests.c create mode 100644 src/std/link/test/ioRecord.c create mode 100644 src/std/link/test/ioRecord.db create mode 100644 src/std/link/test/ioRecord.dbd create mode 100644 src/std/link/test/lnkStateTest.c create mode 100644 src/std/link/test/rtemsTestHarness.c diff --git a/src/Makefile b/src/Makefile index 257099840..482e14843 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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 the file LICENSE that is included with this distribution. +# in the file LICENSE that is included with this distribution. #************************************************************************* TOP = .. @@ -72,9 +72,11 @@ std_DEPEND_DIRS = ioc libCom/RTEMS DIRS += std/filters/test std/filters/test_DEPEND_DIRS = std +DIRS += std/link/test +std/link/test_DEPEND_DIRS = std + DIRS += std/rec/test std/rec/test_DEPEND_DIRS = std include $(TOP)/configure/RULES_DIRS - diff --git a/src/std/link/test/Makefile b/src/std/link/test/Makefile new file mode 100644 index 000000000..30aec6038 --- /dev/null +++ b/src/std/link/test/Makefile @@ -0,0 +1,53 @@ +#************************************************************************* +# Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne +# 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 + +TESTLIBRARY = Recs + +Recs_SRCS += ioRecord.c +Recs_LIBS += dbCore ca Com + +PROD_LIBS = Recs dbRecStd dbCore ca Com + +DBDDEPENDS_FILES += linkTest.dbd$(DEP) +TARGETS += $(COMMON_DIR)/linkTest.dbd +linkTest_DBD += menuGlobal.dbd +linkTest_DBD += menuConvert.dbd +linkTest_DBD += menuScan.dbd +linkTest_DBD += links.dbd +linkTest_DBD += ioRecord.dbd +TESTFILES += $(COMMON_DIR)/linkTest.dbd + +testHarness_SRCS += linkTest_registerRecordDeviceDriver.cpp + +TESTPROD_HOST += lnkStateTest +lnkStateTest_SRCS += lnkStateTest.c +lnkStateTest_SRCS += linkTest_registerRecordDeviceDriver.cpp +testHarness_SRCS += lnkStateTest.c +TESTFILES += ../ioRecord.db +TESTS += lnkStateTest + +# epicsRunLinkTests runs all the test programs in a known working order. +testHarness_SRCS += epicsRunLinkTests.c + +linkTestHarness_SRCS += $(testHarness_SRCS) +linkTestHarness_SRCS_RTEMS += rtemsTestHarness.c + +PROD_vxWorks = linkTestHarness +PROD_RTEMS = linkTestHarness + +TESTSPEC_vxWorks = linkTestHarness.munch; epicsRunLinkTests +TESTSPEC_RTEMS = linkTestHarness.boot; epicsRunLinkTests + +TESTSCRIPTS_HOST += $(TESTS:%=%.t) + +include $(TOP)/configure/RULES + +ioRecord$(DEP): $(COMMON_DIR)/ioRecord.h +lnkStateTest$(DEP): $(COMMON_DIR)/ioRecord.h diff --git a/src/std/link/test/epicsRunLinkTests.c b/src/std/link/test/epicsRunLinkTests.c new file mode 100644 index 000000000..2cfc7a8a0 --- /dev/null +++ b/src/std/link/test/epicsRunLinkTests.c @@ -0,0 +1,28 @@ +/*************************************************************************\ +* Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in the file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Run filter tests as a batch. + */ + +#include "epicsUnitTest.h" +#include "epicsExit.h" +#include "dbmf.h" + +int lnkStateTest(void); +int lnkCalcTest(void); + +void epicsRunLinkTests(void) +{ + testHarness(); + + runTest(lnkStateTest); + + dbmfFreeChunks(); + + epicsExit(0); /* Trigger test harness */ +} diff --git a/src/std/link/test/ioRecord.c b/src/std/link/test/ioRecord.c new file mode 100644 index 000000000..1807c9171 --- /dev/null +++ b/src/std/link/test/ioRecord.c @@ -0,0 +1,22 @@ +/*************************************************************************\ +* Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in the file LICENSE that is included with this distribution. + \*************************************************************************/ + +/* + * Author: Andrew Johnson + */ + +#include +#include + +#define GEN_SIZE_OFFSET +#include "ioRecord.h" +#undef GEN_SIZE_OFFSET + +#include + +static rset ioRSET; +epicsExportAddress(rset,ioRSET); diff --git a/src/std/link/test/ioRecord.db b/src/std/link/test/ioRecord.db new file mode 100644 index 000000000..7a95a1025 --- /dev/null +++ b/src/std/link/test/ioRecord.db @@ -0,0 +1 @@ +record(io, io) {} diff --git a/src/std/link/test/ioRecord.dbd b/src/std/link/test/ioRecord.dbd new file mode 100644 index 000000000..efaec10a0 --- /dev/null +++ b/src/std/link/test/ioRecord.dbd @@ -0,0 +1,14 @@ +# This is a soft record type with both input and output links + +recordtype(io) { + include "dbCommon.dbd" + field(VAL, DBF_LONG) { + prompt("Value") + } + field(INPUT, DBF_INLINK) { + prompt("Input Link") + } + field(OUTPUT, DBF_OUTLINK) { + prompt("Output Link") + } +} diff --git a/src/std/link/test/lnkStateTest.c b/src/std/link/test/lnkStateTest.c new file mode 100644 index 000000000..a2d514e6b --- /dev/null +++ b/src/std/link/test/lnkStateTest.c @@ -0,0 +1,130 @@ +/*************************************************************************\ +* Copyright (c) 2018 Andrew Johnson +* EPICS BASE is distributed subject to a Software License Agreement found +* in the file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#include "dbAccess.h" +#include "alarm.h" +#include "dbUnitTest.h" +#include "errlog.h" +#include "epicsThread.h" +#include "dbLink.h" +#include "dbState.h" +#include "ioRecord.h" + +#include "testMain.h" + +void linkTest_registerRecordDeviceDriver(struct dbBase *); + +static void startTestIoc(const char *dbfile) +{ + testdbPrepare(); + testdbReadDatabase("linkTest.dbd", NULL, NULL); + linkTest_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase(dbfile, NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); +} + +static void testState() +{ + dbStateId red; + ioRecord *pio; + DBLINK *pinp, *pout; + long status; + + testDiag("testing lnkState"); + + startTestIoc("ioRecord.db"); + + pio = (ioRecord *) testdbRecordPtr("io"); + pinp = &pio->input; + pout = &pio->output; + + red = dbStateFind("red"); + testOk(!red, "No state red exists"); + + testdbPutFieldOk("io.INPUT", DBF_STRING, "{\"state\":\"red\"}"); + if (testOk1(pinp->type == JSON_LINK)) + testDiag("Link was set to '%s'", pinp->value.json.string); + red = dbStateFind("red"); + testOk(!!red, "state red exists"); + + { + epicsInt16 i16; + + dbStateSet(red); + status = dbGetLink(pinp, DBF_SHORT, &i16, NULL, NULL); + testOk(!status, "dbGetLink succeeded"); + testOk(i16, "Got TRUE"); + + testdbPutFieldOk("io.INPUT", DBF_STRING, "{\"state\":\"!red\"}"); + if (testOk1(pinp->type == JSON_LINK)) + testDiag("Link was set to '%s'", pinp->value.json.string); + + status = dbGetLink(pinp, DBF_SHORT, &i16, NULL, NULL); + testOk(!status, "dbGetLink succeeded"); + testOk(!i16, "Got FALSE"); + + testdbPutFieldOk("io.OUTPUT", DBF_STRING, "{\"state\":\"red\"}"); + if (testOk1(pout->type == JSON_LINK)) + testDiag("Link was set to '%s'", pout->value.json.string); + + i16 = 0; + status = dbPutLink(pout, DBF_SHORT, &i16, 1); + testOk(!status, "dbPutLink %d succeeded", i16); + testOk(!dbStateGet(red), "state was cleared"); + + i16 = 0x8000; + status = dbPutLink(pout, DBF_SHORT, &i16, 1); + testOk(!status, "dbPutLink 0x%hx succeeded", i16); + testOk(dbStateGet(red), "state was set"); + } + + status = dbPutLink(pout, DBF_STRING, "", 1); + testOk(!status, "dbPutLink '' succeeded"); + testOk(!dbStateGet(red), "state was cleared"); + + status = dbPutLink(pout, DBF_STRING, "FALSE", 1); /* Not really... */ + testOk(!status, "dbPutLink 'FALSE' succeeded"); + testOk(dbStateGet(red), "state was set"); + + status = dbPutLink(pout, DBF_STRING, "0", 1); + testOk(!status, "dbPutLink '0' succeeded"); + testOk(!dbStateGet(red), "state was cleared"); + + { + epicsFloat64 f64 = 0.1; + + status = dbPutLink(pout, DBF_DOUBLE, &f64, 1); + testOk(!status, "dbPutLink %g succeeded", f64); + testOk(dbStateGet(red), "state was set"); + + testdbPutFieldOk("io.OUTPUT", DBF_STRING, "{\"state\":\"!red\"}"); + if (testOk1(pout->type == JSON_LINK)) + testDiag("Link was set to '%s'", pout->value.json.string); + + status = dbPutLink(pout, DBF_DOUBLE, &f64, 1); + testOk(!status, "dbPutLink %g succeeded", f64); + testOk(!dbStateGet(red), "state was cleared"); + } + + testIocShutdownOk(); + + testdbCleanup(); +} + + +MAIN(lnkStateTest) +{ + testPlan(0); + + testState(); + + return testDone(); +} diff --git a/src/std/link/test/rtemsTestHarness.c b/src/std/link/test/rtemsTestHarness.c new file mode 100644 index 000000000..7397f74d6 --- /dev/null +++ b/src/std/link/test/rtemsTestHarness.c @@ -0,0 +1,14 @@ +/*************************************************************************\ +* Copyright (c) 2018 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in the file LICENSE that is included with this distribution. +\*************************************************************************/ + +extern void epicsRunLinkTests(void); + +int main(int argc, char **argv) +{ + epicsRunLinkTests(); /* calls epicsExit(0) */ + return 0; +} From bcfdc8d3688e92cc4e99a7d9afc956c687eee49c Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sat, 2 Jun 2018 23:59:14 -0500 Subject: [PATCH 22/29] lnkStateTest: set testPlan --- src/std/link/test/lnkStateTest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/std/link/test/lnkStateTest.c b/src/std/link/test/lnkStateTest.c index a2d514e6b..ba686d9e4 100644 --- a/src/std/link/test/lnkStateTest.c +++ b/src/std/link/test/lnkStateTest.c @@ -122,7 +122,7 @@ static void testState() MAIN(lnkStateTest) { - testPlan(0); + testPlan(28); testState(); From ffe6fceffaa24f8cefdae05bb91a8ba36bd4cce8 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 3 Jun 2018 10:01:33 -0500 Subject: [PATCH 23/29] dbJLink: Some extra checks at parse/init time --- src/ioc/db/dbJLink.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/ioc/db/dbJLink.c b/src/ioc/db/dbJLink.c index 1c448dd4d..3acf75660 100644 --- a/src/ioc/db/dbJLink.c +++ b/src/ioc/db/dbJLink.c @@ -207,7 +207,8 @@ static int dbjl_start_map(void *ctx) { parser->dbfType = DBF_FWDLINK; result = jlif_continue; break; - case jlif_continue: + case jlif_key_stop: + case jlif_key_continue: break; default: errlogPrintf("dbJLinkInit: Bad return %d from '%s'::parse_start_map()\n", @@ -446,13 +447,14 @@ long dbJLinkParse(const char *json, size_t jlen, short dbfType, long dbJLinkInit(struct link *plink) { - jlink *pjlink; - assert(plink); - pjlink = plink->value.json.jlink; - if (pjlink) - plink->lset = pjlink->pif->get_lset(pjlink); + if (plink->type == JSON_LINK) { + jlink *pjlink = plink->value.json.jlink; + + if (pjlink) + plink->lset = pjlink->pif->get_lset(pjlink); + } dbLinkOpen(plink); return 0; From 36f23f3aece56990faa6cf43cbb8f15eccdd8417 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 3 Jun 2018 10:46:29 -0500 Subject: [PATCH 24/29] lnkDebug fix: Initialize child link's precord pointer --- src/std/link/lnkDebug.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/std/link/lnkDebug.c b/src/std/link/lnkDebug.c index 9ede69cb2..96ce5e5c1 100644 --- a/src/std/link/lnkDebug.c +++ b/src/std/link/lnkDebug.c @@ -346,6 +346,7 @@ static void delegate_openLink(struct link *plink) printf("Link trace: Calling %s::openLink(%p = jlink %p)\n", dlink->child_jlif->name, clink, clink->value.json.jlink); + clink->precord = plink->precord; clset->openLink(clink); if (dlink->trace) From 2fb46fc541bd07835c5820fe09bd3cd5ff8e78b6 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 3 Jun 2018 11:03:20 -0500 Subject: [PATCH 25/29] lnkDebug: Fix typo in getAlarm range checks --- src/std/link/lnkDebug.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/std/link/lnkDebug.c b/src/std/link/lnkDebug.c index 96ce5e5c1..3238a0b62 100644 --- a/src/std/link/lnkDebug.c +++ b/src/std/link/lnkDebug.c @@ -684,10 +684,10 @@ static long delegate_getAlarm(const struct link *plink, epicsEnum16 *stat, if (res == 0) printf(" Got:%s%s%s%s\n", stat ? " Status = " : "", - stat && (*stat < ALARM_NSEV) ? + stat && (*stat < ALARM_NSTATUS) ? epicsAlarmConditionStrings[*stat] : "Bad-status", sevr ? " Severity = " : "", - sevr && (*sevr < ALARM_NSTATUS) ? + sevr && (*sevr < ALARM_NSEV) ? epicsAlarmSeverityStrings[*sevr] : "Bad-severity" ); } From 6e3aa77c42bd8f4b764b30d8a055ece6aeeb745d Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 3 Jun 2018 11:18:40 -0500 Subject: [PATCH 26/29] lnkCalc fix: Don't evaluate minor expression when major returned true --- src/std/link/lnkCalc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/std/link/lnkCalc.c b/src/std/link/lnkCalc.c index fa1a363fe..2ac568840 100644 --- a/src/std/link/lnkCalc.c +++ b/src/std/link/lnkCalc.c @@ -600,7 +600,7 @@ static long lnkCalc_getValue(struct link *plink, short dbrType, void *pbuffer, } } - if (!status && clink->post_minor) { + if (!status && !clink->sevr && clink->post_minor) { double alval = clink->val; status = calcPerform(clink->arg, &alval, clink->post_minor); @@ -664,7 +664,7 @@ static long lnkCalc_putValue(struct link *plink, short dbrType, } } - if (!status && clink->post_minor) { + if (!status && !clink->sevr && clink->post_minor) { double alval = clink->val; status = calcPerform(clink->arg, &alval, clink->post_minor); From 3b77d9be8cb3cf7e5e26339302b098155abcee16 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 3 Jun 2018 12:34:19 -0500 Subject: [PATCH 27/29] lnkStateTest: Show status value in test output --- src/std/link/test/lnkStateTest.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/std/link/test/lnkStateTest.c b/src/std/link/test/lnkStateTest.c index ba686d9e4..d3da06f5c 100644 --- a/src/std/link/test/lnkStateTest.c +++ b/src/std/link/test/lnkStateTest.c @@ -60,7 +60,7 @@ static void testState() dbStateSet(red); status = dbGetLink(pinp, DBF_SHORT, &i16, NULL, NULL); - testOk(!status, "dbGetLink succeeded"); + testOk(!status, "dbGetLink succeeded (status = %ld)", status); testOk(i16, "Got TRUE"); testdbPutFieldOk("io.INPUT", DBF_STRING, "{\"state\":\"!red\"}"); @@ -68,7 +68,7 @@ static void testState() testDiag("Link was set to '%s'", pinp->value.json.string); status = dbGetLink(pinp, DBF_SHORT, &i16, NULL, NULL); - testOk(!status, "dbGetLink succeeded"); + testOk(!status, "dbGetLink succeeded (status = %ld)", status); testOk(!i16, "Got FALSE"); testdbPutFieldOk("io.OUTPUT", DBF_STRING, "{\"state\":\"red\"}"); @@ -77,32 +77,32 @@ static void testState() i16 = 0; status = dbPutLink(pout, DBF_SHORT, &i16, 1); - testOk(!status, "dbPutLink %d succeeded", i16); + testOk(!status, "dbPutLink %d succeeded (status = %ld)", i16, status); testOk(!dbStateGet(red), "state was cleared"); i16 = 0x8000; status = dbPutLink(pout, DBF_SHORT, &i16, 1); - testOk(!status, "dbPutLink 0x%hx succeeded", i16); + testOk(!status, "dbPutLink 0x%hx succeeded (status = %ld)", i16, status); testOk(dbStateGet(red), "state was set"); } status = dbPutLink(pout, DBF_STRING, "", 1); - testOk(!status, "dbPutLink '' succeeded"); + testOk(!status, "dbPutLink '' succeeded (status = %ld)", status); testOk(!dbStateGet(red), "state was cleared"); status = dbPutLink(pout, DBF_STRING, "FALSE", 1); /* Not really... */ - testOk(!status, "dbPutLink 'FALSE' succeeded"); + testOk(!status, "dbPutLink 'FALSE' succeeded (status = %ld)", status); testOk(dbStateGet(red), "state was set"); status = dbPutLink(pout, DBF_STRING, "0", 1); - testOk(!status, "dbPutLink '0' succeeded"); + testOk(!status, "dbPutLink '0' succeeded (status = %ld)", status); testOk(!dbStateGet(red), "state was cleared"); { epicsFloat64 f64 = 0.1; status = dbPutLink(pout, DBF_DOUBLE, &f64, 1); - testOk(!status, "dbPutLink %g succeeded", f64); + testOk(!status, "dbPutLink %g succeeded (status = %ld)", f64, status); testOk(dbStateGet(red), "state was set"); testdbPutFieldOk("io.OUTPUT", DBF_STRING, "{\"state\":\"!red\"}"); @@ -110,7 +110,7 @@ static void testState() testDiag("Link was set to '%s'", pout->value.json.string); status = dbPutLink(pout, DBF_DOUBLE, &f64, 1); - testOk(!status, "dbPutLink %g succeeded", f64); + testOk(!status, "dbPutLink %g succeeded (status = %ld)", f64, status); testOk(!dbStateGet(red), "state was cleared"); } From b02944805996fa8dcf7856cae420a334d95b8beb Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Sun, 3 Jun 2018 12:35:56 -0500 Subject: [PATCH 28/29] Added lnkCalcTest --- src/std/link/test/Makefile | 8 +- src/std/link/test/epicsRunLinkTests.c | 1 + src/std/link/test/lnkCalcTest.c | 164 ++++++++++++++++++++++++++ 3 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 src/std/link/test/lnkCalcTest.c diff --git a/src/std/link/test/Makefile b/src/std/link/test/Makefile index 30aec6038..2a6507e07 100644 --- a/src/std/link/test/Makefile +++ b/src/std/link/test/Makefile @@ -23,6 +23,7 @@ linkTest_DBD += menuScan.dbd linkTest_DBD += links.dbd linkTest_DBD += ioRecord.dbd TESTFILES += $(COMMON_DIR)/linkTest.dbd +TESTFILES += ../ioRecord.db testHarness_SRCS += linkTest_registerRecordDeviceDriver.cpp @@ -30,9 +31,14 @@ TESTPROD_HOST += lnkStateTest lnkStateTest_SRCS += lnkStateTest.c lnkStateTest_SRCS += linkTest_registerRecordDeviceDriver.cpp testHarness_SRCS += lnkStateTest.c -TESTFILES += ../ioRecord.db TESTS += lnkStateTest +TESTPROD_HOST += lnkCalcTest +lnkCalcTest_SRCS += lnkCalcTest.c +lnkCalcTest_SRCS += linkTest_registerRecordDeviceDriver.cpp +testHarness_SRCS += lnkCalcTest.c +TESTS += lnkCalcTest + # epicsRunLinkTests runs all the test programs in a known working order. testHarness_SRCS += epicsRunLinkTests.c diff --git a/src/std/link/test/epicsRunLinkTests.c b/src/std/link/test/epicsRunLinkTests.c index 2cfc7a8a0..a0f3b7c72 100644 --- a/src/std/link/test/epicsRunLinkTests.c +++ b/src/std/link/test/epicsRunLinkTests.c @@ -21,6 +21,7 @@ void epicsRunLinkTests(void) testHarness(); runTest(lnkStateTest); + runTest(lnkCalcTest); dbmfFreeChunks(); diff --git a/src/std/link/test/lnkCalcTest.c b/src/std/link/test/lnkCalcTest.c new file mode 100644 index 000000000..c0d76dda3 --- /dev/null +++ b/src/std/link/test/lnkCalcTest.c @@ -0,0 +1,164 @@ +/*************************************************************************\ +* Copyright (c) 2018 Andrew Johnson +* EPICS BASE is distributed subject to a Software License Agreement found +* in the file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#include "dbAccess.h" +#include "alarm.h" +#include "dbUnitTest.h" +#include "errlog.h" +#include "epicsThread.h" +#include "dbLink.h" +#include "dbState.h" +#include "recGbl.h" +#include "testMain.h" +#include "ioRecord.h" + +#define testPutLongStr(PV, VAL) \ + testdbPutArrFieldOk(PV, DBF_CHAR, sizeof(VAL), VAL); + +void linkTest_registerRecordDeviceDriver(struct dbBase *); + +static void startTestIoc(const char *dbfile) +{ + testdbPrepare(); + testdbReadDatabase("linkTest.dbd", NULL, NULL); + linkTest_registerRecordDeviceDriver(pdbbase); + testdbReadDatabase(dbfile, NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); +} + +static void testCalc() +{ + ioRecord *pio; + DBLINK *pinp, *pout; + long status; + epicsFloat64 f64; + + startTestIoc("ioRecord.db"); + + pio = (ioRecord *) testdbRecordPtr("io"); + pinp = &pio->input; + pout = &pio->output; + + testDiag("testing lnkCalc input"); + + { + dbStateId red; + + testPutLongStr("io.INPUT", "{\"calc\":{" + "\"expr\":\"a\"," + "\"args\":[{\"state\":\"red\"}]" + "}}"); + if (testOk1(pinp->type == JSON_LINK)) + testDiag("Link was set to '%s'", pinp->value.json.string); + red = dbStateFind("red"); + testOk(!!red, "State red was created"); + + dbStateSet(red); + status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL); + testOk(!status, "dbGetLink succeeded (status = %ld)", status); + testOk(f64, "Got TRUE (%g)", f64); + + dbStateClear(red); + status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL); + testOk(!status, "dbGetLink succeeded (status = %ld)", status); + testOk(!f64, "Got FALSE (%g)", f64); + } + + { + dbStateId major = dbStateCreate("major"); + dbStateId minor = dbStateCreate("minor"); + epicsEnum16 stat, sevr; + + testPutLongStr("io.INPUT", "{\"calc\":{" + "\"expr\":\"0\"," + "\"major\":\"A\"," + "\"minor\":\"B\"," + "\"args\":[{\"state\":\"major\"},{\"state\":\"minor\"}]" + "}}"); + if (testOk1(pinp->type == JSON_LINK)) + testDiag("Link was set to '%s'", pinp->value.json.string); + + dbStateSet(major); + dbStateSet(minor); + status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL); + testOk(!status, "dbGetLink succeeded (status = %ld)", status); + testOk(f64 == 0.0, "Got zero (%g)", f64); + testOk(recGblResetAlarms(pio) && DBE_ALARM, "Record alarm was raised"); + status = dbGetAlarm(pinp, &stat, &sevr); + testOk(!status, "dbGetAlarm succeeded (status = %ld)", status); + testOk(stat == LINK_ALARM, "Alarm status = LINK (%d)", stat); + testOk(sevr == MAJOR_ALARM, "Alarm severity = MAJOR (%d)", sevr); + + dbStateClear(major); + status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL); + testOk(!status, "dbGetLink succeeded (status = %ld)", status); + testOk(recGblResetAlarms(pio) && DBE_ALARM, "Record alarm was raised"); + status = dbGetAlarm(pinp, &stat, &sevr); + testOk(!status, "dbGetAlarm succeeded (status = %ld)", status); + testOk(stat == LINK_ALARM, "Alarm status = LINK (%d)", stat); + testOk(sevr == MINOR_ALARM, "Alarm severity = MINOR (%d)", sevr); + } + + testDiag("testing lnkCalc output"); + + { + dbStateId red = dbStateFind("red"); + dbStateId out = dbStateCreate("out"); + + testPutLongStr("io.OUTPUT", "{\"calc\":{" + "\"expr\":\"!a\"," + "\"out\":{\"state\":\"out\"}," + "\"args\":[{\"state\":\"red\"}]," + "\"units\":\"things\"," + "\"prec\":3" + "}}"); + if (testOk1(pout->type == JSON_LINK)) + testDiag("Link was set to '%s'", pout->value.json.string); + + dbStateSet(red); + f64 = 1.0; + status = dbPutLink(pout, DBF_DOUBLE, &f64, 1); + testOk(!status, "dbPutLink succeeded (status = %ld)", status); + testOk(!dbStateGet(out), "output was cleared"); + + dbStateClear(red); + status = dbPutLink(pout, DBF_DOUBLE, &f64, 1); + testOk(!status, "dbGetLink succeeded (status = %ld)", status); + testOk(dbStateGet(out), "output was set"); + } + + { + char units[20] = {0}; + short prec = 0; + + status = dbGetUnits(pout, units, sizeof(units)); + testOk(!status, "dbGetUnits succeeded (status = %ld)", status); + testOk(!strcmp(units, "things"), "Units string correct (%s)", units); + + status = dbGetPrecision(pout, &prec); + testOk(!status, "dbGetPrecision succeeded (status = %ld)", status); + testOk(prec == 3, "Precision correct (%d)", prec); + } + + testIocShutdownOk(); + + testdbCleanup(); +} + + +MAIN(lnkCalcTest) +{ + testPlan(0); + + testCalc(); + + return testDone(); +} From c0a7ab976c49e39f35919f639082ab00a49728c8 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 4 Jun 2018 15:27:01 -0500 Subject: [PATCH 29/29] Fix warnings from clang --- src/std/link/lnkDebug.c | 2 +- src/std/link/test/lnkCalcTest.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/std/link/lnkDebug.c b/src/std/link/lnkDebug.c index 3238a0b62..bd8f84e5a 100644 --- a/src/std/link/lnkDebug.c +++ b/src/std/link/lnkDebug.c @@ -882,7 +882,7 @@ static jlif_key_result lnkDebug_start_map(jlink *pjlink) case DBF_FWDLINK: return jlif_key_child_outlink; } - return jlif_stop; + return jlif_key_stop; } static jlif_result lnkDebug_end_map(jlink *pjlink) diff --git a/src/std/link/test/lnkCalcTest.c b/src/std/link/test/lnkCalcTest.c index c0d76dda3..3fe8382d4 100644 --- a/src/std/link/test/lnkCalcTest.c +++ b/src/std/link/test/lnkCalcTest.c @@ -91,7 +91,7 @@ static void testCalc() status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL); testOk(!status, "dbGetLink succeeded (status = %ld)", status); testOk(f64 == 0.0, "Got zero (%g)", f64); - testOk(recGblResetAlarms(pio) && DBE_ALARM, "Record alarm was raised"); + testOk(recGblResetAlarms(pio) & DBE_ALARM, "Record alarm was raised"); status = dbGetAlarm(pinp, &stat, &sevr); testOk(!status, "dbGetAlarm succeeded (status = %ld)", status); testOk(stat == LINK_ALARM, "Alarm status = LINK (%d)", stat); @@ -100,7 +100,7 @@ static void testCalc() dbStateClear(major); status = dbGetLink(pinp, DBF_DOUBLE, &f64, NULL, NULL); testOk(!status, "dbGetLink succeeded (status = %ld)", status); - testOk(recGblResetAlarms(pio) && DBE_ALARM, "Record alarm was raised"); + testOk(recGblResetAlarms(pio) & DBE_ALARM, "Record alarm was raised"); status = dbGetAlarm(pinp, &stat, &sevr); testOk(!status, "dbGetAlarm succeeded (status = %ld)", status); testOk(stat == LINK_ALARM, "Alarm status = LINK (%d)", stat);