From bc1cd1585291fe11a74c480bf59ffcf79fbd1cd6 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Thu, 28 Aug 2014 11:23:22 -0700 Subject: [PATCH 1/6] ioc/db: add optional "tagged" plugin configuration (sets integer tag and any value in one step) --- src/ioc/db/chfPlugin.c | 60 +++++++++++++++++++++-------------- src/ioc/db/chfPlugin.h | 72 ++++++++++++++++++++++++++++++++---------- 2 files changed, 92 insertions(+), 40 deletions(-) diff --git a/src/ioc/db/chfPlugin.c b/src/ioc/db/chfPlugin.c index 0cf4f367f..bcc6a2efe 100644 --- a/src/ioc/db/chfPlugin.c +++ b/src/ioc/db/chfPlugin.c @@ -1,13 +1,14 @@ /*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin -* fuer Materialien und Energie GmbH. +* für Materialien und Energie GmbH. +* Copyright (c) 2014 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* - * Author: Ralph Lange + * Author: Ralph Lange */ /* Based on the linkoptions utility by Michael Davidsaver (BNL) */ @@ -84,19 +85,19 @@ store_integer_value(const chfPluginArgDef *opt, char *user, epicsInt32 val) switch (opt->optType) { case chfPluginArgInt32: - ival = (epicsInt32 *) ((char *)user + opt->offset); + ival = (epicsInt32 *) ((char *)user + opt->dataOffset); *ival = val; break; case chfPluginArgBoolean: - sval = user + opt->offset; + sval = user + opt->dataOffset; *sval = !!val; break; case chfPluginArgDouble: - dval = (double*) (user + opt->offset); + dval = (double*) (user + opt->dataOffset); *dval = val; break; case chfPluginArgString: - sval = user + opt->offset; + sval = user + opt->dataOffset; ret = sprintf(buff, "%ld", (long)val); if (ret < 0 || (unsigned) ret > opt->size - 1) { return -1; @@ -105,7 +106,7 @@ store_integer_value(const chfPluginArgDef *opt, char *user, epicsInt32 val) sval[opt->size-1]='\0'; break; case chfPluginArgEnum: - eval = (int*) (user + opt->offset); + eval = (int*) (user + opt->dataOffset); for (emap = opt->enums; emap && emap->name; emap++) { if (val == emap->value) { *eval = val; @@ -143,19 +144,19 @@ static int store_boolean_value(const chfPluginArgDef *opt, char *user, int val) switch (opt->optType) { case chfPluginArgInt32: - ival = (epicsInt32 *) (user + opt->offset); + ival = (epicsInt32 *) (user + opt->dataOffset); *ival = val; break; case chfPluginArgBoolean: - sval = user + opt->offset; + sval = user + opt->dataOffset; *sval = val; break; case chfPluginArgDouble: - dval = (double*) (user + opt->offset); + dval = (double*) (user + opt->dataOffset); *dval = !!val; break; case chfPluginArgString: - sval = user + opt->offset; + sval = user + opt->dataOffset; if ((unsigned) (val ? 4 : 5) > opt->size - 1) { return -1; } @@ -196,24 +197,24 @@ store_double_value(const chfPluginArgDef *opt, void *vuser, double val) if (val < INT_MIN || val > INT_MAX) { return -1; } - ival = (epicsInt32 *) (user + opt->offset); + ival = (epicsInt32 *) (user + opt->dataOffset); *ival = (epicsInt32) val; break; case chfPluginArgBoolean: - sval = user + opt->offset; + sval = user + opt->dataOffset; *sval = !!val; break; case chfPluginArgDouble: - dval = (double*) (user + opt->offset); + dval = (double*) (user + opt->dataOffset); *dval = val; break; case chfPluginArgString: - sval = user + opt->offset; + sval = user + opt->dataOffset; if (opt->size <= 8) { /* Play it safe: 3 exp + 2 sign + 'e' + '.' */ return -1; } i = epicsSnprintf(sval, opt->size, "%.*g", (int) opt->size - 7, val); - if (i >= opt->size) { + if (i < 0 || (unsigned) i >= opt->size) { return -1; } break; @@ -252,11 +253,11 @@ store_string_value(const chfPluginArgDef *opt, char *user, const char *val, switch (opt->optType) { case chfPluginArgInt32: - ival = (epicsInt32 *) (user + opt->offset); + ival = (epicsInt32 *) (user + opt->dataOffset); return epicsParseInt32(val, ival, 0, &end); case chfPluginArgBoolean: - sval = user + opt->offset; + sval = user + opt->dataOffset; if (epicsStrnCaseCmp(val, "true", len) == 0) { *sval = 1; } else if (epicsStrnCaseCmp(val, "false", len) == 0) { @@ -270,17 +271,17 @@ store_string_value(const chfPluginArgDef *opt, char *user, const char *val, } break; case chfPluginArgDouble: - dval = (double*) (user + opt->offset); + dval = (double*) (user + opt->dataOffset); return epicsParseDouble(val, dval, &end); case chfPluginArgString: - i = opt->size-1 < len ? opt->size-1 : len; - sval = user + opt->offset; + i = opt->size-1 < len ? opt->size-1 : (int) len; + sval = user + opt->dataOffset; strncpy(sval, val, i); sval[i] = '\0'; break; case chfPluginArgEnum: - eval = (int*) (user + opt->offset); + eval = (int*) (user + opt->dataOffset); for (emap = opt->enums; emap && emap->name; emap++) { if (strncmp(emap->name, val, len) == 0) { *eval = emap->value; @@ -455,8 +456,10 @@ parse_map_key(chFilter *filter, const char *key, size_t stringLen) { const chfPluginArgDef *cur; const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; - chfFilter *f = (chfFilter*)filter->puser; + chfFilter *f = (chfFilter*)filter->puser; + int *tag; int i; + int j; f->nextParam = -1; for(cur = opts, i = 0; cur && cur->name; cur++, i++) { @@ -470,6 +473,17 @@ parse_map_key(chFilter *filter, const char *key, size_t stringLen) } f->found[i/32] |= 1<<(i%32); + /* For tagged parameters: + * set tag to this choice, and mark all other choices as found */ + if (opts[i].tagged) { + tag = (int*) ((char*) f->puser + opts[i].tagOffset); + *tag = opts[i].choice; + for (cur = opts, j = 0; cur && cur->name; cur++, j++) { + if (cur->tagged && cur->tagOffset == opts[i].tagOffset) { + f->found[j/32] |= 1<<(j%32); + } + } + } return parse_continue; } diff --git a/src/ioc/db/chfPlugin.h b/src/ioc/db/chfPlugin.h index 47d3055c4..eabaa7ac3 100644 --- a/src/ioc/db/chfPlugin.h +++ b/src/ioc/db/chfPlugin.h @@ -1,13 +1,14 @@ /*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin -* fuer Materialien und Energie GmbH. +* für Materialien und Energie GmbH. +* Copyright (c) 2014 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* - * Author: Ralph Lange + * Author: Ralph Lange */ /* Based on the linkoptions utility by Michael Davidsaver (BNL) */ @@ -44,10 +45,11 @@ struct db_field_log; * * typedef struct myStruct { * ... other stuff + * char mode; * epicsInt32 ival; - * double dval; + * double dval; * epicsInt32 ival2; - * int enumval; + * int enumval; * char strval[20]; * char boolval; * } myStruct; @@ -57,18 +59,22 @@ struct db_field_log; * * static const * chfPluginDef myStructDef[] = { - * chfInt32 (myStruct, ival, "Integer" , 0, 0), - * chfInt32 (myStruct, ival2, "Second" , 1, 0), - * chfDouble(myStruct, dval, "Double" , 1, 0), - * chfString(myStruct, strval , "String" , 1, 0), - * chfEnum (myStruct, enumval, "Color" , 1, 0, colorEnum), - * chfBoolean(myStruct, boolval, "Bool" , 1, 0), + * chfTagInt32(myStruct, ival, "Integer" , ival2, 3, 0, 0), + * chfInt32 (myStruct, ival2, "Second" , 1, 0), + * chfDouble (myStruct, dval, "Double" , 1, 0), + * chfString (myStruct, strval , "String" , 1, 0), + * chfEnum (myStruct, enumval, "Color" , 1, 0, colorEnum), + * chfBoolean (myStruct, boolval, "Bool" , 1, 0), * chfPluginEnd * }; * * Note: The 4th argument specifies the parameter to be required (1) or optional (0), * the 5th whether converting to the required type is allowed (1), or * type mismatches are an error (0). + * Note: The "Tag" version has two additional arguments. the 4th arg specifies the tag + * field (integer type) inside the structure to be set, the 5th arg specifies the + * value to set the tag field to. Arguments 6 and 7 specify "required" and + * "conversion" as described above. * */ @@ -231,25 +237,57 @@ typedef struct chfPluginArgDef { chfPluginArg optType; unsigned int required:1; unsigned int convert:1; - epicsUInt32 offset; - epicsUInt32 size; + unsigned int tagged:1; + epicsUInt32 tagOffset; + epicsUInt32 choice; + epicsUInt32 dataOffset; + epicsUInt32 size; const chfPluginEnumType *enums; } chfPluginArgDef; +/* Simple arguments */ + #define chfInt32(Struct, Member, Name, Req, Conv) \ -{Name, chfPluginArgInt32, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} + {Name, chfPluginArgInt32, Req, Conv, 0, 0, 0, \ + OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} #define chfBoolean(Struct, Member, Name, Req, Conv) \ -{Name, chfPluginArgBoolean, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} + {Name, chfPluginArgBoolean, Req, Conv, 0, 0, 0, \ + OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} #define chfDouble(Struct, Member, Name, Req, Conv) \ -{Name, chfPluginArgDouble, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} + {Name, chfPluginArgDouble, Req, Conv, 0, 0, 0, \ + OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} #define chfString(Struct, Member, Name, Req, Conv) \ -{Name, chfPluginArgString, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} + {Name, chfPluginArgString, Req, Conv, 0, 0, 0, \ + OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} #define chfEnum(Struct, Member, Name, Req, Conv, Enums) \ -{Name, chfPluginArgEnum, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), Enums} + {Name, chfPluginArgEnum, Req, Conv, 0, 0, 0, \ + OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), Enums} + +/* Tagged arguments */ + +#define chfTagInt32(Struct, Member, Name, Tag, Choice, Req, Conv) \ + {Name, chfPluginArgInt32, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \ + OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} + +#define chfTagBoolean(Struct, Member, Name, Tag, Choice, Req, Conv) \ + {Name, chfPluginArgBoolean, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \ + OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} + +#define chfTagDouble(Struct, Member, Name, Tag, Choice, Req, Conv) \ + {Name, chfPluginArgDouble, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \ + OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} + +#define chfTagString(Struct, Member, Name, Tag, Choice, Req, Conv) \ + {Name, chfPluginArgString, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \ + OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} + +#define chfTagEnum(Struct, Member, Name, Tag, Choice, Req, Conv, Enums) \ + {Name, chfPluginArgEnum, Req, Conv, 1, OFFSET(Struct, Tag), Choice, \ + OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), Enums} #define chfPluginArgEnd {0} From 17da152e222fbf87e6e5d4d2f94218f1da2322bd Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Thu, 28 Aug 2014 11:24:09 -0700 Subject: [PATCH 2/6] ioc/db/test: add test for tagged plugin configuration --- src/ioc/db/test/chfPluginTest.c | 141 ++++++++++++++++++++++++++------ 1 file changed, 115 insertions(+), 26 deletions(-) diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index 028ecceb2..1e2dcace9 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -1,13 +1,14 @@ /*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin -* fuer Materialien und Energie GmbH. +* für Materialien und Energie GmbH. +* Copyright (c) 2014 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* - * Author: Ralph Lange + * Author: Ralph Lange */ #include @@ -59,6 +60,8 @@ typedef struct myStruct { int sent5; char str[20]; int sent6; + char tval; + int sent7; char c; char c1[2]; int offpre; @@ -68,6 +71,16 @@ typedef struct myStruct { static const chfPluginEnumType colorEnum[] = { {"R", 1}, {"G", 2}, {"B", 4}, {NULL,0} }; +static const +chfPluginArgDef taggedOpts[] = { + chfTagInt32 (myStruct, ival, "i" , tval, 1, 0, 0), + chfTagBoolean(myStruct, flag, "f" , tval, 2, 0, 0), + chfTagDouble (myStruct, dval, "d" , tval, 3, 0, 0), + chfTagString (myStruct, str, "s" , tval, 4, 0, 0), + chfTagEnum (myStruct, enumval, "c" , tval, 5, 0, 0, colorEnum), + chfPluginArgEnd +}; + static const chfPluginArgDef strictOpts[] = { chfInt32 (myStruct, ival, "i" , 1, 0), @@ -133,8 +146,9 @@ static void clearStruct(void *p) { if (!my) return; memset(my, 0, sizeof(myStruct)); my->sent1 = my->sent2 = my->sent3 = my->sent4 = - my->sent5 = my->sent6 = PATTERN; + my->sent5 = my->sent6 = my->sent7 = PATTERN; my->ival = 12; + my->tval = 99; my->flag = 1; my->dval = 1.234e5; strcpy(my->str, "hello"); @@ -447,7 +461,7 @@ static chfPluginIf postPif = { }; static int checkValues(myStruct *my, - epicsUInt32 i, int f, double d, char *s1, char *s2, int c) { + char t, epicsUInt32 i, int f, double d, char *s1, char *s2, int c) { int ret = 1; int s1fail, s2fail; int s2valid = (s2 && s2[0] != '\0'); @@ -460,6 +474,8 @@ static int checkValues(myStruct *my, CHK(my->sent4, PATTERN, "%08x") CHK(my->sent5, PATTERN, "%08x") CHK(my->sent6, PATTERN, "%08x") + CHK(my->sent7, PATTERN, "%08x") + CHK(my->tval, t, "%08x") CHK(my->ival, i, "%08x") CHK(my->flag, f, "%02x") CHK(my->dval, d, "%f") @@ -492,7 +508,7 @@ MAIN(chfPluginTest) _set_output_format(_TWO_DIGIT_EXPONENT); #endif - testPlan(1351); + testPlan(1397); dbChannelInit(); db_init_events(); @@ -532,6 +548,8 @@ MAIN(chfPluginTest) eltc(1); testHead("Register plugins"); + testOk(!chfPluginRegister("tagged", &myPif, taggedOpts), + "register plugin tagged"); testOk(!chfPluginRegister("strict", &myPif, strictOpts), "register plugin strict"); testOk(!chfPluginRegister("noconv", &myPif, noconvOpts), @@ -543,13 +561,84 @@ MAIN(chfPluginTest) testOk(!chfPluginRegister("post", &postPif, sloppyOpts), "register plugin post"); + /* TAGGED parsing: shorthand for integer plus other parameter */ + + testHead("TAGGED parsing: all ok"); + + /* tag i */ + e1 = e_alloc | e_ok; c1 = 0; + testOk(!!(pch = dbChannelCreate( + "x.{\"tagged\":{\"i\":1}}")), "create channel for tagged parsing: i"); + testOk(checkValues(puser1, 1, 1, 1, 1.234e5, "hello", 0, 4), + "guards intact, values correct"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + e1 = e_close | e_free; c1 = 0; + if (pch) dbChannelDelete(pch); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + /* tag f */ + e1 = e_alloc | e_ok; c1 = 0; + testOk(!!(pch = dbChannelCreate( + "x.{\"tagged\":{\"f\":false}}")), "create channel for tagged parsing: f"); + testOk(checkValues(puser1, 2, 12, 0, 1.234e5, "hello", 0, 4), + "guards intact, values correct"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + e1 = e_close | e_free; c1 = 0; + if (pch) dbChannelDelete(pch); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + /* tag d */ + e1 = e_alloc | e_ok; c1 = 0; + testOk(!!(pch = dbChannelCreate( + "x.{\"tagged\":{\"d\":1.2e15}}")), "create channel for tagged parsing: d"); + testOk(checkValues(puser1, 3, 12, 1, 1.2e15, "hello", 0, 4), + "guards intact, values correct"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + e1 = e_close | e_free; c1 = 0; + if (pch) dbChannelDelete(pch); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + /* tag s */ + e1 = e_alloc | e_ok; c1 = 0; + testOk(!!(pch = dbChannelCreate( + "x.{\"tagged\":{\"s\":\"bar\"}}")), "create channel for tagged parsing: s"); + testOk(checkValues(puser1, 4, 12, 1, 1.234e5, "bar", 0, 4), + "guards intact, values correct"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + e1 = e_close | e_free; c1 = 0; + if (pch) dbChannelDelete(pch); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + /* tag c */ + e1 = e_alloc | e_ok; c1 = 0; + testOk(!!(pch = dbChannelCreate( + "x.{\"tagged\":{\"c\":\"R\"}}")), "create channel for tagged parsing: c"); + testOk(checkValues(puser1, 5, 12, 1, 1.234e5, "hello", 0, 1), + "guards intact, values correct"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + e1 = e_close | e_free; c1 = 0; + if (pch) dbChannelDelete(pch); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + /* STRICT parsing: mandatory, no conversion */ /* All perfect */ testHead("STRICT parsing: all ok"); e1 = e_alloc | e_ok; c1 = 0; - testOk(!!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "strict parsing: JSON correct"); - testOk(checkValues(puser1, 1, 0, 1.2e15, "bar", 0, 1), + testOk(!!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), + "create channel for strict parsing: JSON correct"); + testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "bar", 0, 1), "guards intact, values correct"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); @@ -564,35 +653,35 @@ MAIN(chfPluginTest) e1 = e_alloc | e_error | e_free; c1 = 0; testOk(!(pch = dbChannelCreate( "x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), - "strict parsing: c missing"); + "create channel for strict parsing: c missing"); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_alloc | e_error | e_free; c1 = 0; testOk(!(pch = dbChannelCreate( "x.{\"strict\":{\"f\":false,\"i\":1,\"d\":1.2e15,\"c\":\"R\"}}")), - "strict parsing: s missing"); + "create channel for strict parsing: s missing"); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_alloc | e_error | e_free; c1 = 0; testOk(!(pch = dbChannelCreate( "x.{\"strict\":{\"i\":1,\"c\":\"R\",\"f\":false,\"s\":\"bar\"}}")), - "strict parsing: d missing"); + "create channel for strict parsing: d missing"); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_alloc | e_error | e_free; c1 = 0; testOk(!(pch = dbChannelCreate( "x.{\"strict\":{\"d\":1.2e15,\"c\":\"R\",\"i\":1,\"s\":\"bar\"}}")), - "strict parsing: f missing"); + "create channel for strict parsing: f missing"); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); e1 = e_alloc | e_error | e_free; c1 = 0; testOk(!(pch = dbChannelCreate( "x.{\"strict\":{\"c\":\"R\",\"s\":\"bar\",\"f\":false,\"d\":1.2e15}}")), - "strict parsing: i missing"); + "create channel for strict parsing: i missing"); testOk(!puser1, "user part cleaned up"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); @@ -604,8 +693,8 @@ MAIN(chfPluginTest) e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate( "x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), - "noconv parsing: c missing"); - testOk(checkValues(puser1, 1, 0, 1.2e15, "bar", 0, 4), + "create channel for noconv parsing: c missing"); + testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "bar", 0, 4), "guards intact, values correct"); if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); @@ -618,29 +707,29 @@ MAIN(chfPluginTest) e1 = e_any; testOk(!!(pch = dbChannelCreate( "x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"c\":\"R\"}}")), - "noconv parsing: s missing"); - testOk(checkValues(puser1, 1, 0, 1.2e15, "hello", 0, 1), + "create channel for noconv parsing: s missing"); + testOk(checkValues(puser1, 99, 1, 0, 1.2e15, "hello", 0, 1), "guards intact, values correct"); if (pch) dbChannelDelete(pch); testOk(!!(pch = dbChannelCreate( "x.{\"noconv\":{\"i\":1,\"f\":false,\"s\":\"bar\",\"c\":\"R\"}}")), - "noconv parsing: d missing"); - testOk(checkValues(puser1, 1, 0, 1.234e5, "bar", 0, 1), + "create channel for noconv parsing: d missing"); + testOk(checkValues(puser1, 99, 1, 0, 1.234e5, "bar", 0, 1), "guards intact, values correct"); if (pch) dbChannelDelete(pch); testOk(!!(pch = dbChannelCreate( "x.{\"noconv\":{\"i\":1,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), - "noconv parsing: f missing"); - testOk(checkValues(puser1, 1, 1, 1.2e15, "bar", 0, 1), + "create channel for noconv parsing: f missing"); + testOk(checkValues(puser1, 99, 1, 1, 1.2e15, "bar", 0, 1), "guards intact, values correct"); if (pch) dbChannelDelete(pch); testOk(!!(pch = dbChannelCreate( "x.{\"noconv\":{\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), - "noconv parsing: i missing"); - testOk(checkValues(puser1, 12, 0, 1.2e15, "bar", 0, 1), + "create channel for noconv parsing: i missing"); + testOk(checkValues(puser1, 99, 12, 0, 1.2e15, "bar", 0, 1), "guards intact, values correct"); if (pch) dbChannelDelete(pch); @@ -648,7 +737,7 @@ MAIN(chfPluginTest) #define WRONGTYPETEST(Var, Val, Typ) \ e1 = e_alloc | e_error | e_free; c1 = 0; \ testOk(!(pch = dbChannelCreate("x.{\"noconv\":{\""#Var"\":"#Val"}}")), \ - "noconv parsing: wrong type "#Typ" for "#Var); \ + "create channel for noconv parsing: wrong type "#Typ" for "#Var); \ testOk(!puser1, "user part cleaned up"); \ if (!testOk(c1 == e1, "all expected calls happened")) \ testDiag("expected %#x - called %#x", e1, c1); @@ -677,8 +766,8 @@ MAIN(chfPluginTest) e1 = e_alloc | e_ok; c1 = 0; \ testDiag("Calling dbChannelCreate x.{\"sloppy\":{\""#Var"\":"#Val"}}"); \ testOk(!!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), \ - "sloppy parsing: "#Typ" (good) for "#Var); \ - testOk(checkValues(puser1, Ival, Fval, Dval, Sval1, Sval2, Cval), \ + "create channel for sloppy parsing: "#Typ" (good) for "#Var); \ + testOk(checkValues(puser1, 99, Ival, Fval, Dval, Sval1, Sval2, Cval), \ "guards intact, values correct"); \ if (!testOk(c1 == e1, "create channel: all expected calls happened")) \ testDiag("expected %#x - called %#x", e1, c1); \ @@ -692,7 +781,7 @@ MAIN(chfPluginTest) e1 = e_alloc | e_error | e_free; c1 = 0; \ testDiag("Calling dbChannelCreate x.{\"sloppy\":{\""#Var"\":"#Val"}}"); \ testOk(!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), \ - "sloppy parsing: "#Typ" (bad) for "#Var); \ + "create channel for sloppy parsing: "#Typ" (bad) for "#Var); \ testOk(!puser1, "user part cleaned up"); \ if (!testOk(c1 == e1, "create channel: all expected calls happened")) \ testDiag("expected %#x - called %#x", e1, c1); From 648bf6a808253b5c7fef857bc4b656194df053e2 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Thu, 28 Aug 2014 11:24:48 -0700 Subject: [PATCH 3/6] std/filters: add tagged configuration options to dbnd and sync plugins --- src/std/filters/dbnd.c | 11 +++++++---- src/std/filters/sync.c | 16 +++++++++++----- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/std/filters/dbnd.c b/src/std/filters/dbnd.c index 36c852b2b..0278a9a33 100644 --- a/src/std/filters/dbnd.c +++ b/src/std/filters/dbnd.c @@ -1,13 +1,14 @@ /*************************************************************************\ * Copyright (c) 2010 Brookhaven National Laboratory. * Copyright (c) 2010 Helmholtz-Zentrum Berlin -* fuer Materialien und Energie GmbH. +* für Materialien und Energie GmbH. +* Copyright (c) 2014 ITER Organization. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* - * Author: Ralph Lange + * Author: Ralph Lange */ #include @@ -34,8 +35,10 @@ chfPluginEnumType modeEnum[] = { {"abs", 0}, {"rel", 1}, {NULL,0} }; static const chfPluginArgDef opts[] = { - chfDouble (myStruct, cval, "d", 0, 1), - chfEnum (myStruct, mode, "m", 0, 1, modeEnum), + chfDouble (myStruct, cval, "d", 0, 1), + chfEnum (myStruct, mode, "m", 0, 1, modeEnum), + chfTagDouble (myStruct, cval, "abs", mode, 0, 0, 1), + chfTagDouble (myStruct, cval, "rel", mode, 1, 0, 1), chfPluginArgEnd }; diff --git a/src/std/filters/sync.c b/src/std/filters/sync.c index 9dfd47197..ec1bcd409 100644 --- a/src/std/filters/sync.c +++ b/src/std/filters/sync.c @@ -22,9 +22,9 @@ #define STATE_NAME_LENGTH 20 static const -chfPluginEnumType modeEnum[] = { {"before", 0}, {"first", 1}, - {"last", 2}, {"after", 3}, - {"while", 4}, {"unless", 5}, +chfPluginEnumType modeEnum[] = { {"before", 0}, {"first", 1}, + {"last", 2}, {"after", 3}, + {"while", 4}, {"unless", 5}, {NULL,0} }; typedef enum syncMode { syncModeBefore=0, @@ -47,8 +47,14 @@ static void *myStructFreeList; static const chfPluginArgDef opts[] = { - chfEnum (myStruct, mode, "m", 1, 1, modeEnum), - chfString (myStruct, state, "s", 1, 0), + chfEnum (myStruct, mode, "m", 1, 1, modeEnum), + chfString (myStruct, state, "s", 1, 0), + chfTagString (myStruct, state, "before", mode, 0, 1, 0), + chfTagString (myStruct, state, "first", mode, 1, 1, 0), + chfTagString (myStruct, state, "last", mode, 2, 1, 0), + chfTagString (myStruct, state, "after", mode, 3, 1, 0), + chfTagString (myStruct, state, "while", mode, 4, 1, 0), + chfTagString (myStruct, state, "unless", mode, 5, 1, 0), chfPluginArgEnd }; From 1c317adbdafcefb9831e731de85750a13388a83e Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 30 Sep 2014 11:48:41 +0200 Subject: [PATCH 4/6] ioc/db: add tests for strict tagged plugin options, fix 'required' algorithm --- src/ioc/db/chfPlugin.c | 19 +++--- src/ioc/db/test/chfPluginTest.c | 103 +++++++++++++++++++++++++++----- 2 files changed, 97 insertions(+), 25 deletions(-) diff --git a/src/ioc/db/chfPlugin.c b/src/ioc/db/chfPlugin.c index bcc6a2efe..9770f5d00 100644 --- a/src/ioc/db/chfPlugin.c +++ b/src/ioc/db/chfPlugin.c @@ -462,7 +462,7 @@ parse_map_key(chFilter *filter, const char *key, size_t stringLen) int j; f->nextParam = -1; - for(cur = opts, i = 0; cur && cur->name; cur++, i++) { + for (cur = opts, i = 0; cur && cur->name; cur++, i++) { if (strncmp(key, cur->name, stringLen) == 0) { f->nextParam = i; break; @@ -472,18 +472,19 @@ parse_map_key(chFilter *filter, const char *key, size_t stringLen) return parse_stop; } - f->found[i/32] |= 1<<(i%32); - /* For tagged parameters: - * set tag to this choice, and mark all other choices as found */ if (opts[i].tagged) { tag = (int*) ((char*) f->puser + opts[i].tagOffset); *tag = opts[i].choice; - for (cur = opts, j = 0; cur && cur->name; cur++, j++) { - if (cur->tagged && cur->tagOffset == opts[i].tagOffset) { - f->found[j/32] |= 1<<(j%32); - } - } } + + f->found[i/32] |= 1<<(i%32); + /* Mark tag and all other options pointing to the same data as found */ + for (cur = opts, j = 0; cur && cur->name; cur++, j++) { + if ((opts[i].tagged && cur->dataOffset == opts[i].tagOffset) + || cur->dataOffset == opts[i].dataOffset) + f->found[j/32] |= 1<<(j%32); + } + return parse_continue; } diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index 1e2dcace9..f5bd37a52 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -60,7 +60,7 @@ typedef struct myStruct { int sent5; char str[20]; int sent6; - char tval; + epicsUInt32 tval; int sent7; char c; char c1[2]; @@ -72,12 +72,25 @@ static const chfPluginEnumType colorEnum[] = { {"R", 1}, {"G", 2}, {"B", 4}, {NULL,0} }; static const -chfPluginArgDef taggedOpts[] = { - chfTagInt32 (myStruct, ival, "i" , tval, 1, 0, 0), - chfTagBoolean(myStruct, flag, "f" , tval, 2, 0, 0), - chfTagDouble (myStruct, dval, "d" , tval, 3, 0, 0), - chfTagString (myStruct, str, "s" , tval, 4, 0, 0), - chfTagEnum (myStruct, enumval, "c" , tval, 5, 0, 0, colorEnum), +chfPluginArgDef sloppyTaggedOpts[] = { + chfInt32 (myStruct, tval, "t" , 0, 0), + chfTagInt32 (myStruct, ival, "I" , tval, 1, 0, 0), + chfTagBoolean(myStruct, flag, "F" , tval, 2, 0, 0), + chfTagDouble (myStruct, dval, "D" , tval, 3, 0, 0), + chfTagString (myStruct, str, "S" , tval, 4, 0, 0), + chfTagEnum (myStruct, enumval, "C" , tval, 5, 0, 0, colorEnum), + chfPluginArgEnd +}; + +static const +chfPluginArgDef strictTaggedOpts[] = { + chfInt32 (myStruct, tval, "t" , 1, 0), + chfBoolean (myStruct, flag, "f" , 1, 0), + chfTagInt32 (myStruct, ival, "I" , tval, 1, 0, 0), + chfTagBoolean(myStruct, flag, "F" , tval, 2, 0, 0), + chfTagDouble (myStruct, dval, "D" , tval, 3, 1, 0), + chfTagDouble (myStruct, dval, "D2", tval, 4, 1, 0), + chfTagEnum (myStruct, enumval, "C" , tval, 5, 0, 0, colorEnum), chfPluginArgEnd }; @@ -508,7 +521,7 @@ MAIN(chfPluginTest) _set_output_format(_TWO_DIGIT_EXPONENT); #endif - testPlan(1397); + testPlan(1428); dbChannelInit(); db_init_events(); @@ -548,8 +561,10 @@ MAIN(chfPluginTest) eltc(1); testHead("Register plugins"); - testOk(!chfPluginRegister("tagged", &myPif, taggedOpts), - "register plugin tagged"); + testOk(!chfPluginRegister("sloppy-tagged", &myPif, sloppyTaggedOpts), + "register plugin sloppy-tagged"); + testOk(!chfPluginRegister("strict-tagged", &myPif, strictTaggedOpts), + "register plugin strict-tagged"); testOk(!chfPluginRegister("strict", &myPif, strictOpts), "register plugin strict"); testOk(!chfPluginRegister("noconv", &myPif, noconvOpts), @@ -563,12 +578,64 @@ MAIN(chfPluginTest) /* TAGGED parsing: shorthand for integer plus other parameter */ - testHead("TAGGED parsing: all ok"); + /* STRICT TAGGED parsing: mandatory, no conversions */ + + /* All perfect */ + testHead("STRICT TAGGED parsing: all ok"); + /* tag D (t and d) and f */ + e1 = e_alloc | e_ok; c1 = 0; + testOk(!!(pch = dbChannelCreate( + "x.{\"strict-tagged\":{\"D\":1.2e15,\"f\":false}}")), + "create channel for strict-tagged parsing: D (t and d) and f"); + testOk(checkValues(puser1, 3, 12, 0, 1.2e15, "hello", 0, 4), + "guards intact, values correct"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + e1 = e_close | e_free; c1 = 0; + if (pch) dbChannelDelete(pch); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + /* tag D2 (t and d) and f */ + e1 = e_alloc | e_ok; c1 = 0; + testOk(!!(pch = dbChannelCreate( + "x.{\"strict-tagged\":{\"D2\":1.2e15,\"f\":false}}")), + "create channel for strict-tagged parsing: D2 (t and d) and f"); + testOk(checkValues(puser1, 4, 12, 0, 1.2e15, "hello", 0, 4), + "guards intact, values correct"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + e1 = e_close | e_free; c1 = 0; + if (pch) dbChannelDelete(pch); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + /* tag F: (t and f), d missing) */ + e1 = e_alloc | e_error | e_free; c1 = 0; + testOk(!(pch = dbChannelCreate( + "x.{\"strict-tagged\":{\"F\":false}}")), + "create channel for strict-tagged parsing: F (t and f), d missing"); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + /* tag I: (t and i) and f, d missing) */ + e1 = e_alloc | e_error | e_free; c1 = 0; + testOk(!(pch = dbChannelCreate( + "x.{\"strict-tagged\":{\"I\":1,\"f\":false}}")), + "create channel for strict-tagged parsing: I (t and i) and f, d missing"); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); + + /* SLOPPY TAGGED parsing: optional, all others have defaults */ + + testHead("SLOPPY TAGGED parsing: all ok"); /* tag i */ e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate( - "x.{\"tagged\":{\"i\":1}}")), "create channel for tagged parsing: i"); + "x.{\"sloppy-tagged\":{\"I\":1}}")), + "create channel for sloppy-tagged parsing: I"); testOk(checkValues(puser1, 1, 1, 1, 1.234e5, "hello", 0, 4), "guards intact, values correct"); if (!testOk(c1 == e1, "all expected calls happened")) @@ -581,7 +648,8 @@ MAIN(chfPluginTest) /* tag f */ e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate( - "x.{\"tagged\":{\"f\":false}}")), "create channel for tagged parsing: f"); + "x.{\"sloppy-tagged\":{\"F\":false}}")), + "create channel for sloppy-tagged parsing: F"); testOk(checkValues(puser1, 2, 12, 0, 1.234e5, "hello", 0, 4), "guards intact, values correct"); if (!testOk(c1 == e1, "all expected calls happened")) @@ -594,7 +662,8 @@ MAIN(chfPluginTest) /* tag d */ e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate( - "x.{\"tagged\":{\"d\":1.2e15}}")), "create channel for tagged parsing: d"); + "x.{\"sloppy-tagged\":{\"D\":1.2e15}}")), + "create channel for sloppy-tagged parsing: D"); testOk(checkValues(puser1, 3, 12, 1, 1.2e15, "hello", 0, 4), "guards intact, values correct"); if (!testOk(c1 == e1, "all expected calls happened")) @@ -607,7 +676,8 @@ MAIN(chfPluginTest) /* tag s */ e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate( - "x.{\"tagged\":{\"s\":\"bar\"}}")), "create channel for tagged parsing: s"); + "x.{\"sloppy-tagged\":{\"S\":\"bar\"}}")), + "create channel for sloppy-tagged parsing: S"); testOk(checkValues(puser1, 4, 12, 1, 1.234e5, "bar", 0, 4), "guards intact, values correct"); if (!testOk(c1 == e1, "all expected calls happened")) @@ -620,7 +690,8 @@ MAIN(chfPluginTest) /* tag c */ e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate( - "x.{\"tagged\":{\"c\":\"R\"}}")), "create channel for tagged parsing: c"); + "x.{\"sloppy-tagged\":{\"C\":\"R\"}}")), + "create channel for sloppy-tagged parsing: C"); testOk(checkValues(puser1, 5, 12, 1, 1.234e5, "hello", 0, 1), "guards intact, values correct"); if (!testOk(c1 == e1, "all expected calls happened")) From 6b71e5bfe584b641248600d8899d79655ff8ee62 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 30 Sep 2014 16:19:13 +0200 Subject: [PATCH 5/6] std/filters: add minimal error handling when using freeListCalloc() in arr plugin --- src/std/filters/arr.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/std/filters/arr.c b/src/std/filters/arr.c index f2f5f82f1..b872cd623 100644 --- a/src/std/filters/arr.c +++ b/src/std/filters/arr.c @@ -43,6 +43,8 @@ chfPluginArgDef opts[] = { static void * allocPvt(void) { myStruct *my = (myStruct*) freeListCalloc(myStructFreeList); + if (!my) return NULL; + my->incr = 1; my->end = -1; return (void *) my; @@ -120,12 +122,13 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { pfl->no_elements = nTarget; if (nTarget) { void *pdst = freeListCalloc(my->arrayFreeList); - - pfl->u.r.dtor = freeArray; - pfl->u.r.pvt = my->arrayFreeList; - offset = (offset + start) % chan->addr.no_elements; - dbExtractArrayFromRec(&chan->addr, pdst, nTarget, nSource, offset, my->incr); - pfl->u.r.field = pdst; + if (pdst) { + pfl->u.r.dtor = freeArray; + pfl->u.r.pvt = my->arrayFreeList; + offset = (offset + start) % chan->addr.no_elements; + dbExtractArrayFromRec(&chan->addr, pdst, nTarget, nSource, offset, my->incr); + pfl->u.r.field = pdst; + } } dbScanUnlock(prec); chan->addr.pfield = pfieldsave; @@ -141,6 +144,7 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { pfl->no_elements = nTarget; if (nTarget) { /* Copy the data out */ pdst = freeListCalloc(my->arrayFreeList); + if (!pdst) return pfl; offset = start; dbExtractArrayFromBuf(psrc, pdst, pfl->field_size, pfl->field_type, nTarget, nSource, offset, my->incr); } From 7ecbe87047a078c1dcc099adbf170d75669b9c97 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 30 Sep 2014 16:20:16 +0200 Subject: [PATCH 6/6] ioc/db: add test for allocPvt() returning NULL to chfPluginTest --- src/ioc/db/chfPlugin.c | 4 +++- src/ioc/db/test/chfPluginTest.c | 39 ++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/ioc/db/chfPlugin.c b/src/ioc/db/chfPlugin.c index 9770f5d00..00e565e71 100644 --- a/src/ioc/db/chfPlugin.c +++ b/src/ioc/db/chfPlugin.c @@ -334,8 +334,10 @@ static parse_result parse_start(chFilter *filter) /* Call the plugin to allocate its structure, it returns NULL on error */ if (p->pif->allocPvt) { - if ((f->puser = p->pif->allocPvt()) == NULL) + if ((f->puser = p->pif->allocPvt()) == NULL) { + errlogPrintf("chfConfigParseStart: plugin pvt alloc failed\n"); goto errplugin; + } } filter->puser = (void*) f; diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index f5bd37a52..7b74aa770 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -189,6 +189,15 @@ static void * allocPvt(void) return my; } +static void * allocPvtFail(void) +{ + if (!puser1) { + testOk(e1 & e_alloc, "allocPvt (1) called"); + c1 |= e_alloc; + } + return NULL; +} + static void freePvt(void *user) { if (user == puser1) { @@ -473,6 +482,20 @@ static chfPluginIf postPif = { channel_close }; +static chfPluginIf allocFailPif = { + allocPvtFail, + freePvt, + + parse_error, + parse_ok, + + channel_open, + channelRegisterPre, + channelRegisterPost, + channel_report, + channel_close +}; + static int checkValues(myStruct *my, char t, epicsUInt32 i, int f, double d, char *s1, char *s2, int c) { int ret = 1; @@ -521,7 +544,7 @@ MAIN(chfPluginTest) _set_output_format(_TWO_DIGIT_EXPONENT); #endif - testPlan(1428); + testPlan(1429); dbChannelInit(); db_init_events(); @@ -575,6 +598,20 @@ MAIN(chfPluginTest) "register plugin pre"); testOk(!chfPluginRegister("post", &postPif, sloppyOpts), "register plugin post"); + testOk(!chfPluginRegister("alloc-fail", &allocFailPif, sloppyOpts), + "register plugin alloc-fail"); + + /* Check failing allocation of plugin private structures */ + + testHead("Failing allocation of plugin private structures"); + /* tag i */ + e1 = e_alloc; c1 = 0; + testOk(!(pch = dbChannelCreate( + "x.{\"alloc-fail\":{\"i\":1}}")), + "create channel for alloc-fail: allocPvt returning NULL"); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); /* TAGGED parsing: shorthand for integer plus other parameter */