JSON Links implementation

The lnkConst.c implementation is not yet complete, no support for arrays of
strings (JMOP).

Link error messages should display their record & field name, which is not yet
possible.

The ability to embed links as parameters to other link types is not complete
yet; this will be required for the calc link type.

This code currently passes all existing tests, but additional tests are needed
for the new functionality.
This commit is contained in:
Andrew Johnson
2016-08-29 01:12:09 -05:00
parent 056edc0d8a
commit 7edc0c67ca
22 changed files with 1071 additions and 46 deletions

View File

@@ -25,6 +25,7 @@ INC += dbConvertJSON.h
INC += dbDbLink.h
INC += dbExtractArray.h
INC += dbEvent.h
INC += dbJLink.h
INC += dbLink.h
INC += dbLock.h
INC += dbNotify.h
@@ -73,6 +74,7 @@ dbCore_SRCS += dbConvertJSON.c
dbCore_SRCS += dbDbLink.c
dbCore_SRCS += dbFastLinkConv.c
dbCore_SRCS += dbExtractArray.c
dbCore_SRCS += dbJLink.c
dbCore_SRCS += dbLink.c
dbCore_SRCS += dbNotify.c
dbCore_SRCS += dbScan.c

View File

@@ -13,6 +13,7 @@
*/
#include <stdio.h>
#include <string.h>
#include "dbDefs.h"
@@ -46,28 +47,41 @@ void dbConstAddLink(struct link *plink)
static long dbConstLoadScalar(struct link *plink, short dbrType, void *pbuffer)
{
if (!plink->value.constantStr)
return S_db_badField;
const char *pstr = plink->value.constantStr;
size_t len;
/* Constant scalars are always numeric */
if (!pstr)
return S_db_badField;
len = strlen(pstr);
/* Choice values must be numeric */
if (dbrType == DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
dbrType = DBF_USHORT;
if (*pstr == '[' && pstr[len-1] == ']') {
/* Convert from JSON array */
long nReq = 1;
return dbPutConvertJSON(pstr, dbrType, pbuffer, &nReq);
}
return dbFastPutConvertRoutine[DBR_STRING][dbrType]
(plink->value.constantStr, pbuffer, NULL);
(pstr, pbuffer, NULL);
}
static long dbConstLoadArray(struct link *plink, short dbrType, void *pbuffer,
long *pnReq)
{
if (!plink->value.constantStr)
const char *pstr = plink->value.constantStr;
if (!pstr)
return S_db_badField;
/* No support for arrays of choice types */
/* Choice values must be numeric */
if (dbrType == DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
return S_db_badField;
dbrType = DBF_USHORT;
return dbPutConvertJSON(plink->value.constantStr, dbrType, pbuffer, pnReq);
return dbPutConvertJSON(pstr, dbrType, pbuffer, pnReq);
}
static long dbConstGetNelements(const struct link *plink, long *nelements)

View File

@@ -10,6 +10,7 @@
#include <stdio.h>
#include "dbDefs.h"
#include "errlog.h"
#include "yajl_alloc.h"
#include "yajl_parse.h"
@@ -23,7 +24,7 @@ typedef struct parseContext {
int depth;
short dbrType;
short dbrSize;
void *pdest;
char *pdest;
int elems;
} parseContext;
@@ -67,8 +68,10 @@ static int dbcj_string(void *ctx, const unsigned char *val, unsigned int len) {
/* Not attempting to handle char-array fields here, they need more
* metadata about the field than we have available at the moment.
*/
if (parser->dbrType != DBF_STRING)
if (parser->dbrType != DBF_STRING) {
errlogPrintf("dbPutConvertJSON: String provided, numeric value(s) expected\n");
return 0; /* Illegal */
}
if (parser->elems > 0) {
if (len > parser->dbrSize - 1)
@@ -82,6 +85,7 @@ static int dbcj_string(void *ctx, const unsigned char *val, unsigned int len) {
}
static int dbcj_start_map(void *ctx) {
errlogPrintf("dbPutConvertJSON: Map type not supported\n");
return 0; /* Illegal */
}
@@ -96,7 +100,9 @@ static int dbcj_end_map(void *ctx) {
static int dbcj_start_array(void *ctx) {
parseContext *parser = (parseContext *) ctx;
parser->depth++;
if (++parser->depth > 1)
errlogPrintf("dbPutConvertJSON: Embedded arrays not supported\n");
return (parser->depth == 1);
}

370
src/ioc/db/dbJLink.c Normal file
View File

@@ -0,0 +1,370 @@
/*************************************************************************\
* 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.
\*************************************************************************/
/* dbJLink.c */
#include <stdio.h>
#include <string.h>
#include "epicsAssert.h"
#include "errlog.h"
#include "yajl_alloc.h"
#include "yajl_parse.h"
#define epicsExportSharedSybols
#include "dbAccessDefs.h"
#include "dbCommon.h"
#include "dbJLink.h"
#include "dbStaticLib.h"
#include "link.h"
/* Change 'undef' to 'define' to turn on debug statements: */
#undef DEBUG_JLINK
#ifdef DEBUG_JLINK
int jlinkDebug = 10;
# define IFDEBUG(n) \
if (jlinkDebug >= n) /* block or statement */
#else
# define IFDEBUG(n) \
if(0) /* Compiler will elide the block or statement */
#endif
typedef struct parseContext {
jlink *pjlink;
jlink *product;
struct link *plink;
short dbfType;
short jsonDepth;
short linkDepth;
unsigned key_is_link:1;
} parseContext;
#define CALLIF(routine) !routine ? jlif_stop : routine
static int dbjl_value(parseContext *parser, jlif_result result) {
jlink *pjlink = parser->pjlink;
IFDEBUG(10) {
printf("dbjl_value(%s@%p, %d)\n", pjlink->pif->name, pjlink, result);
printf(" jsonDepth=%d, linkDepth=%d, key_is_link=%d\n",
parser->jsonDepth, parser->linkDepth, parser->key_is_link);
}
if (result == jlif_stop || parser->linkDepth > 0)
return result;
parser->product = pjlink;
parser->key_is_link = 0;
parser->pjlink = pjlink->parent;
IFDEBUG(8)
printf("dbjl_value: product = %p\n", pjlink);
return pjlink->pif->end_parse ? pjlink->pif->end_parse(pjlink)
: jlif_continue;
}
static int dbjl_null(void *ctx) {
parseContext *parser = (parseContext *) ctx;
jlink *pjlink = parser->pjlink;
IFDEBUG(10)
printf("dbjl_null(%s@%p)\n", pjlink->pif->name, pjlink);
assert(pjlink);
return dbjl_value(parser, CALLIF(pjlink->pif->parse_null)(pjlink));
}
static int dbjl_boolean(void *ctx, int val) {
parseContext *parser = (parseContext *) ctx;
jlink *pjlink = parser->pjlink;
assert(pjlink);
return dbjl_value(parser, CALLIF(pjlink->pif->parse_boolean)(pjlink, val));
}
static int dbjl_integer(void *ctx, long num) {
parseContext *parser = (parseContext *) ctx;
jlink *pjlink = parser->pjlink;
IFDEBUG(10)
printf("dbjl_integer(%s@%p, %ld)\n", pjlink->pif->name, pjlink, num);
assert(pjlink);
return dbjl_value(parser, CALLIF(pjlink->pif->parse_integer)(pjlink, num));
}
static int dbjl_double(void *ctx, double num) {
parseContext *parser = (parseContext *) ctx;
jlink *pjlink = parser->pjlink;
IFDEBUG(10)
printf("dbjl_double(%s@%p, %g)\n", pjlink->pif->name, pjlink, num);
assert(pjlink);
return dbjl_value(parser, CALLIF(pjlink->pif->parse_double)(pjlink, num));
}
static int dbjl_string(void *ctx, const unsigned char *val, unsigned len) {
parseContext *parser = (parseContext *) ctx;
jlink *pjlink = parser->pjlink;
IFDEBUG(10)
printf("dbjl_string(%s@%p, \"%.*s\")\n", pjlink->pif->name, pjlink, len, val);
assert(pjlink);
return dbjl_value(parser,
CALLIF(pjlink->pif->parse_string)(pjlink, (const char *) val, len));
}
static int dbjl_start_map(void *ctx) {
parseContext *parser = (parseContext *) ctx;
jlink *pjlink = parser->pjlink;
jlif_key_result result;
if (!pjlink) {
IFDEBUG(10) {
printf("dbjl_start_map(NULL)\n");
printf(" jsonDepth=%d, linkDepth=%d, key_is_link=%d\n",
parser->jsonDepth, parser->linkDepth, parser->key_is_link);
}
assert(parser->jsonDepth == 0);
parser->jsonDepth++;
parser->key_is_link = 1;
return jlif_continue; /* Opening '{' */
}
IFDEBUG(10) {
printf("dbjl_start_map(%s@%p)\n", pjlink->pif->name, pjlink);
printf(" jsonDepth=%d, linkDepth=%d, key_is_link=%d\n",
parser->jsonDepth, parser->linkDepth, parser->key_is_link);
}
parser->linkDepth++;
parser->jsonDepth++;
result = CALLIF(pjlink->pif->parse_start_map)(pjlink);
if (result == jlif_key_embed_link) {
parser->key_is_link = 1;
result = jlif_continue;
}
IFDEBUG(10)
printf("dbjl_start_map -> %d\n", result);
return result;
}
static int dbjl_map_key(void *ctx, const unsigned char *key, unsigned len) {
parseContext *parser = (parseContext *) ctx;
jlink *pjlink = parser->pjlink;
char link_name[MAX_LINK_NAME + 1];
size_t lnlen = len;
linkSup *linkSup;
jlif *pjlif;
jlif_result result;
if (!parser->key_is_link) {
if (!pjlink) {
errlogPrintf("dbJLinkInit: Illegal second link key '%.*s' seen for %s\n",
len, key, parser->plink->precord->name);
return jlif_stop;
}
IFDEBUG(10) {
printf("dbjl_map_key(%s@%p, \"%.*s\")\n",
pjlink->pif->name, pjlink, len, key);
printf(" jsonDepth=%d, linkDepth=%d, key_is_link=%d\n",
parser->jsonDepth, parser->linkDepth, parser->key_is_link);
}
assert(parser->linkDepth > 0);
return CALLIF(pjlink->pif->parse_map_key)(pjlink, (const char *) key, len);
}
IFDEBUG(10) {
printf("dbjl_map_key(NULL, \"%.*s\")\n", len, key);
printf(" jsonDepth=%d, linkDepth=%d, key_is_link=%d\n",
parser->jsonDepth, parser->linkDepth, parser->key_is_link);
}
if (lnlen > MAX_LINK_NAME)
lnlen = MAX_LINK_NAME;
strncpy(link_name, (const char *) key, lnlen);
link_name[lnlen] = '\0';
linkSup = dbFindLinkSup(pdbbase, link_name);
if (!linkSup) {
errlogPrintf("dbJLinkInit: Link type '%s' not found for %s\n",
link_name, parser->plink->precord->name);
return jlif_stop;
}
pjlif = linkSup->pjlif;
if (!pjlif) {
errlogPrintf("dbJLinkInit: Support for Link type '%s' not loaded\n",
link_name);
return jlif_stop;
}
pjlink = pjlif->alloc_jlink(parser->plink);
if (!pjlink) {
errlogPrintf("dbJLinkInit: Out of memory\n");
return jlif_stop;
}
pjlink->pif = pjlif;
if (parser->pjlink) {
/* This is an embedded link */
pjlink->parent = parser->pjlink;
}
result = pjlif->start_parse ? pjlif->start_parse(pjlink) : jlif_continue;
if (result == jlif_continue) {
parser->pjlink = pjlink;
IFDEBUG(8)
printf("dbjl_map_key: New %s@%p\n", pjlink->pif->name, pjlink);
}
else {
pjlif->free_jlink(pjlink);
}
// FIXME Ensure link map has only one link key...
IFDEBUG(10)
printf("dbjl_map_key -> %d\n", result);
return result;
}
static int dbjl_end_map(void *ctx) {
parseContext *parser = (parseContext *) ctx;
jlink *pjlink = parser->pjlink;
jlif_result result;
IFDEBUG(10) {
printf("dbjl_end_map(%s@%p)\n",
pjlink ? pjlink->pif->name : "NULL", pjlink);
printf(" jsonDepth=%d, linkDepth=%d, key_is_link=%d\n",
parser->jsonDepth, parser->linkDepth, parser->key_is_link);
}
parser->jsonDepth--;
if (parser->linkDepth > 0) {
parser->linkDepth--;
result = dbjl_value(parser, CALLIF(pjlink->pif->parse_end_map)(pjlink));
}
else {
result = jlif_continue;
}
return result;
}
static int dbjl_start_array(void *ctx) {
parseContext *parser = (parseContext *) ctx;
jlink *pjlink = parser->pjlink;
IFDEBUG(10) {
printf("dbjl_start_array(%s@%p)\n", pjlink->pif->name, pjlink);
printf(" jsonDepth=%d, linkDepth=%d, key_is_link=%d\n",
parser->jsonDepth, parser->linkDepth, parser->key_is_link);
}
assert(pjlink);
parser->linkDepth++;
parser->jsonDepth++;
return CALLIF(pjlink->pif->parse_start_array)(pjlink);
}
static int dbjl_end_array(void *ctx) {
parseContext *parser = (parseContext *) ctx;
jlink *pjlink = parser->pjlink;
IFDEBUG(10) {
printf("dbjl_end_array(%s@%p)\n", pjlink->pif->name, pjlink);
printf(" jsonDepth=%d, linkDepth=%d, key_is_link=%d\n",
parser->jsonDepth, parser->linkDepth, parser->key_is_link);
}
assert(pjlink);
parser->linkDepth--;
parser->jsonDepth--;
return dbjl_value(parser, CALLIF(pjlink->pif->parse_end_array)(pjlink));
}
static yajl_callbacks dbjl_callbacks = {
dbjl_null, dbjl_boolean, dbjl_integer, dbjl_double, NULL, dbjl_string,
dbjl_start_map, dbjl_map_key, dbjl_end_map, dbjl_start_array, dbjl_end_array
};
static const yajl_parser_config dbjl_config =
{ 0, 0 }; /* allowComments = NO, checkUTF8 = NO */
long dbJLinkInit(struct link *plink, short dbfType)
{
parseContext context, *parser = &context;
yajl_alloc_funcs dbjl_allocs;
yajl_handle yh;
yajl_status ys;
const char *json = plink->value.json.string;
size_t jlen = strlen(json);
long status;
IFDEBUG(10)
printf("dbJLinkInit(\"%s.????\", %d)\n", plink->precord->name, dbfType);
parser->pjlink = NULL;
parser->product = NULL;
parser->plink = plink;
parser->dbfType = dbfType;
parser->jsonDepth = 0;
parser->linkDepth = 0;
parser->key_is_link = 0;
IFDEBUG(10)
printf("dbJLinkInit: jsonDepth=%d, linkDepth=%d, key_is_link=%d\n",
parser->jsonDepth, parser->linkDepth, parser->key_is_link);
yajl_set_default_alloc_funcs(&dbjl_allocs);
yh = yajl_alloc(&dbjl_callbacks, &dbjl_config, &dbjl_allocs, parser);
if (!yh)
return S_db_noMemory;
ys = yajl_parse(yh, (const unsigned char *) json, (unsigned) jlen);
if (ys == yajl_status_insufficient_data)
ys = yajl_parse_complete(yh);
switch (ys) {
unsigned char *err;
jlink *pjlink;
case yajl_status_ok:
assert(parser->jsonDepth == 0);
pjlink = parser->product;
assert(pjlink);
plink->value.json.jlink = pjlink;
plink->lset = pjlink->pif->get_lset(pjlink);
status = 0;
break;
case yajl_status_error:
err = yajl_get_error(yh, 1, (const unsigned char *) json, (unsigned) jlen);
errlogPrintf("dbJLinkInit: %s\n", err);
yajl_free_error(yh, err);
/* fall through */
default:
status = S_db_badField;
}
yajl_free(yh);
return status;
}

70
src/ioc/db/dbJLink.h Normal file
View File

@@ -0,0 +1,70 @@
/*************************************************************************\
* 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.
\*************************************************************************/
/* dbJLink.h */
#ifndef INC_dbJLink_H
#define INC_dbJLink_H
#include <stdlib.h>
#include <shareLib.h>
/* Limit for link name key length */
#define MAX_LINK_NAME 15
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
jlif_stop = 0,
jlif_continue = 1
} jlif_result;
typedef enum {
jlif_key_stop = jlif_stop,
jlif_key_continue = jlif_continue,
jlif_key_embed_link
} jlif_key_result;
struct link;
struct lset;
typedef struct jlink jlink;
typedef struct jlif {
const char *name;
jlink* (*alloc_jlink)(struct link *);
void (*free_jlink)(jlink *);
jlif_result (*start_parse)(jlink *);
jlif_result (*parse_null)(jlink *);
jlif_result (*parse_boolean)(jlink *, int val);
jlif_result (*parse_integer)(jlink *, long num);
jlif_result (*parse_double)(jlink *, double num);
jlif_result (*parse_string)(jlink *, const char *val, size_t len);
jlif_key_result (*parse_start_map)(jlink *);
jlif_result (*parse_map_key)(jlink *, const char *key, size_t len);
jlif_result (*parse_end_map)(jlink *);
jlif_result (*parse_start_array)(jlink *);
jlif_result (*parse_end_array)(jlink *);
jlif_result (*end_parse)(jlink *);
struct lset* (*get_lset)(const jlink *);
void (*report)(const jlink *);
} jlif;
typedef struct jlink {
jlif *pif;
jlink *parent;
/* Link types extend or embed this structure for private storage */
} jlink;
epicsShareFunc long dbJLinkInit(struct link *plink, short dbfType);
#ifdef __cplusplus
}
#endif
#endif /* INC_dbJLink_H */

View File

@@ -37,6 +37,7 @@
#include "dbDbLink.h"
#include "db_field_log.h"
#include "dbFldTypes.h"
#include "dbJLink.h"
#include "dbLink.h"
#include "dbLock.h"
#include "dbScan.h"
@@ -77,6 +78,11 @@ void dbInitLink(struct link *plink, short dbfType)
return;
}
if (plink->type == JSON_LINK) {
dbJLinkInit(plink, dbfType);
return;
}
if (plink->type != PV_LINK)
return;
@@ -120,6 +126,15 @@ void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,
return;
}
if (plink->type == JSON_LINK) {
/*
* FIXME: Can't create DB links as dbJLink types yet,
* dbLock.c doesn't have any way to find/track them.
*/
dbJLinkInit(plink, dbfType);
return;
}
if (plink->type != PV_LINK)
return;

View File

@@ -941,7 +941,9 @@ static void printBuffer(
}
else {
for (i = 0; i < no_elements; i+= MAXLINE - 5) {
sprintf(pmsg, " \"%.*s\"", MAXLINE - 5, (char *)pbuffer + i);
int width = no_elements - i;
if (width > MAXLINE - 5) width = MAXLINE - 5;
sprintf(pmsg, " \"%.*s\"", width, (char *)pbuffer + i);
if (i + MAXLINE - 5 < no_elements) strcat(pmsg, " +");
dbpr_msgOut(pMsgBuff, tab_size);
}

View File

@@ -47,8 +47,8 @@ typedef struct devSup {
typedef struct linkSup {
ELLNODE node;
char *name;
char *lset_name;
struct lset *lset;
char *jlif_name;
struct jlif *pjlif;
} linkSup;
typedef struct dbDeviceMenu {

View File

@@ -75,7 +75,7 @@ static void dbRecordtypeFieldItem(char *name,char *value);
static void dbDevice(char *recordtype,char *linktype,
char *dsetname,char *choicestring);
static void dbDriver(char *name);
static void dbLinkType(char *name, char *lset_name);
static void dbLinkType(char *name, char *jlif_name);
static void dbRegistrar(char *name);
static void dbFunction(char *name);
static void dbVariable(char *name, char *type);
@@ -786,7 +786,7 @@ static void dbDriver(char *name)
ellAdd(&pdbbase->drvList,&pdrvSup->node);
}
static void dbLinkType(char *name, char *lset_name)
static void dbLinkType(char *name, char *jlif_name)
{
linkSup *pLinkSup;
GPHENTRY *pgphentry;
@@ -797,7 +797,7 @@ static void dbLinkType(char *name, char *lset_name)
}
pLinkSup = dbCalloc(1,sizeof(linkSup));
pLinkSup->name = epicsStrDup(name);
pLinkSup->lset_name = epicsStrDup(lset_name);
pLinkSup->jlif_name = epicsStrDup(jlif_name);
pgphentry = gphAdd(pdbbase->pgpHash, pLinkSup->name, &pdbbase->linkList);
if (!pgphentry) {
yyerrorAbort("gphAdd failed");
@@ -1053,7 +1053,7 @@ static void dbRecordField(char *name,char *value)
value++;
value[strlen(value) - 1] = 0;
}
dbTranslateEscape(value, value); /* yuck: in-place, but safe */
dbTranslateEscape(value, value); /* in-place; safe & legal */
status = dbPutString(pdbentry,value);
if(status) {
epicsPrintf("Can't set \"%s.%s\" to \"%s\"\n",

View File

@@ -46,6 +46,7 @@
#include "special.h"
#include "dbCommon.h"
#include "dbJLink.h"
int dbStaticDebug = 0;
static char *pNullString = "";
@@ -122,7 +123,11 @@ void dbFreeLinkContents(struct link *plink)
case CONSTANT: free((void *)plink->value.constantStr); break;
case MACRO_LINK: free((void *)plink->value.macro_link.macroStr); break;
case PV_LINK: free((void *)plink->value.pv_link.pvname); break;
case JSON_LINK: parm = plink->value.json.string; break;
case JSON_LINK:
if (plink->value.json.jlink)
plink->value.json.jlink->pif->free_jlink(plink->value.json.jlink);
parm = plink->value.json.string;
break;
case VME_IO: parm = plink->value.vmeio.parm; break;
case CAMAC_IO: parm = plink->value.camacio.parm; break;
case AB_IO: parm = plink->value.abio.parm; break;
@@ -560,7 +565,7 @@ void dbFreeBase(dbBase *pdbbase)
pdrvSup = pdrvSupNext;
}
while ((plinkSup = (linkSup *) ellGet(&pdbbase->linkList))) {
free(plinkSup->lset_name);
free(plinkSup->jlif_name);
free(plinkSup->name);
free(plinkSup);
}
@@ -1086,7 +1091,7 @@ long dbWriteLinkFP(DBBASE *pdbbase, FILE *fp)
}
for (plinkSup = (linkSup *) ellFirst(&pdbbase->linkList);
plinkSup; plinkSup = (linkSup *) ellNext(&plinkSup->node)) {
fprintf(fp, "link(%s,%s)\n", plinkSup->name, plinkSup->lset_name);
fprintf(fp, "link(%s,%s)\n", plinkSup->name, plinkSup->jlif_name);
}
return 0;
}
@@ -2267,9 +2272,8 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
/* Check for braces => JSON */
if (*str == '{' && str[len-1] == '}') {
/* FIXME Parse JSON object here */
pinfo->ltype = JSON_LINK;
return 0;
return 0;
}
/* Check for other HW link types */
@@ -2337,7 +2341,6 @@ long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
/* Link may be an array constant */
if (pstr[0] == '[' && pstr[len-1] == ']' &&
(strchr(pstr, ',') || strchr(pstr, '"'))) {
/* FIXME Parse JSON array here */
pinfo->ltype = CONSTANT;
return 0;
}

View File

@@ -87,9 +87,10 @@ struct pv_link {
short lastGetdbrType; /* last dbrType for DB or CA get */
};
struct jlink;
struct json_link {
char *string;
/* ... */
struct jlink *jlink;
};
/* structure of a VME io channel */

View File

@@ -66,6 +66,7 @@
#include "recSup.h"
#include "registryDeviceSupport.h"
#include "registryDriverSupport.h"
#include "registryJLinks.h"
#include "registryRecordType.h"
#include "rsrv.h"
@@ -79,6 +80,7 @@ static enum {
/* define forward references*/
static int checkDatabase(dbBase *pdbbase);
static void initDrvSup(void);
static void initJLinks(void);
static void initRecSup(void);
static void initDevSup(void);
static void finishDevSup(void);
@@ -145,6 +147,7 @@ static int iocBuild_2(void)
{
initHookAnnounce(initHookAfterCaLinkInit);
initJLinks();
initDrvSup();
initHookAnnounce(initHookAfterInitDrvSup);
@@ -374,6 +377,22 @@ static void initDrvSup(void) /* Locate all driver support entry tables */
}
}
static void initJLinks(void)
{
linkSup *plinkSup;
for (plinkSup = (linkSup *)ellFirst(&pdbbase->linkList); plinkSup;
plinkSup = (linkSup *)ellNext(&plinkSup->node)) {
jlif *pjlif = registryJLinkFind(plinkSup->name);
if (!pjlif) {
errlogPrintf("iocInit: JLink %s not found\n", plinkSup->name);
continue;
}
plinkSup->pjlif = pjlif;
}
}
static void initRecSup(void)
{
dbRecordType *pdbRecordType;

View File

@@ -14,6 +14,7 @@ SRC_DIRS += $(IOCDIR)/registry
INC += registryRecordType.h
INC += registryDeviceSupport.h
INC += registryDriverSupport.h
INC += registryJLinks.h
INC += registryFunction.h
INC += registryCommon.h
INC += registryIocRegister.h
@@ -21,6 +22,7 @@ INC += registryIocRegister.h
dbCore_SRCS += registryRecordType.c
dbCore_SRCS += registryDeviceSupport.c
dbCore_SRCS += registryDriverSupport.c
dbCore_SRCS += registryJLinks.c
dbCore_SRCS += registryFunction.c
dbCore_SRCS += registryCommon.c
dbCore_SRCS += registryIocRegister.c

View File

@@ -19,6 +19,7 @@
#include "registryCommon.h"
#include "registryDeviceSupport.h"
#include "registryDriverSupport.h"
#include "registryJLinks.h"
void registerRecordTypes(DBBASE *pbase, int nRecordTypes,
@@ -76,3 +77,16 @@ void registerDrivers(DBBASE *pbase, int nDrivers,
}
}
void registerJLinks(DBBASE *pbase, int nLinks, jlif * const *jlifsl)
{
int i;
for (i = 0; i < nLinks; i++) {
if (registryJLinkFind(jlifsl[i]->name)) continue;
if (!registryJLinkAdd(jlifsl[i])) {
errlogPrintf("registryJLinkAdd failed %s\n",
jlifsl[i]->name);
continue;
}
}
}

View File

@@ -12,6 +12,7 @@
#include "dbStaticLib.h"
#include "devSup.h"
#include "dbJLink.h"
#include "registryRecordType.h"
#include "shareLib.h"
@@ -28,6 +29,8 @@ epicsShareFunc void registerDevices(
epicsShareFunc void registerDrivers(
DBBASE *pbase, int nDrivers,
const char * const *driverSupportNames, struct drvet * const *drvsl);
epicsShareFunc void registerJLinks(
DBBASE *pbase, int nDrivers, jlif * const *jlifsl);
#ifdef __cplusplus
}

View File

@@ -0,0 +1,27 @@
/*************************************************************************\
* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
/* registryJLinks.c */
#define epicsExportSharedSymbols
#include "registry.h"
#include "registryJLinks.h"
#include "dbJLink.h"
static void *registryID = "JSON link types";
epicsShareFunc int registryJLinkAdd(struct jlif *pjlif)
{
return registryAdd(registryID, pjlif->name, pjlif);
}
epicsShareFunc jlif * registryJLinkFind(const char *name)
{
return registryFind(registryID, name);
}

View File

@@ -0,0 +1,28 @@
/*************************************************************************\
* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
* National Laboratory.
* Copyright (c) 2002 The Regents of the University of California, as
* Operator of Los Alamos National Laboratory.
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#ifndef INC_registryJLinks_H
#define INC_registryJLinks_H
#include "dbJLink.h"
#include "shareLib.h"
#ifdef __cplusplus
extern "C" {
#endif
epicsShareFunc int registryJLinkAdd(jlif *pjlif);
epicsShareFunc jlif * registryJLinkFind(const char *name);
#ifdef __cplusplus
}
#endif
#endif /* INC_registryDriverSupport_H */

View File

@@ -11,7 +11,7 @@ SRC_DIRS += $(STDDIR)/link
DBD += links.dbd
# dbRecStd_SRCS += ...
dbRecStd_SRCS += lnkConst.c
HTMLS += links.html

View File

@@ -1,7 +1,7 @@
=head1 Link Types
=head1 JSON Links
Links are an extensible mechanism for adding new kinds of database link,
using JSON for link addresses.
JSON Links are an extensible mechanism for adding new kinds of database link,
using JSON for the link address.
The following link types are available in this release:
=over
@@ -12,6 +12,8 @@ The following link types are available in this release:
=item * L<Channel Access|/"Channel Access Link ca">
=item * L<Calc|/"Calculation Link calc">
=back
=head2 Using Links
@@ -21,35 +23,36 @@ must appear inside a pair of braces C< {} > expressed as a JSON
(L<JavaScript Object Notation|http://www.json.org/>) object, which allows link
parameters to be defined as needed.
Note that due to the required presence of the double-quote characters in the
JSON strings in a link field value string in a database file, it will usually
be necessary to escape all double-quote characters in the JSON object by
preceding them with a backslash C< \ > character.
Database configuration tools that support this link mechanism must be careful
to handle these escapes correctly on reading and writing string values from/to
a .db file.
=head2 Filter Reference
=head2 Link Type Reference
=cut
link(const,lsetConst)
link(const, lnkConstIf)
=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<getValue()> routine is called. Most record types support the use of
constant links by calling C<recGblInitConstantLink()> at initialization, which results in
the constant value being loaded into the target field at that time.
=head4 Parameters
...
A const link takes a parameter which may be an integer, double or string, or an array of
those types. If an array contains both integers and double values the integers will be
promoted to doubles. Mixing strings and numbers in an array will result in an error.
=head4 Example
=head4 Examples
...
{const: 3.14159265358979}
{const: "Pi"}
{const: [1, 2.718281828459, 3.14159265358979]}
{const: ["One", "e", "Pi"]}
=cut
link(db,lsetDatabase)
#link(db, lnkDbIf)
=head3 Database Link C<"db">
@@ -65,7 +68,7 @@ link(db,lsetDatabase)
=cut
link(ca,lsetChannelAccess)
#link(ca, lnkCaIf)
=head3 Channel Access Link C<"ca">
@@ -90,3 +93,19 @@ link(ca,lsetChannelAccess)
...
=cut
#link(calc, lnkCalcIf)
=head3 Calculation Link C<"calc">
...
=head4 Parameters
...
=head4 Example
...
=cut

412
src/std/link/lnkConst.c Normal file
View File

@@ -0,0 +1,412 @@
/*************************************************************************\
* 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.
\*************************************************************************/
/* lnkConst.c */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "dbDefs.h"
#include "errlog.h"
#include "epicsAssert.h"
#include "epicsTypes.h"
#include "dbAccessDefs.h"
#include "dbConvertFast.h"
#include "dbLink.h"
#include "dbJLink.h"
#include "epicsExport.h"
/* Change 'undef' to 'define' to turn on debug statements: */
#undef DEBUG_LINK
#ifdef DEBUG_LINK
int lnkConstDebug = 10;
# define IFDEBUG(n) \
if (lnkConstDebug >= n) /* block or statement */
#else
# define IFDEBUG(n) \
if(0) /* Compiler will elide the block or statement */
#endif
typedef long (*FASTCONVERT)();
typedef struct clink {
struct jlink jlink; /* embedded */
int nElems;
enum {s0, si32, sf64, sc40, a0, ai32, af64, ac40} type;
union {
epicsInt32 scalar_integer;
epicsFloat64 scalar_double;
char * scalar_string;
void *pmem;
epicsInt32 *pintegers;
epicsFloat64 *pdoubles;
char **pstrings;
} value;
} clink;
static lset lnkConst_lset;
/*************************** jlif Routines **************************/
static jlink* lnkConst_alloc(struct link *plink) {
clink *clink = calloc(1, sizeof(struct clink));
IFDEBUG(10)
printf("lnkConst_alloc()\n");
clink->type = s0;
clink->nElems = 0;
clink->value.pmem = NULL;
IFDEBUG(10)
printf("lnkConst_alloc -> const@%p\n", clink);
return &clink->jlink;
}
static void lnkConst_free(jlink *pjlink) {
clink *clink = CONTAINER(pjlink, struct clink, jlink);
IFDEBUG(10)
printf("lnkConst_free(const@%p)\n", pjlink);
switch (clink->type) {
int i;
case ac40:
for (i=0; i<clink->nElems; i++)
free(clink->value.pstrings[i]);
/* fall through */
case ai32:
case af64:
free(clink->value.pmem);
default:
break;
}
free(clink);
}
static jlif_result lnkConst_integer(jlink *pjlink, long num) {
clink *clink = CONTAINER(pjlink, struct clink, jlink);
IFDEBUG(10)
printf("lnkConst_integer(const@%p, %ld)\n", pjlink, num);
switch (clink->type) {
void *buf;
int nElems;
case s0:
clink->nElems = 1;
clink->type = si32;
clink->value.scalar_integer = num;
break;
case a0:
clink->type = ai32;
/* fall thorough */
case ai32:
nElems = clink->nElems + 1;
buf = realloc(clink->value.pmem, nElems * sizeof(epicsInt32));
if (!buf) break;
clink->value.pmem = buf;
clink->value.pintegers[clink->nElems] = num;
clink->nElems = nElems;
break;
case af64:
nElems = clink->nElems + 1;
buf = realloc(clink->value.pmem, nElems * sizeof(epicsFloat64));
if (!buf) break;
clink->value.pmem = buf;
clink->value.pdoubles[clink->nElems++] = num;
break;
case ac40:
errlogPrintf("lnkConst: Mixed data types in array\n");
/* FIXME ??? */
default:
return jlif_stop;
}
return jlif_continue;
}
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);
}
static jlif_result lnkConst_double(jlink *pjlink, double num) {
clink *clink = CONTAINER(pjlink, struct clink, jlink);
IFDEBUG(10)
printf("lnkConst_double(const@%p, %g)\n", pjlink, num);
switch (clink->type) {
epicsFloat64 *f64buf;
int nElems, i;
case s0:
clink->nElems = 1;
clink->type = sf64;
clink->value.scalar_double = num;
break;
case a0:
clink->type = af64;
/* fall thorough */
case af64:
nElems = clink->nElems + 1;
f64buf = realloc(clink->value.pmem, nElems * sizeof(epicsFloat64));
if (!f64buf) break;
clink->value.pdoubles = f64buf;
clink->value.pdoubles[clink->nElems++] = num;
break;
case ai32: /* promote earlier ai32 values to af64 */
f64buf = calloc(clink->nElems + 1, sizeof(epicsFloat64));
if (!f64buf) break;
for (i = 0; i < clink->nElems; i++) {
f64buf[i] = clink->value.pintegers[i];
}
f64buf[clink->nElems++] = num;
free(clink->value.pmem);
clink->value.pdoubles = f64buf;
clink->type = af64;
break;
case ac40:
errlogPrintf("lnkConst: Mixed data types in array\n");
/* FIXME ??? */
default:
return jlif_stop;
}
return jlif_continue;
}
static jlif_result lnkConst_string(jlink *pjlink, const char *val, size_t len) {
clink *clink = CONTAINER(pjlink, struct clink, jlink);
IFDEBUG(10)
printf("lnkConst_string(const@%p, \"%.*s\")\n", clink, (int) len, val);
if (len > MAX_STRING_SIZE)
len = MAX_STRING_SIZE;
if (clink->type == s0) {
char *buf = malloc(len + 1);
if (!buf)
return jlif_stop;
strncpy(buf, val, len);
buf[len] = '\0';
clink->nElems = 1;
clink->type = sc40;
clink->value.scalar_string = buf;
return jlif_continue;
}
/* FIXME Implement a0 and ac40 */
/* FIXME How to handle ai32 and af64? */
errlogPrintf("lnkConst: Mixed data types in array\n");
return jlif_stop;
}
static jlif_result lnkConst_start_array(jlink *pjlink) {
clink *clink = CONTAINER(pjlink, struct clink, jlink);
IFDEBUG(10)
printf("lnkConst_start_array(const@%p)\n", pjlink);
if (clink->type != s0) {
errlogPrintf("lnkConst: Embedded array value\n");
return jlif_stop;
}
clink->type = a0;
return jlif_continue;
}
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;
}
static void lnkConst_report(const jlink *pjlink) {
clink *clink = CONTAINER(pjlink, struct clink, jlink);
IFDEBUG(10)
printf("lnkConst_report(const@%p)\n", clink);
/* FIXME Implement! */
}
/*************************** lset Routines **************************/
static long lnkConst_loadScalar(struct link *plink, short dbrType, void *pbuffer)
{
clink *clink = CONTAINER(plink->value.json.jlink, struct clink, jlink);
long status;
IFDEBUG(10)
printf("lnkConst_loadScalar(const@%p, %d, %p)\n",
clink, dbrType, pbuffer);
switch (clink->type) {
case si32:
status = dbFastPutConvertRoutine[DBF_LONG][dbrType]
(&clink->value.scalar_integer, pbuffer, NULL);
break;
case sf64:
status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType]
(&clink->value.scalar_double, pbuffer, NULL);
break;
case sc40:
status = dbFastPutConvertRoutine[DBF_STRING][dbrType]
(clink->value.scalar_string, pbuffer, NULL);
break;
case ai32:
status = dbFastPutConvertRoutine[DBF_LONG][dbrType]
(clink->value.pintegers, pbuffer, NULL);
break;
case af64:
status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType]
(clink->value.pdoubles, pbuffer, NULL);
break;
default:
status = S_db_badField;
break;
}
return status;
}
static long lnkConst_loadArray(struct link *plink, short dbrType, void *pbuffer,
long *pnReq)
{
clink *clink = CONTAINER(plink->value.json.jlink, struct clink, jlink);
short dbrSize = dbValueSize(dbrType);
char *pdest = pbuffer;
int nElems = clink->nElems;
FASTCONVERT conv;
long status;
IFDEBUG(10)
printf("lnkConst_loadArray(const@%p, %d, %p, (%ld))\n",
clink, dbrType, pbuffer, *pnReq);
if (nElems > *pnReq)
nElems = *pnReq;
switch (clink->type) {
int i;
case si32:
status = dbFastPutConvertRoutine[DBF_LONG][dbrType]
(&clink->value.scalar_integer, pdest, NULL);
break;
case sf64:
status = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType]
(&clink->value.scalar_double, pdest, NULL);
break;
case sc40:
status = dbFastPutConvertRoutine[DBF_STRING][dbrType]
(clink->value.scalar_string, pbuffer, NULL);
break;
case ai32:
conv = dbFastPutConvertRoutine[DBF_LONG][dbrType];
for (i = 0; i < nElems; i++) {
conv(&clink->value.pintegers[i], pdest, NULL);
pdest += dbrSize;
}
status = 0;
break;
case af64:
conv = dbFastPutConvertRoutine[DBF_DOUBLE][dbrType];
for (i = 0; i < nElems; i++) {
conv(&clink->value.pdoubles[i], pdest, NULL);
pdest += dbrSize;
}
status = 0;
break;
default:
status = S_db_badField;
}
*pnReq = nElems;
return status;
}
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;
}
static long lnkConst_getValue(struct link *plink, short dbrType, void *pbuffer,
epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest)
{
IFDEBUG(10)
printf("lnkConst_loadScalar(const@%p, %d, %p, ... (%ld))\n",
plink->value.json.jlink, dbrType, pbuffer, *pnRequest);
if (pnRequest)
*pnRequest = 0;
return 0;
}
/************************* Interface Tables *************************/
static lset lnkConst_lset = {
1, 0, /* Constant, not Volatile */
NULL, lnkConst_loadScalar, lnkConst_loadArray, NULL,
NULL, lnkConst_getNelements, lnkConst_getValue,
NULL, NULL, NULL,
NULL, NULL,
NULL, NULL,
NULL, NULL,
NULL
};
static jlif lnkConstIf = {
"const", lnkConst_alloc, lnkConst_free, NULL,
NULL, lnkConst_boolean, lnkConst_integer, lnkConst_double, lnkConst_string,
NULL, NULL, NULL, lnkConst_start_array, lnkConst_end_array,
NULL, lnkConst_get_lset, lnkConst_report
};
epicsExportAddress(jlif, lnkConstIf);

View File

@@ -3,8 +3,8 @@ use DBD::Base;
@ISA = qw(DBD::Base);
sub init {
my ($this, $name, $lset) = @_;
$this->SUPER::init($lset, "link support (lset)");
my ($this, $name, $jlif) = @_;
$this->SUPER::init($jlif, "link support (jlif)");
$this->{KEY} = $name;
return $this;
}

View File

@@ -159,6 +159,20 @@ if (%drivers) {
print $out "};\n\n";
}
my %links = %{$dbd->links};
if (%links) {
my @links = sort keys %links;
# Declare the link interfaces
print $out wrap('epicsShareExtern jlif ', ' ',
join(', ', map {"*pvar_jlif_$_"} @links)), ";\n\n";
# List of pointers to each link interface
print $out "static struct jlif *jlifsl[] = {\n";
print $out join(",\n", map {" pvar_jlif_$_"} @links);
print $out "};\n\n";
}
my @registrars = sort keys %{$dbd->registrars};
my @functions = sort keys %{$dbd->functions};
push @registrars, map {"register_func_$_"} @functions;
@@ -234,6 +248,10 @@ print $out (<< 'END') if %drivers;
registerDrivers(pbase, NELEMENTS(drvsl), driverSupportNames, drvsl);
END
print $out (<< 'END') if %links;
registerJLinks(pbase, NELEMENTS(jlifsl), jlifsl);
END
print $out (<< "END") for @registrars;
pvar_func_$_();
END