From 1d056c6fe42bc645f50c823d8b5861faa2f1b554 Mon Sep 17 00:00:00 2001 From: Doug Murray Date: Tue, 6 Dec 2022 12:47:50 -0800 Subject: [PATCH 01/19] Add support for CA tools timeout from environment variable EPICS_CLI_TIMEOUT --- modules/ca/src/tools/caget.c | 6 ++++-- modules/ca/src/tools/cainfo.c | 11 +++++++++-- modules/ca/src/tools/camonitor.c | 11 +++++++++-- modules/ca/src/tools/caput.c | 13 ++++++++++--- modules/ca/src/tools/tool_lib.c | 22 +++++++++++++++++++++- modules/ca/src/tools/tool_lib.h | 1 + modules/libcom/src/misc/epicsStdlib.h | 4 ++-- 7 files changed, 56 insertions(+), 12 deletions(-) diff --git a/modules/ca/src/tools/caget.c b/modules/ca/src/tools/caget.c index e60f61922..d24fcd066 100644 --- a/modules/ca/src/tools/caget.c +++ b/modules/ca/src/tools/caget.c @@ -392,6 +392,8 @@ int main (int argc, char *argv[]) LINE_BUFFER(stdout); /* Configure stdout buffering */ + use_ca_timeout_env ( &caTimeout); + while ((opt = getopt(argc, argv, ":taicnhsSVe:f:g:l:#:d:0:w:p:F:")) != -1) { switch (opt) { case 'h': /* Print usage */ @@ -437,8 +439,8 @@ int main (int argc, char *argv[]) if(epicsScanDouble(optarg, &caTimeout) != 1) { fprintf(stderr, "'%s' is not a valid timeout value " - "- ignored. ('caget -h' for help.)\n", optarg); - caTimeout = DEFAULT_TIMEOUT; + "- ignored, using '%.1f'. ('caget -h' for help.)\n", + optarg, caTimeout); } break; case '#': /* Array count */ diff --git a/modules/ca/src/tools/cainfo.c b/modules/ca/src/tools/cainfo.c index 7fa4dfb45..1bedcf52e 100644 --- a/modules/ca/src/tools/cainfo.c +++ b/modules/ca/src/tools/cainfo.c @@ -141,6 +141,8 @@ int main (int argc, char *argv[]) LINE_BUFFER(stdout); /* Configure stdout buffering */ + use_ca_timeout_env ( &caTimeout); + while ((opt = getopt(argc, argv, ":nhVw:s:p:")) != -1) { switch (opt) { case 'h': /* Print usage */ @@ -150,11 +152,16 @@ int main (int argc, char *argv[]) printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() ); return 0; case 'w': /* Set CA timeout value */ + /* + * epicsScanDouble is a macro defined as epicsParseDouble, + * (found in modules/libcom/src/misc) which will only + * change caTimeout here if it finds an acceptable value. + */ if(epicsScanDouble(optarg, &caTimeout) != 1) { fprintf(stderr, "'%s' is not a valid timeout value " - "- ignored. ('cainfo -h' for help.)\n", optarg); - caTimeout = DEFAULT_TIMEOUT; + "- ignored, using '%.1f'. ('cainfo -h' for help.)\n", + optarg, caTimeout); } break; case 's': /* ca_client_status interest level */ diff --git a/modules/ca/src/tools/camonitor.c b/modules/ca/src/tools/camonitor.c index b272bc17f..c2bd10a74 100644 --- a/modules/ca/src/tools/camonitor.c +++ b/modules/ca/src/tools/camonitor.c @@ -219,6 +219,8 @@ int main (int argc, char *argv[]) LINE_BUFFER(stdout); /* Configure stdout buffering */ + use_ca_timeout_env ( &caTimeout); + while ((opt = getopt(argc, argv, ":nhVm:sSe:f:g:l:#:0:w:t:p:F:")) != -1) { switch (opt) { case 'h': /* Print usage */ @@ -251,11 +253,16 @@ int main (int argc, char *argv[]) } break; case 'w': /* Set CA timeout value */ + /* + * epicsScanDouble is a macro defined as epicsParseDouble, + * (found in modules/libcom/src/misc) which will only + * change caTimeout here if it finds an acceptable value. + */ if(epicsScanDouble(optarg, &caTimeout) != 1) { fprintf(stderr, "'%s' is not a valid timeout value " - "- ignored. ('camonitor -h' for help.)\n", optarg); - caTimeout = DEFAULT_TIMEOUT; + "- ignored, using '%.1f'. ('camonitor -h' for help.)\n", + optarg, caTimeout); } break; case '#': /* Array count */ diff --git a/modules/ca/src/tools/caput.c b/modules/ca/src/tools/caput.c index 05247643a..3f368469c 100644 --- a/modules/ca/src/tools/caput.c +++ b/modules/ca/src/tools/caput.c @@ -284,6 +284,8 @@ int main (int argc, char *argv[]) LINE_BUFFER(stdout); /* Configure stdout buffering */ putenv("POSIXLY_CORRECT="); /* Behave correct on GNU getopt systems */ + use_ca_timeout_env ( &caTimeout); + while ((opt = getopt(argc, argv, ":cnlhatsVS#:w:p:F:")) != -1) { switch (opt) { case 'h': /* Print usage */ @@ -318,11 +320,16 @@ int main (int argc, char *argv[]) request = callback; break; case 'w': /* Set CA timeout value */ + /* + * epicsScanDouble is a macro defined as epicsParseDouble, + * (found in modules/libcom/src/misc) which will only + * change caTimeout here if it finds an acceptable value. + */ if(epicsScanDouble(optarg, &caTimeout) != 1) { fprintf(stderr, "'%s' is not a valid timeout value " - "- ignored. ('caput -h' for help.)\n", optarg); - caTimeout = DEFAULT_TIMEOUT; + "- ignored, using '%.1f'. ('caput -h' for help.)\n", + optarg, caTimeout); } break; case '#': /* Array count */ @@ -337,7 +344,7 @@ int main (int argc, char *argv[]) if (sscanf(optarg,"%u", &caPriority) != 1) { fprintf(stderr, "'%s' is not a valid CA priority " - "- ignored. ('caget -h' for help.)\n", optarg); + "- ignored. ('caput -h' for help.)\n", optarg); caPriority = DEFAULT_CA_PRIORITY; } if (caPriority > CA_PRIORITY_MAX) caPriority = CA_PRIORITY_MAX; diff --git a/modules/ca/src/tools/tool_lib.c b/modules/ca/src/tools/tool_lib.c index 6d4d971b6..58bd3d047 100644 --- a/modules/ca/src/tools/tool_lib.c +++ b/modules/ca/src/tools/tool_lib.c @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -53,7 +54,7 @@ char fieldSeparator = ' '; /* OFS default is whitespace */ int enumAsNr = 0; /* used for -n option - get DBF_ENUM as number */ int charArrAsStr = 0; /* used for -S option - treat char array as (long) string */ -double caTimeout = 1.0; /* wait time default (see -w option) */ +double caTimeout = DEFAULT_TIMEOUT; /* wait time default (see -w option) */ capri caPriority = DEFAULT_CA_PRIORITY; /* CA Priority */ #define TIMETEXTLEN 28 /* Length of timestamp text buffer */ @@ -639,3 +640,22 @@ int connect_pvs (pv* pvs, int nPvs) } return returncode; } + + +/* Set the timeout to EPICS_CLI_TIMEOUT */ +void use_ca_timeout_env ( double* timeout) +{ + const char* tmoStr; /* contents of environment var */ + + if ((tmoStr = getenv("EPICS_CLI_TIMEOUT")) != NULL && timeout != NULL) + { + if(epicsScanDouble(tmoStr, timeout) != 1) + { + fprintf(stderr, "'%s' is not a valid timeout value " + "(from 'EPICS_CLI_TIMEOUT' in the environment) - " + "ignored. (use '-h' for help.)\n", tmoStr); + *timeout = DEFAULT_TIMEOUT; + } + + } +} diff --git a/modules/ca/src/tools/tool_lib.h b/modules/ca/src/tools/tool_lib.h index 0933fc8c2..ed3d8b0cd 100644 --- a/modules/ca/src/tools/tool_lib.h +++ b/modules/ca/src/tools/tool_lib.h @@ -100,6 +100,7 @@ extern char *dbr2str (const void *value, unsigned type); extern void print_time_val_sts (pv *pv, unsigned long reqElems); extern int create_pvs (pv *pvs, int nPvs, caCh *pCB ); extern int connect_pvs (pv *pvs, int nPvs ); +extern void use_ca_timeout_env (double* timeout); /* * no additions below this endif diff --git a/modules/libcom/src/misc/epicsStdlib.h b/modules/libcom/src/misc/epicsStdlib.h index 660ad9abc..dcb9fb584 100644 --- a/modules/libcom/src/misc/epicsStdlib.h +++ b/modules/libcom/src/misc/epicsStdlib.h @@ -87,8 +87,8 @@ LIBCOM_API int * \brief Convert a string to a double type * * \param str Pointer to a constant character array - * \param to Pointer to the specified type (this will be set during the conversion) - * \param units Pointer to a char * (this will be set with the units string) + * \param to Pointer to the specified type (this will be set only upon successful conversion) + * \param units Pointer to a char * (this will be set with the units string only upon successful conversion) * \return Status code (0=OK, see macro definitions for possible errors) */ LIBCOM_API int From 3a2d225682d79f79c84852dbcb98d54cb250d62f Mon Sep 17 00:00:00 2001 From: Sebastian Marsching Date: Sat, 22 Jul 2023 08:27:32 -0700 Subject: [PATCH 02/19] Detect error in fprintf and return (fixes #309). fprintf returns a negative value in order to signal an error. We have to detect this situation in epicsStrPrintEscaped and return a negative when fprintf returns a negative value in order to give the calling code a chance to detect this situation. The old implementation (of simply accumulating the return values of fprintf) was wrong anyway, because it would not only lead to an error in fprintf to be lost but would also cause the returned number to be too small (not representing the actual number of bytes written) in such a case. The only case where the old implementation would work correctly was when all calls to fprintf succeeded or all these calls failed. --- modules/libcom/src/misc/epicsString.c | 36 ++++++++++++++++++--------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/modules/libcom/src/misc/epicsString.c b/modules/libcom/src/misc/epicsString.c index ca61d9b8c..bcd10a504 100644 --- a/modules/libcom/src/misc/epicsString.c +++ b/modules/libcom/src/misc/epicsString.c @@ -231,27 +231,39 @@ int epicsStrPrintEscaped(FILE *fp, const char *s, size_t len) { int nout = 0; + if (fp == NULL) + return -1; + + if (s == NULL || strlen(s) == 0 || len == 0) + return 0; // No work to do + while (len--) { char c = *s++; + int rc = 0; switch (c) { - case '\a': nout += fprintf(fp, "\\a"); break; - case '\b': nout += fprintf(fp, "\\b"); break; - case '\f': nout += fprintf(fp, "\\f"); break; - case '\n': nout += fprintf(fp, "\\n"); break; - case '\r': nout += fprintf(fp, "\\r"); break; - case '\t': nout += fprintf(fp, "\\t"); break; - case '\v': nout += fprintf(fp, "\\v"); break; - case '\\': nout += fprintf(fp, "\\\\"); break; - case '\'': nout += fprintf(fp, "\\'"); break; - case '\"': nout += fprintf(fp, "\\\""); break; + case '\a': rc = fprintf(fp, "\\a"); break; + case '\b': rc = fprintf(fp, "\\b"); break; + case '\f': rc = fprintf(fp, "\\f"); break; + case '\n': rc = fprintf(fp, "\\n"); break; + case '\r': rc = fprintf(fp, "\\r"); break; + case '\t': rc = fprintf(fp, "\\t"); break; + case '\v': rc = fprintf(fp, "\\v"); break; + case '\\': rc = fprintf(fp, "\\\\"); break; + case '\'': rc = fprintf(fp, "\\'"); break; + case '\"': rc = fprintf(fp, "\\\""); break; default: if (isprint(0xff & (int)c)) - nout += fprintf(fp, "%c", c); + rc = fprintf(fp, "%c", c); else - nout += fprintf(fp, "\\x%02x", (unsigned char)c); + rc = fprintf(fp, "\\x%02x", (unsigned char)c); break; } + if (rc < 0) { + return rc; + } else { + nout += rc; + } } return nout; } From f488765631998c9d0b27a14c13aa412ec81a5fe9 Mon Sep 17 00:00:00 2001 From: AlexWells Date: Sat, 22 Jul 2023 08:28:25 -0700 Subject: [PATCH 03/19] Add tests for PR#310 Also add missing NULL/empty checks --- modules/libcom/test/epicsStringTest.c | 39 ++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/modules/libcom/test/epicsStringTest.c b/modules/libcom/test/epicsStringTest.c index a1c2874fb..b5f744b37 100644 --- a/modules/libcom/test/epicsStringTest.c +++ b/modules/libcom/test/epicsStringTest.c @@ -195,6 +195,42 @@ void testStrTok(void) testTok(NULL, " \t", "bbb ", "bbb", ""); } +static +void testEpicsStrPrintEscaped(void) +{ + const char *filename = "testEpicsStrPrintEscaped"; + // Avoid printing to stdout by redirecting everything to a file + FILE *testFile = fopen(filename, "a"); + FILE *readOnly = fopen(filename, "r"); + + testDiag("testEpicsStrPrintEscaped()"); + + // Passing cases + testOk1(epicsStrPrintEscaped(testFile, "1234", 4) == 4); + testOk1(epicsStrPrintEscaped(testFile, "\a\b\f\n\r\t\v\\\'\"", 10) == 20); + + // Failing cases + testOk1(epicsStrPrintEscaped(NULL, "1234", 4) == -1); + // On some platforms (specifially certain versions of MinGW-w64), fprintf + // is broken and does not return -1 when the write operation fails. On + // those platforms, epicsStrPrintEscaped cannot detect failure either, so + // testing that it reports failure does not make sense on those platforms. + // For this reason, we only test this behavior of epcisStrPrintEscaped when + // after checking that fprintf behaves correctly. + if (fprintf(readOnly, "test") == -1) { + testOk1(epicsStrPrintEscaped(readOnly, "1234", 4) == -1); + } else { + testSkip(1, "fprintf is broken on this system"); + } + testOk1(epicsStrPrintEscaped(testFile, NULL, 4) == 0); + testOk1(epicsStrPrintEscaped(testFile, "", 4) == 0); + testOk1(epicsStrPrintEscaped(testFile, "1234", 0) == 0); + + fclose(testFile); + fclose(readOnly); + remove(filename); +} + MAIN(epicsStringTest) { const char * const empty = ""; @@ -209,7 +245,7 @@ MAIN(epicsStringTest) char *s; int status; - testPlan(439); + testPlan(446); testChars(); @@ -407,6 +443,7 @@ MAIN(epicsStringTest) testDistance(); testStrTok(); + testEpicsStrPrintEscaped(); return testDone(); } From 500a57738b149cd1124f2d1cee01ed60f637c53b Mon Sep 17 00:00:00 2001 From: Emilio Perez Date: Tue, 7 Mar 2023 11:23:45 +0000 Subject: [PATCH 04/19] Validate target record name when creating an alias This fixes issue #312 by printing an error when a field is specified --- .../database/src/ioc/dbStatic/dbLexRoutines.c | 2 +- modules/database/test/ioc/db/Makefile | 2 ++ modules/database/test/ioc/db/dbStaticTest.c | 20 +++++++++++++++++-- .../test/ioc/db/dbStaticTestAlias1.db | 4 ++++ .../test/ioc/db/dbStaticTestAlias2.db | 4 ++++ 5 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 modules/database/test/ioc/db/dbStaticTestAlias1.db create mode 100644 modules/database/test/ioc/db/dbStaticTestAlias2.db diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c index 0fd250826..85af1f110 100644 --- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c +++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c @@ -1258,7 +1258,7 @@ static void dbAlias(char *name, char *alias) DBENTRY dbEntry; DBENTRY *pdbEntry = &dbEntry; - if(dbRecordNameValidate(alias)) + if(dbRecordNameValidate(alias) || dbRecordNameValidate(name)) return; dbInitEntry(savedPdbbase, pdbEntry); diff --git a/modules/database/test/ioc/db/Makefile b/modules/database/test/ioc/db/Makefile index 11f74f238..ad1d3d9b7 100644 --- a/modules/database/test/ioc/db/Makefile +++ b/modules/database/test/ioc/db/Makefile @@ -181,6 +181,8 @@ dbStaticTest_SRCS += dbStaticTest.c dbStaticTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp testHarness_SRCS += dbStaticTest.c TESTFILES += ../dbStaticTest.db +TESTFILES += ../dbStaticTestAlias1.db +TESTFILES += ../dbStaticTestAlias2.db TESTS += dbStaticTest # This runs all the test programs in a known working order: diff --git a/modules/database/test/ioc/db/dbStaticTest.c b/modules/database/test/ioc/db/dbStaticTest.c index 7cba3cec0..d118c790a 100644 --- a/modules/database/test/ioc/db/dbStaticTest.c +++ b/modules/database/test/ioc/db/dbStaticTest.c @@ -291,6 +291,18 @@ static void testDbVerify(const char *record) dbFinishEntry(&entry); } +static void testWrongAliasRecord(const char *filename) +{ + FILE *fp = NULL; + dbPath(pdbbase,"." OSI_PATH_LIST_SEPARATOR ".."); + dbOpenFile(pdbbase, filename, &fp); + if(!fp) { + testAbort("Unable to read %s", filename); + } + testOk(dbReadDatabaseFP(&pdbbase, fp, NULL, NULL) != 0, + "Wrong alias record in %s is expected to fail", filename); +} + void dbTestIoc_registerRecordDeviceDriver(struct dbBase *); MAIN(dbStaticTest) @@ -298,13 +310,14 @@ MAIN(dbStaticTest) const char *ldir; FILE *fp = NULL; - testPlan(310); + testPlan(312); testdbPrepare(); testdbReadDatabase("dbTestIoc.dbd", NULL, NULL); dbTestIoc_registerRecordDeviceDriver(pdbbase); dbPath(pdbbase,"." OSI_PATH_LIST_SEPARATOR ".."); - if(!(ldir = dbOpenFile(pdbbase, "dbStaticTest.db", &fp))) { + ldir = dbOpenFile(pdbbase, "dbStaticTest.db", &fp); + if(!fp) { testAbort("Unable to read dbStaticTest.db"); } if(dbReadDatabaseFP(&pdbbase, fp, NULL, NULL)) { @@ -312,6 +325,9 @@ MAIN(dbStaticTest) ldir, OSI_PATH_LIST_SEPARATOR); } + testWrongAliasRecord("dbStaticTestAlias1.db"); + testWrongAliasRecord("dbStaticTestAlias2.db"); + testEntry("testrec.VAL"); testEntry("testalias.VAL"); testEntry("testalias2.VAL"); diff --git a/modules/database/test/ioc/db/dbStaticTestAlias1.db b/modules/database/test/ioc/db/dbStaticTestAlias1.db new file mode 100644 index 000000000..20dc4e2f1 --- /dev/null +++ b/modules/database/test/ioc/db/dbStaticTestAlias1.db @@ -0,0 +1,4 @@ +record(x, "testrec2alias") { +} + +alias("testrec2alias.NAME", "testalias4") diff --git a/modules/database/test/ioc/db/dbStaticTestAlias2.db b/modules/database/test/ioc/db/dbStaticTestAlias2.db new file mode 100644 index 000000000..f36b66432 --- /dev/null +++ b/modules/database/test/ioc/db/dbStaticTestAlias2.db @@ -0,0 +1,4 @@ +record(x, "testrec2alias2") { +} + +alias("testrec2alias2", "testalias5.NAME") From 3ee6097ab7c2bad474c1f77a411917912293a7a3 Mon Sep 17 00:00:00 2001 From: Karl Vestin Date: Thu, 9 Mar 2023 11:07:39 +0100 Subject: [PATCH 05/19] Fixes #361 --- modules/database/src/std/rec/printfRecord.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/database/src/std/rec/printfRecord.c b/modules/database/src/std/rec/printfRecord.c index 96bf63d2f..cf5e4d0c9 100644 --- a/modules/database/src/std/rec/printfRecord.c +++ b/modules/database/src/std/rec/printfRecord.c @@ -169,7 +169,7 @@ static void doPrintf(printfRecord *prec) precision = 0; if (ch == '%') { - added = epicsSnprintf(pval, vspace + 1, "%s", format); + added = epicsSnprintf(pval, vspace + 1, format); } else if (linkn++ >= PRINTF_NLINKS) { /* No more LNKn fields */ From 39b6fa26dad299596deb5dd4d07b874f7b0e2208 Mon Sep 17 00:00:00 2001 From: Karl Vestin Date: Tue, 7 Mar 2023 15:27:05 +0100 Subject: [PATCH 06/19] Added unit test for binary output record type --- modules/database/test/std/rec/Makefile | 33 + modules/database/test/std/rec/aiTest.c | 445 ++++++++++++++ modules/database/test/std/rec/aiTest.db | 20 + modules/database/test/std/rec/biTest.c | 122 ++++ modules/database/test/std/rec/biTest.db | 6 + modules/database/test/std/rec/boTest.c | 158 +++++ modules/database/test/std/rec/boTest.db | 6 + .../test/std/rec/epicsRunRecordTests.c | 12 + modules/database/test/std/rec/printfTest.c | 577 ++++++++++++++++++ modules/database/test/std/rec/printfTest.db | 53 ++ 10 files changed, 1432 insertions(+) create mode 100644 modules/database/test/std/rec/aiTest.c create mode 100644 modules/database/test/std/rec/aiTest.db create mode 100644 modules/database/test/std/rec/biTest.c create mode 100644 modules/database/test/std/rec/biTest.db create mode 100644 modules/database/test/std/rec/boTest.c create mode 100644 modules/database/test/std/rec/boTest.db create mode 100644 modules/database/test/std/rec/printfTest.c create mode 100644 modules/database/test/std/rec/printfTest.db diff --git a/modules/database/test/std/rec/Makefile b/modules/database/test/std/rec/Makefile index 816164ebe..a717b769e 100644 --- a/modules/database/test/std/rec/Makefile +++ b/modules/database/test/std/rec/Makefile @@ -23,6 +23,11 @@ PROD_LIBS = dbRecStdTest dbRecStd dbCore ca Com TARGETS += $(COMMON_DIR)/recTestIoc.dbd DBDDEPENDS_FILES += recTestIoc.dbd$(DEP) recTestIoc_DBD = base.dbd +recTestIoc_DBD += bptTypeKdegC.dbd +recTestIoc_DBD += bptTypeKdegF.dbd +recTestIoc_DBD += bptTypeJdegF.dbd +recTestIoc_DBD += bptTypeJdegC.dbd + TESTFILES += $(COMMON_DIR)/recTestIoc.dbd testHarness_SRCS += recTestIoc_registerRecordDeviceDriver.cpp @@ -93,6 +98,34 @@ testHarness_SRCS += longoutTest.c TESTFILES += ../longoutTest.db TESTS += longoutTest +TESTPROD_HOST += boTest +boTest_SRCS += boTest.c +boTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += boTest.c +TESTFILES += ../boTest.db +TESTS += boTest + +TESTPROD_HOST += biTest +biTest_SRCS += biTest.c +biTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += biTest.c +TESTFILES += ../biTest.db +TESTS += biTest + +TESTPROD_HOST += printfTest +printfTest_SRCS += printfTest.c +printfTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += printfTest.c +TESTFILES += ../printfTest.db +TESTS += printfTest + +TESTPROD_HOST += aiTest +aiTest_SRCS += aiTest.c +aiTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp +testHarness_SRCS += aiTest.c +TESTFILES += ../aiTest.db +TESTS += aiTest + TARGETS += $(COMMON_DIR)/asTestIoc.dbd DBDDEPENDS_FILES += asTestIoc.dbd$(DEP) asTestIoc_DBD += base.dbd diff --git a/modules/database/test/std/rec/aiTest.c b/modules/database/test/std/rec/aiTest.c new file mode 100644 index 000000000..887f56582 --- /dev/null +++ b/modules/database/test/std/rec/aiTest.c @@ -0,0 +1,445 @@ +/*************************************************************************\ +* Copyright (c) 2023 Karl Vestin +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "dbUnitTest.h" +#include "testMain.h" +#include "errlog.h" +#include "dbAccess.h" +#include "menuAlarmSevr.h" +#include "menuConvert.h" +#include "epicsMath.h" +#include "menuScan.h" +#include "caeventmask.h" + +void recTestIoc_registerRecordDeviceDriver(struct dbBase *); + +static void test_soft_input(void){ + double value = 543.123; + + /* set soft channel */ + testdbPutFieldOk("test_ai_rec.DTYP", DBF_STRING, "Soft Channel"); + testdbPutFieldOk("test_ai_rec.INP", DBF_STRING, "test_ai_link_rec.VAL"); + testdbPutFieldOk("test_ai_link_rec.FLNK", DBF_STRING, "test_ai_rec"); + + /* set VAL to on linked record */ + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, value); + + /* verify that this record VAL is updated but RVAL is not */ + testdbGetFieldEqual("test_ai_rec.VAL", DBF_DOUBLE, value); + testdbGetFieldEqual("test_ai_rec.RVAL", DBF_DOUBLE, 0.0); + + // number of tests = 6 +} + +static void test_raw_soft_input(void){ + double value = -255; + + /* set soft channel */ + testdbPutFieldOk("test_ai_rec.DTYP", DBF_STRING, "Raw Soft Channel"); + testdbPutFieldOk("test_ai_rec.INP", DBF_STRING, "test_ai_link_rec.VAL"); + testdbPutFieldOk("test_ai_link_rec.FLNK", DBF_STRING, "test_ai_rec"); + + /* set VAL to on linked record */ + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, value); + + /* verify that this record RVAL and VAL are updated */ + testdbGetFieldEqual("test_ai_rec.VAL", DBF_DOUBLE, value); + testdbGetFieldEqual("test_ai_rec.RVAL", DBF_DOUBLE, value); + + // number of tests = 6 +} + +static void test_operator_display(void){ + const short hopr = 199; + const short lopr = 50; + const short prec = 2; + const char egu[] = "mm"; + const char desc[] = "hmm?"; + + /* set operator display parameters */ + testdbPutFieldOk("test_ai_rec.EGU", DBF_STRING, egu); + testdbPutFieldOk("test_ai_rec.DESC", DBF_STRING, desc); + testdbPutFieldOk("test_ai_rec.HOPR", DBF_SHORT, hopr); + testdbPutFieldOk("test_ai_rec.LOPR", DBF_SHORT, lopr); + testdbPutFieldOk("test_ai_rec.PREC", DBF_SHORT, prec); + + /* verify operator display parameters read back */ + testdbGetFieldEqual("test_ai_rec.NAME", DBF_STRING, "test_ai_rec"); + testdbGetFieldEqual("test_ai_rec.EGU", DBF_STRING, egu); + testdbGetFieldEqual("test_ai_rec.DESC", DBF_STRING, desc); + testdbGetFieldEqual("test_ai_rec.HOPR", DBF_SHORT, hopr); + testdbGetFieldEqual("test_ai_rec.LOPR", DBF_SHORT, lopr); + testdbGetFieldEqual("test_ai_rec.PREC", DBF_SHORT, prec); + + // number of tests = 7 +} + +static void test_no_linr_unit_conversion(void){ + const short roff = 10; + const short aslo = 2; + const short aoff = 4; + + const short rval = 9; + const short val = (((rval + roff) * aslo) + aoff); + + + /* set soft channel */ + testdbPutFieldOk("test_ai_rec.DTYP", DBF_STRING, "Raw Soft Channel"); + testdbPutFieldOk("test_ai_rec.INP", DBF_STRING, "test_ai_link_rec.VAL"); + testdbPutFieldOk("test_ai_link_rec.FLNK", DBF_STRING, "test_ai_rec"); + + /* set unit conversion parameters */ + testdbPutFieldOk("test_ai_rec.ROFF", DBF_SHORT, roff); + testdbPutFieldOk("test_ai_rec.ASLO", DBF_SHORT, aslo); + testdbPutFieldOk("test_ai_rec.AOFF", DBF_SHORT, aoff); + testdbPutFieldOk("test_ai_rec.LINR", DBF_LONG, menuConvertNO_CONVERSION); + + /* verify conversion */ + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_SHORT, rval); + testdbGetFieldEqual("test_ai_rec.VAL", DBF_SHORT, val); + testdbGetFieldEqual("test_ai_rec.RVAL", DBF_SHORT, rval); + + // number of tests = 10 +} + +static void test_slope_linr_unit_conversion(void){ + const short roff = 1; + const short aslo = 3; + const short aoff = 99; + const short eslo = 3; + const short eoff = 2; + + const short rval = 7; + const short val = ((((rval + roff) * aslo) + aoff) * eslo) + eoff; + + + /* set soft channel */ + testdbPutFieldOk("test_ai_rec.DTYP", DBF_STRING, "Raw Soft Channel"); + testdbPutFieldOk("test_ai_rec.INP", DBF_STRING, "test_ai_link_rec.VAL"); + testdbPutFieldOk("test_ai_link_rec.FLNK", DBF_STRING, "test_ai_rec"); + + /* set unit conversion parameters */ + testdbPutFieldOk("test_ai_rec.ROFF", DBF_LONG, roff); + testdbPutFieldOk("test_ai_rec.ASLO", DBF_LONG, aslo); + testdbPutFieldOk("test_ai_rec.AOFF", DBF_LONG, aoff); + testdbPutFieldOk("test_ai_rec.ESLO", DBF_LONG, eslo); + testdbPutFieldOk("test_ai_rec.EOFF", DBF_LONG, eoff); + testdbPutFieldOk("test_ai_rec.LINR", DBF_LONG, menuConvertSLOPE); + + /* verify conversion */ + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_LONG, rval); + testdbGetFieldEqual("test_ai_rec.VAL", DBF_LONG, val); + testdbGetFieldEqual("test_ai_rec.RVAL", DBF_LONG, rval); + + // number of tests = 12 +} + +static void test_linear_linr_unit_conversion(void){ + const long roff = 6; + const long aslo = 2; + const long aoff = 19; + const long eslo = 4; + const long eoff = 1; + const long egul = 9999; + const long eguf = -1000; + + const long rval = 2; + /* Since our raw soft input does not support EGUL and EGUF conversion LINEAR should work like SLOPE */ + const long val = ((((rval + roff) * aslo) + aoff) * eslo) + eoff; + + + /* set soft channel */ + testdbPutFieldOk("test_ai_rec.DTYP", DBF_STRING, "Raw Soft Channel"); + testdbPutFieldOk("test_ai_rec.INP", DBF_STRING, "test_ai_link_rec.VAL"); + testdbPutFieldOk("test_ai_link_rec.FLNK", DBF_STRING, "test_ai_rec"); + + /* set unit conversion parameters */ + testdbPutFieldOk("test_ai_rec.ROFF", DBF_LONG, roff); + testdbPutFieldOk("test_ai_rec.ASLO", DBF_LONG, aslo); + testdbPutFieldOk("test_ai_rec.AOFF", DBF_LONG, aoff); + testdbPutFieldOk("test_ai_rec.ESLO", DBF_LONG, eslo); + testdbPutFieldOk("test_ai_rec.EOFF", DBF_LONG, eoff); + /* Since our raw soft input does not support EGUL and EGUF conversion we set them to lagre values here just to check they dont break anything */ + testdbPutFieldOk("test_ai_rec.EGUL", DBF_LONG, egul); + testdbPutFieldOk("test_ai_rec.EGUF", DBF_LONG, eguf); + testdbPutFieldOk("test_ai_rec.LINR", DBF_LONG, menuConvertLINEAR); + + /* verify conversion */ + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_LONG, rval); + testdbGetFieldEqual("test_ai_rec.VAL", DBF_LONG, val); + testdbGetFieldEqual("test_ai_rec.RVAL", DBF_LONG, rval); + + // number of tests = 14 +} + +static void test_bpt_conversion(void){ + const float typeKdegFin = 699.550510; + const float typeKdegF = 3.427551e+02; + const float typeKdegCin = 2902.787322; + const float typeKdegC = 7.028131e+02; + const float typeJdegFin = 1218.865107; + const float typeJdegF = 3.897504e+02; + const float typeJdegCin = 4042.988372; + const float typeJdegC = 6.918437e+02; + + /* set soft channel */ + testdbPutFieldOk("test_ai_rec.DTYP", DBF_STRING, "Raw Soft Channel"); + testdbPutFieldOk("test_ai_rec.INP", DBF_STRING, "test_ai_link_rec.VAL"); + testdbPutFieldOk("test_ai_link_rec.FLNK", DBF_STRING, "test_ai_rec"); + + /* set unit conversion parameters */ + testdbPutFieldOk("test_ai_rec.ROFF", DBF_LONG, 0); + testdbPutFieldOk("test_ai_rec.ASLO", DBF_LONG, 1); + testdbPutFieldOk("test_ai_rec.AOFF", DBF_LONG, 0); + + /* Set type K deg F break point table and verify */ + testdbPutFieldOk("test_ai_rec.LINR", DBF_SHORT, menuConverttypeKdegF); + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_FLOAT, typeKdegFin); + testdbGetFieldEqual("test_ai_rec.VAL", DBF_FLOAT, typeKdegF); + + /* Set type K deg C break point table and verify */ + testdbPutFieldOk("test_ai_rec.LINR", DBF_SHORT, menuConverttypeKdegC); + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_FLOAT, typeKdegCin); + testdbGetFieldEqual("test_ai_rec.VAL", DBF_FLOAT, typeKdegC); + + /* Set type J deg F break point table and verify */ + testdbPutFieldOk("test_ai_rec.LINR", DBF_SHORT, menuConverttypeJdegF); + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_FLOAT, typeJdegFin); + testdbGetFieldEqual("test_ai_rec.VAL", DBF_FLOAT, typeJdegF); + + /* Set type J deg C break point table and verify */ + testdbPutFieldOk("test_ai_rec.LINR", DBF_SHORT, menuConverttypeJdegC); + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_FLOAT, typeJdegCin); + testdbGetFieldEqual("test_ai_rec.VAL", DBF_FLOAT, typeJdegC); + + // number of tests = 18 +} + +static void test_smoothing_filter(void){ + const double smoo = 0.7; + const short roff = 1; + const short aslo = 3; + const short aoff = -2; + const short eslo = 2; + const short eoff = 6; + + const short rval1 = 7; + const short val1 = ((((rval1 + roff) * aslo) + aoff) * eslo) + eoff; + const short rval2 = 9; + const short val2_pre_smoo = ((((rval2 + roff) * aslo) + aoff) * eslo) + eoff; + const float val2 = val1 * smoo + (1 - smoo) * val2_pre_smoo; + + + /* set soft channel */ + testdbPutFieldOk("test_ai_rec.DTYP", DBF_STRING, "Raw Soft Channel"); + testdbPutFieldOk("test_ai_rec.INP", DBF_STRING, "test_ai_link_rec.VAL"); + testdbPutFieldOk("test_ai_link_rec.FLNK", DBF_STRING, "test_ai_rec"); + + /* set unit conversion parameters */ + testdbPutFieldOk("test_ai_rec.ROFF", DBF_LONG, roff); + testdbPutFieldOk("test_ai_rec.ASLO", DBF_LONG, aslo); + testdbPutFieldOk("test_ai_rec.AOFF", DBF_LONG, aoff); + testdbPutFieldOk("test_ai_rec.ESLO", DBF_LONG, eslo); + testdbPutFieldOk("test_ai_rec.EOFF", DBF_LONG, eoff); + testdbPutFieldOk("test_ai_rec.LINR", DBF_LONG, menuConvertSLOPE); + + /* set well known starting point (without smmothing) */ + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_SHORT, rval1); + testdbGetFieldEqual("test_ai_rec.VAL", DBF_SHORT, val1); + + /* set SMOO */ + testdbPutFieldOk("test_ai_rec.SMOO", DBF_DOUBLE, smoo); + + /* update value again and verify smoothing */ + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_SHORT, rval2); + testdbGetFieldEqual("test_ai_rec.VAL", DBF_FLOAT, val2); + + /* clear SMOO */ + testdbPutFieldOk("test_ai_rec.SMOO", DBF_DOUBLE, 0.0); + + // number of tests = 15 +} + +static void test_udf(void){ + /* set soft channel */ + testdbPutFieldOk("test_ai_rec2.DTYP", DBF_STRING, "Raw Soft Channel"); + testdbPutFieldOk("test_ai_rec2.INP", DBF_STRING, "test_ai_link_rec2.VAL"); + testdbPutFieldOk("test_ai_link_rec2.FLNK", DBF_STRING, "test_ai_rec2"); + + /* verify UDF */ + testdbGetFieldEqual("test_ai_rec2.UDF", DBF_CHAR, TRUE); + + /* set VAL and verify UDF is cleared */ + testdbPutFieldOk("test_ai_link_rec2.VAL", DBF_FLOAT, 7.0); + testdbGetFieldEqual("test_ai_rec2.UDF", DBF_CHAR, FALSE); + + // number of tests = 6 +} + +static void test_alarm(void){ + const double h = 5000; + const double hh = 7500; + const double l = 200; + const double ll = -20; + const double hyst = 5; + + /* set soft channel */ + testdbPutFieldOk("test_ai_rec.DTYP", DBF_STRING, "Soft Channel"); + testdbPutFieldOk("test_ai_rec.INP", DBF_STRING, "test_ai_link_rec.VAL"); + testdbPutFieldOk("test_ai_link_rec.FLNK", DBF_STRING, "test_ai_rec"); + + /* set alarm parameters */ + testdbPutFieldOk("test_ai_rec.HIGH", DBF_DOUBLE, h); + testdbPutFieldOk("test_ai_rec.HIHI", DBF_DOUBLE, hh); + testdbPutFieldOk("test_ai_rec.LOW", DBF_DOUBLE, l); + testdbPutFieldOk("test_ai_rec.LOLO", DBF_DOUBLE, ll); + testdbPutFieldOk("test_ai_rec.HHSV", DBF_SHORT, menuAlarmSevrMAJOR); + testdbPutFieldOk("test_ai_rec.HSV", DBF_SHORT, menuAlarmSevrMINOR); + testdbPutFieldOk("test_ai_rec.LSV", DBF_SHORT, menuAlarmSevrMINOR); + testdbPutFieldOk("test_ai_rec.LLSV", DBF_SHORT, menuAlarmSevrMAJOR); + + /* set non alarm VAL and verify */ + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, 400.0); + testdbGetFieldEqual("test_ai_rec.SEVR", DBF_SHORT, menuAlarmSevrNO_ALARM); + + /* set LOW alarm VAL and verify */ + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, 190.0); + testdbGetFieldEqual("test_ai_rec.VAL", DBF_DOUBLE, 190.0); + testdbGetFieldEqual("test_ai_rec.SEVR", DBF_SHORT, menuAlarmSevrMINOR); + + /* set LOLO alarm VAL and verify */ + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, -9998.0); + testdbGetFieldEqual("test_ai_rec.SEVR", DBF_SHORT, menuAlarmSevrMAJOR); + + /* set HIGH alarm VAL and verify */ + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, 6111.0); + testdbGetFieldEqual("test_ai_rec.SEVR", DBF_SHORT, menuAlarmSevrMINOR); + + /* set HIHI alarm VAL and verify */ + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, 19998.0); + testdbGetFieldEqual("test_ai_rec.SEVR", DBF_SHORT, menuAlarmSevrMAJOR); + + /* set HYST, verify and clear */ + testdbPutFieldOk("test_ai_rec.HYST", DBF_DOUBLE, hyst); + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, hh - 1.0); + testdbGetFieldEqual("test_ai_rec.SEVR", DBF_SHORT, menuAlarmSevrMAJOR); + testdbPutFieldOk("test_ai_rec.HYST", DBF_DOUBLE, 0.0); + testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, hh - 2.0); + testdbGetFieldEqual("test_ai_rec.SEVR", DBF_SHORT, menuAlarmSevrMINOR); + + /* verify LALM */ + testdbGetFieldEqual("test_ai_rec.LALM", DBF_DOUBLE, h); + + // number of tests = 29 +} + +static void test_aftc(void){ + const double h = 5000; + const double hh = 7500; + const double l = 200; + const double ll = -20; + const double aftc = 3; + testMonitor* test_mon = NULL; + epicsTimeStamp startTime; + epicsTimeStamp endTime; + double diffTime = 0; + + /* set soft channel */ + testdbPutFieldOk("test_ai_rec3.DTYP", DBF_STRING, "Raw Soft Channel"); + testdbPutFieldOk("test_ai_rec3.INP", DBF_STRING, "test_ai_link_rec3.VAL"); + testdbPutFieldOk("test_ai_link_rec3.FLNK", DBF_STRING, "test_ai_rec3"); + + /* set alarm parameters */ + testdbPutFieldOk("test_ai_rec3.HIGH", DBF_DOUBLE, h); + testdbPutFieldOk("test_ai_rec3.HIHI", DBF_DOUBLE, hh); + testdbPutFieldOk("test_ai_rec3.LOW", DBF_DOUBLE, l); + testdbPutFieldOk("test_ai_rec3.LOLO", DBF_DOUBLE, ll); + testdbPutFieldOk("test_ai_rec3.HHSV", DBF_SHORT, menuAlarmSevrMAJOR); + testdbPutFieldOk("test_ai_rec3.HSV", DBF_SHORT, menuAlarmSevrMINOR); + testdbPutFieldOk("test_ai_rec3.LSV", DBF_SHORT, menuAlarmSevrMINOR); + testdbPutFieldOk("test_ai_rec3.LLSV", DBF_SHORT, menuAlarmSevrMAJOR); + + /* test AFTC using a monitor and time stamps */ + testdbPutFieldOk("test_ai_rec3.AFTC", DBF_DOUBLE, aftc); + testdbPutFieldOk("test_ai_rec3.SCAN", DBF_SHORT, menuScan_1_second); + + /* set HIHI alarm VAL */ + testdbPutFieldOk("test_ai_link_rec3.VAL", DBF_DOUBLE, 7550.0); + + /* Create test monitor for alarm SEVR */ + test_mon = testMonitorCreate("test_ai_rec3.VAL", DBE_ALARM, 0); + + /* Get start time */ + epicsTimeGetCurrent(&startTime); + + /* wait for monitor to trigger on the new alarm status*/ + testMonitorWait(test_mon); + epicsTimeGetCurrent(&endTime); + + /* Verify that alarm status is now MAJOR */ + testdbGetFieldEqual("test_ai_rec3.SEVR", DBF_SHORT, menuAlarmSevrMAJOR); + + /* set HI alarm VAL */ + testdbPutFieldOk("test_ai_link_rec3.VAL", DBF_DOUBLE, 5550.0); + + /* Create test monitor for alarm SEVR */ + test_mon = testMonitorCreate("test_ai_rec3.VAL", DBE_ALARM, 0); + + /* Get start time */ + epicsTimeGetCurrent(&startTime); + + /* wait for monitor to trigger on the new alarm status*/ + testMonitorWait(test_mon); + epicsTimeGetCurrent(&endTime); + + /* Verify that alarm status is now MINOR */ + testdbGetFieldEqual("test_ai_rec3.SEVR", DBF_SHORT, menuAlarmSevrMINOR); + + /* Verify that time is at least equal to configured aftc */ + diffTime = epicsTimeDiffInSeconds(&endTime, &startTime); + testOk(diffTime >= aftc, "ATFC time %lf", diffTime); + + // number of tests = 18 +} + +MAIN(aiTest) { + +#ifdef _WIN32 +#if (defined(_MSC_VER) && _MSC_VER < 1900) || \ + (defined(_MINGW) && defined(_TWO_DIGIT_EXPONENT)) + _set_output_format(_TWO_DIGIT_EXPONENT); +#endif +#endif + + testPlan(6+6+11+10+12+14+18+15+6+29+18); + + testdbPrepare(); + testdbReadDatabase("recTestIoc.dbd", NULL, NULL); + recTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("aiTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + test_soft_input(); + test_raw_soft_input(); + test_operator_display(); + test_no_linr_unit_conversion(); + test_slope_linr_unit_conversion(); + test_linear_linr_unit_conversion(); + test_bpt_conversion(); + test_smoothing_filter(); + test_udf(); + test_alarm(); + test_aftc(); + + testIocShutdownOk(); + testdbCleanup(); + + return testDone(); +} diff --git a/modules/database/test/std/rec/aiTest.db b/modules/database/test/std/rec/aiTest.db new file mode 100644 index 000000000..ef05fb180 --- /dev/null +++ b/modules/database/test/std/rec/aiTest.db @@ -0,0 +1,20 @@ +record(ai, "test_ai_rec") { +} + +record(ao, "test_ai_link_rec") { + +} + +record(ai, "test_ai_rec2") { +} + +record(ao, "test_ai_link_rec2") { + +} + +record(ai, "test_ai_rec3") { +} + +record(ao, "test_ai_link_rec3") { + +} diff --git a/modules/database/test/std/rec/biTest.c b/modules/database/test/std/rec/biTest.c new file mode 100644 index 000000000..8e551e762 --- /dev/null +++ b/modules/database/test/std/rec/biTest.c @@ -0,0 +1,122 @@ +/*************************************************************************\ +* Copyright (c) 2023 Karl Vestin +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "dbUnitTest.h" +#include "testMain.h" +#include "errlog.h" +#include "dbAccess.h" +#include "menuAlarmSevr.h" + +void recTestIoc_registerRecordDeviceDriver(struct dbBase *); + +static void test_soft_input(void){ + /* set soft channel */ + testdbPutFieldOk("test_bi_rec.DTYP", DBF_STRING, "Soft Channel"); + testdbPutFieldOk("test_bi_rec.INP", DBF_STRING, "test_bi_link_rec.VAL"); + testdbPutFieldOk("test_bi_link_rec.FLNK", DBF_STRING, "test_bi_rec"); + + /* set VAL to on linked record */ + testdbPutFieldOk("test_bi_link_rec.VAL", DBF_SHORT, TRUE); + + /* verify that this record VAL is updated but RVAL is not */ + testdbGetFieldEqual("test_bi_rec.VAL", DBF_SHORT, TRUE); + testdbGetFieldEqual("test_bi_rec.RVAL", DBF_SHORT, FALSE); + + // number of tests = 6 +} + +static void test_raw_soft_input(void){ + /* set soft channel */ + testdbPutFieldOk("test_bi_rec.DTYP", DBF_STRING, "Raw Soft Channel"); + testdbPutFieldOk("test_bi_rec.INP", DBF_STRING, "test_bi_link_rec.VAL"); + testdbPutFieldOk("test_bi_link_rec.FLNK", DBF_STRING, "test_bi_rec"); + + /* set VAL to on linked record */ + testdbPutFieldOk("test_bi_link_rec.VAL", DBF_SHORT, TRUE); + + /* verify that this record RVAL and VAL are updated */ + testdbGetFieldEqual("test_bi_rec.VAL", DBF_SHORT, TRUE); + testdbGetFieldEqual("test_bi_rec.RVAL", DBF_SHORT, TRUE); + + // number of tests = 6 +} + +static void test_operator_display(void){ + /* set operator display parameters */ + testdbPutFieldOk("test_bi_rec.ZNAM", DBF_STRING, "ZNAM_TEST"); + testdbPutFieldOk("test_bi_rec.ONAM", DBF_STRING, "ONAM_TEST"); + testdbPutFieldOk("test_bi_rec.DESC", DBF_STRING, "DESC_TEST"); + + /* verify operator display parameters */ + testdbGetFieldEqual("test_bi_rec.ZNAM", DBF_STRING, "ZNAM_TEST"); + testdbGetFieldEqual("test_bi_rec.ONAM", DBF_STRING, "ONAM_TEST"); + testdbGetFieldEqual("test_bi_rec.NAME", DBF_STRING, "test_bi_rec"); + testdbGetFieldEqual("test_bi_rec.DESC", DBF_STRING, "DESC_TEST"); + + /* verify conversion */ + testdbPutFieldOk("test_bi_link_rec.VAL", DBF_SHORT, TRUE); + testdbGetFieldEqual("test_bi_rec.VAL", DBF_STRING, "ONAM_TEST"); + testdbPutFieldOk("test_bi_link_rec.VAL", DBF_SHORT, FALSE); + testdbGetFieldEqual("test_bi_rec.VAL", DBF_STRING, "ZNAM_TEST"); + + // number of tests = 11 +} + +static void test_alarm(void){ + /* set soft channel */ + testdbPutFieldOk("test_bi_rec.DTYP", DBF_STRING, "Soft Channel"); + testdbPutFieldOk("test_bi_rec.INP", DBF_STRING, "test_bi_link_rec.VAL"); + testdbPutFieldOk("test_bi_link_rec.FLNK", DBF_STRING, "test_bi_rec"); + + /* set start VAL to FALSE*/ + testdbPutFieldOk("test_bi_link_rec.VAL", DBF_SHORT, FALSE); + + /* set alarm parameters */ + testdbPutFieldOk("test_bi_rec.ZSV", DBF_SHORT, menuAlarmSevrNO_ALARM); + testdbPutFieldOk("test_bi_rec.OSV", DBF_SHORT, menuAlarmSevrMINOR); + testdbPutFieldOk("test_bi_rec.COSV", DBF_SHORT, menuAlarmSevrINVALID); + + /* verify alarm status is NO_ALARM*/ + testdbGetFieldEqual("test_bi_rec.SEVR", DBF_SHORT, menuAlarmSevrNO_ALARM); + + /* set ZSV to MAJOR and verify that SEVR is now MAJOR */ + testdbPutFieldOk("test_bi_rec.ZSV", DBF_SHORT, menuAlarmSevrMAJOR); + testdbGetFieldEqual("test_bi_rec.SEVR", DBF_SHORT, menuAlarmSevrMAJOR); + + /* set VAL to 1 on linked record and verify that COSV now sets the SEVR to INVALID */ + testdbPutFieldOk("test_bi_link_rec.VAL", DBF_SHORT, TRUE); + testdbGetFieldEqual("test_bi_rec.SEVR", DBF_SHORT, menuAlarmSevrINVALID); + + /* verify LAML */ + testdbGetFieldEqual("test_bi_rec.LALM", DBF_SHORT, TRUE); + + // number of tests = 13 +} + +MAIN(biTest) { + + testPlan(6+6+11+13); + + testdbPrepare(); + testdbReadDatabase("recTestIoc.dbd", NULL, NULL); + recTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("biTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + test_soft_input(); + test_raw_soft_input(); + test_operator_display(); + test_alarm(); + + testIocShutdownOk(); + testdbCleanup(); + + return testDone(); +} diff --git a/modules/database/test/std/rec/biTest.db b/modules/database/test/std/rec/biTest.db new file mode 100644 index 000000000..4304cce91 --- /dev/null +++ b/modules/database/test/std/rec/biTest.db @@ -0,0 +1,6 @@ +record(bi, "test_bi_rec") { +} + +record(bo, "test_bi_link_rec") { + +} diff --git a/modules/database/test/std/rec/boTest.c b/modules/database/test/std/rec/boTest.c new file mode 100644 index 000000000..55865c79c --- /dev/null +++ b/modules/database/test/std/rec/boTest.c @@ -0,0 +1,158 @@ +/*************************************************************************\ +* Copyright (c) 2023 Karl Vestin +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "dbUnitTest.h" +#include "testMain.h" +#include "errlog.h" +#include "dbAccess.h" +#include "menuAlarmSevr.h" +#include "menuIvoa.h" + +#include "boRecord.h" + +void recTestIoc_registerRecordDeviceDriver(struct dbBase *); + +static void test_soft_output(void){ + /* set soft channel */ + testdbPutFieldOk("test_bo_rec.DTYP", DBF_STRING, "Soft Channel"); + testdbPutFieldOk("test_bo_rec.OUT", DBF_STRING, "test_bo_link_rec"); + + /* set VAL to process record */ + testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, TRUE); + + /* verify that OUT record is updated */ + testdbGetFieldEqual("test_bo_link_rec.VAL", DBF_SHORT, TRUE); + + // number of tests = 4 +} + +static void test_high(void){ + const int high_time = 2; + testMonitor* test_mon = NULL; + epicsTimeStamp startTime; + epicsTimeStamp endTime; + double diffTime = 0; + double diffTimeTolerance = 0.1; + + /* set soft channel */ + testdbPutFieldOk("test_bo_rec.DTYP", DBF_STRING, "Soft Channel"); + testdbPutFieldOk("test_bo_rec.OUT", DBF_STRING, "test_bo_link_rec"); + + /* set HIGH to 2 seconds */ + testdbPutFieldOk("test_bo_rec.HIGH", DBF_SHORT, high_time); + + /* Create test monitor */ + test_mon = testMonitorCreate("test_bo_rec.VAL", DBR_SHORT, 0); + + /* Get start time */ + epicsTimeGetCurrent(&startTime); + + /* set VAL to process record */ + testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, TRUE); + + /* wait and verfiy time */ + testMonitorWait(test_mon); + epicsTimeGetCurrent(&endTime); + + /* EPICS timers have a tendency to trip slightly early, hence the test tolerance is added here to avoid false positives in testing */ + diffTime = epicsTimeDiffInSeconds(&endTime, &startTime) + diffTimeTolerance; + testOk(diffTime >= high_time, "HIGH time %lf", diffTime); + + /* verify that both records are set back to 0 */ + testdbGetFieldEqual("test_bo_rec.VAL", DBF_SHORT, FALSE); + testdbGetFieldEqual("test_bo_link_rec.VAL", DBF_SHORT, FALSE); + + testMonitorDestroy(test_mon); + // number of tests = 7 +} + +static void test_operator_display(void){ + /* set operator display parameters */ + testdbPutFieldOk("test_bo_rec.ZNAM", DBF_STRING, "ZNAM_TEST"); + testdbPutFieldOk("test_bo_rec.ONAM", DBF_STRING, "ONAM_TEST"); + testdbPutFieldOk("test_bo_rec.DESC", DBF_STRING, "DESC_TEST"); + + /* verify operator display parameters */ + testdbGetFieldEqual("test_bo_rec.ZNAM", DBF_STRING, "ZNAM_TEST"); + testdbGetFieldEqual("test_bo_rec.ONAM", DBF_STRING, "ONAM_TEST"); + testdbGetFieldEqual("test_bo_rec.NAME", DBF_STRING, "test_bo_rec"); + testdbGetFieldEqual("test_bo_rec.DESC", DBF_STRING, "DESC_TEST"); + + /* verify conversion */ + testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, TRUE); + testdbGetFieldEqual("test_bo_rec.VAL", DBF_STRING, "ONAM_TEST"); + testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, FALSE); + testdbGetFieldEqual("test_bo_rec.VAL", DBF_STRING, "ZNAM_TEST"); + + // number of tests = 11 +} + +static void test_alarm(void){ + /* set soft channel */ + testdbPutFieldOk("test_bo_rec.DTYP", DBF_STRING, "Soft Channel"); + testdbPutFieldOk("test_bo_rec.OUT", DBF_STRING, "test_bo_link_rec"); + + /* Set start VAL to FALSE*/ + testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, FALSE); + + /* set alarm parameters */ + testdbPutFieldOk("test_bo_rec.ZSV", DBF_SHORT, menuAlarmSevrNO_ALARM); + testdbPutFieldOk("test_bo_rec.OSV", DBF_SHORT, menuAlarmSevrMINOR); + testdbPutFieldOk("test_bo_rec.COSV", DBF_SHORT, menuAlarmSevrINVALID); + testdbPutFieldOk("test_bo_rec.IVOA", DBF_SHORT, menuIvoaSet_output_to_IVOV); + testdbPutFieldOk("test_bo_rec.IVOV", DBF_SHORT, FALSE); + + /* Verify alarm status is NO_ALARM*/ + testdbGetFieldEqual("test_bo_rec.SEVR", DBF_SHORT, menuAlarmSevrNO_ALARM); + + /* Set ZSV to MAJOR and verify that SEVR is now MAJOR */ + testdbPutFieldOk("test_bo_rec.ZSV", DBF_SHORT, menuAlarmSevrMAJOR); + testdbGetFieldEqual("test_bo_rec.SEVR", DBF_SHORT, menuAlarmSevrMAJOR); + + /* Set VAL to 1 and verify that COSV now sets the SEVR to INVALID and in turn triggers the IVOV on output */ + testdbPutFieldOk("test_bo_link_rec.VAL", DBF_SHORT, TRUE); + testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, TRUE); + testdbGetFieldEqual("test_bo_rec.SEVR", DBF_SHORT, menuAlarmSevrINVALID); + testdbGetFieldEqual("test_bo_link_rec.VAL", DBF_SHORT, FALSE); + + /* verify IVOV continue normally action */ + testdbPutFieldOk("test_bo_rec.IVOA", DBF_SHORT, menuIvoaContinue_normally); + testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, TRUE); + testdbGetFieldEqual("test_bo_link_rec.VAL", DBF_SHORT, TRUE); + + /* verify IVOV dont drive outputs action */ + testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, FALSE); + testdbPutFieldOk("test_bo_rec.IVOA", DBF_SHORT, menuIvoaDon_t_drive_outputs); + testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, TRUE); + testdbGetFieldEqual("test_bo_link_rec.VAL", DBF_SHORT, FALSE); + + // number of tests = 22 +} + +MAIN(boTest) { + + testPlan(4+7+11+22); + + testdbPrepare(); + testdbReadDatabase("recTestIoc.dbd", NULL, NULL); + recTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("boTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + test_soft_output(); + test_high(); + test_operator_display(); + test_alarm(); + + testIocShutdownOk(); + testdbCleanup(); + + return testDone(); +} diff --git a/modules/database/test/std/rec/boTest.db b/modules/database/test/std/rec/boTest.db new file mode 100644 index 000000000..0e39cf5f0 --- /dev/null +++ b/modules/database/test/std/rec/boTest.db @@ -0,0 +1,6 @@ +record(bo, "test_bo_rec") { +} + +record(bi, "test_bo_link_rec") { + +} diff --git a/modules/database/test/std/rec/epicsRunRecordTests.c b/modules/database/test/std/rec/epicsRunRecordTests.c index 79db80e3f..60ddd0f32 100644 --- a/modules/database/test/std/rec/epicsRunRecordTests.c +++ b/modules/database/test/std/rec/epicsRunRecordTests.c @@ -24,6 +24,10 @@ int asyncSoftTest(void); int simmTest(void); int mbbioDirectTest(void); int scanEventTest(void); +int boTest(void); +int biTest(void); +int printfTest(void); +int aiTest(void); void epicsRunRecordTests(void) { @@ -51,5 +55,13 @@ void epicsRunRecordTests(void) runTest(scanEventTest); + runTest(boTest); + + runTest(biTest); + + runTest(printfTest); + + runTest(aiTest); + epicsExit(0); /* Trigger test harness */ } diff --git a/modules/database/test/std/rec/printfTest.c b/modules/database/test/std/rec/printfTest.c new file mode 100644 index 000000000..b62b1d9c0 --- /dev/null +++ b/modules/database/test/std/rec/printfTest.c @@ -0,0 +1,577 @@ +/*************************************************************************\ +* Copyright (c) 2023 Karl Vestin +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include "dbUnitTest.h" +#include "testMain.h" +#include "errlog.h" +#include "dbAccess.h" + +void recTestIoc_registerRecordDeviceDriver(struct dbBase *); + +static void test_double_percentile(void){ + const char format_string[] = "Format test string %%d"; + const char result_string[] = "Format test string %d"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 1); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_d_format(void){ + const char format_string[] = "Format test string %d"; + const char result_string[] = "Format test string 7"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 7); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_c_format(void){ + const char format_string[] = "Format test string %c"; + const char result_string[] = "Format test string R"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0, 82 is ASCII for R */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 82); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_i_format(void){ + const char format_string[] = "Format test string %i"; + const char result_string[] = "Format test string -27"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, -27); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_o_format(void){ + const char format_string[] = "Format test string %o"; + const char result_string[] = "Format test string 6777"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 06777); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_u_format(void){ + const char format_string[] = "Format test string %u"; + const char result_string[] = "Format test string 8009"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 8009); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_x_format(void){ + const char format_string[] = "Format test string %x"; + const char result_string[] = "Format test string fafa"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_LONG, 0xfafa); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_X_format(void){ + const char format_string[] = "Format test string %X"; + const char result_string[] = "Format test string BA"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 0x00ba); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_e_format(void){ + const char format_string[] = "Format test string %e"; + const char result_string[] = "Format test string -1.400000e+01"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, -14); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_E_format(void){ + const char format_string[] = "Format test string %E"; + const char result_string[] = "Format test string 1.992000E+03"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 1992); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_f_format(void){ + const char format_string[] = "Format test string %f"; + const char result_string[] = "Format test string -0.062000"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_DOUBLE, -0.062); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_F_format(void){ + const char format_string[] = "Format test string %F"; + const char result_string[] = "Format test string 6729982.999000"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_DOUBLE, 6729982.999); + + /* verify that string is formatted as expected */ + // visual studio less than 2015 does not support %F + // mingw/gcc also fails, suspect this may be gcc version + // related and checking __GNUC__ could resolve but + // initial attempts didn't work so excluding mingw entirely for now + #ifdef _WIN32 + #if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(_MINGW) + testTodoBegin("Fails on windows with old visual studio versions and mingw"); + #endif + #endif + + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + #ifdef _WIN32 + #if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(_MINGW) + testTodoEnd(); + #endif + #endif + // number of tests = 3 +} + +static void test_g_format(void){ + const char format_string[] = "Format test string %g"; + const char result_string[] = "Format test string -0.093"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_DOUBLE, -93e-3); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_G_format(void){ + const char format_string[] = "Format test string %G"; + const char result_string[] = "Format test string 7.2884E+08"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_LONG, 728839938); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_s_format(void){ + + const char format_string[] = "Format test string %d %s"; + const char result_string[] = "Format test string 7 Molly III"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 7); + + /* set value on inp1 */ + testdbPutFieldOk("test_printf_inp1_rec.VAL", DBF_STRING, "Molly III"); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 4 +} + +static void test_plus_flag(void){ + + const char format_string[] = "Format test string %+d"; + const char result_string[] = "Format test string +7"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 7); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_minus_flag(void){ + + const char format_string[] = "Format test string %-10d"; + const char result_string[] = "Format test string 18 "; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 18); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_space_flag(void){ + + const char format_string[] = "Format test string % d"; + const char result_string[] = "Format test string 12"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 12); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_hash_flag(void){ + + const char format_string[] = "Format test string %#o"; + const char result_string[] = "Format test string 014"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 014); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_min_width_flag(void){ + + const char format_string[] = "Format test string %04i"; + const char result_string[] = "Format test string 0003"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 3); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_prec_flag(void){ + + const char format_string[] = "Format test string %.4f"; + const char result_string[] = "Format test string 71.2000"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_DOUBLE, 71.2); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_h_flag(void){ + + const char format_string[] = "Format test string %hx"; + const char result_string[] = "Format test string baba"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_LONG, 0xffbaba); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 3 +} + +static void test_hh_flag(void){ + + const char format_string[] = "Format test string %hhx"; + const char result_string[] = "Format test string c1"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_LONG, 0xffc0c1); + + /* verify that string is formatted as expected */ + #ifdef __rtems__ + testTodoBegin("Fails on UB-20 gcc-9 on RTEMS"); + #endif + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + #ifdef __rtems__ + testTodoEnd(); + #endif + + // number of tests = 3 +} + +static void test_l_flag(void){ + + const char format_string[] = "Format test string %lx"; + const char result_string[] = "Format test string 70a1c0c1"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_LONG, 0x70a1c0c1); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + // number of tests = 3 +} + +static void test_ll_flag(void){ + + const char format_string[] = "%d %s %llx"; + const char result_string[] = "2 Reperbahn ba0110baa0a1c0c1"; + const epicsInt64 value = 0xba0110baa0a1c0c1ull; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 2); + + /* set value on inp1 */ + testdbPutFieldOk("test_printf_inp1_rec.VAL", DBF_STRING, "Reperbahn"); + + /* set value on inp2 */ + testdbPutFieldOk("test_printf_inp2_rec.VAL", DBR_INT64, value); + testdbGetFieldEqual("test_printf_inp2_rec.VAL", DBR_INT64, value); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 6 +} + +static void test_sizv(void){ + + const char format_string[] = "%d %s %llx"; + const char result_string[] = "99 123456789012345678901234567890 6640baa0a1"; + const epicsInt64 value = 0x06640baa0a1c0c1ull; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 99); + + /* set value on inp1 */ + testdbPutFieldOk("test_printf_inp1_rec.VAL", DBF_STRING, "123456789012345678901234567890"); + + /* set value on inp2 */ + testdbPutFieldOk("test_printf_inp2_rec.VAL", DBR_INT64, value); + testdbGetFieldEqual("test_printf_inp2_rec.VAL", DBR_INT64, value); + + /* verify that string is formatted as expected */ + testdbGetArrFieldEqual("test_printf_rec.VAL$", DBF_CHAR, 45, 45, result_string); + + // number of tests = 6 +} + +static void test_all_inputs(void){ + + const char format_string[] = "%d %s %i %i %i %i %i %i %i %i"; + const char result_string[] = "0 One 2 3 4 5 6 7 8 9"; + + /* set format string */ + testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string); + + /* set value on inp0 */ + testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 0); + + /* set value on inp1 */ + testdbPutFieldOk("test_printf_inp1_rec.VAL", DBF_STRING, "One"); + + /* set value on inp2 */ + testdbPutFieldOk("test_printf_inp2_rec.VAL", DBF_SHORT, 2); + + /* set value on inp3 */ + testdbPutFieldOk("test_printf_inp3_rec.VAL", DBF_SHORT, 3); + + /* set value on inp4 */ + testdbPutFieldOk("test_printf_inp4_rec.VAL", DBF_SHORT, 4); + + /* set value on inp5 */ + testdbPutFieldOk("test_printf_inp5_rec.VAL", DBF_SHORT, 5); + + /* set value on inp6 */ + testdbPutFieldOk("test_printf_inp6_rec.VAL", DBF_SHORT, 6); + + /* set value on inp7 */ + testdbPutFieldOk("test_printf_inp7_rec.VAL", DBF_SHORT, 7); + + /* set value on inp8 */ + testdbPutFieldOk("test_printf_inp8_rec.VAL", DBF_SHORT, 8); + + /* set value on inp9 */ + testdbPutFieldOk("test_printf_inp9_rec.VAL", DBF_SHORT, 9); + + /* verify that string is formatted as expected */ + testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string); + + // number of tests = 12 +} + +MAIN(printfTest) { +#ifdef _WIN32 +#if (defined(_MSC_VER) && _MSC_VER < 1900) || \ + (defined(_MINGW) && defined(_TWO_DIGIT_EXPONENT)) + _set_output_format(_TWO_DIGIT_EXPONENT); +#endif +#endif + + testPlan(3+3+3+3+3+3+3+3+4+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+6+6+12); + + testdbPrepare(); + testdbReadDatabase("recTestIoc.dbd", NULL, NULL); + recTestIoc_registerRecordDeviceDriver(pdbbase); + + testdbReadDatabase("printfTest.db", NULL, NULL); + + eltc(0); + testIocInitOk(); + eltc(1); + + test_double_percentile(); + test_c_format(); + test_d_format(); + test_i_format(); + test_o_format(); + test_u_format(); + test_x_format(); + test_X_format(); + test_e_format(); + test_E_format(); + test_f_format(); + test_F_format(); + test_g_format(); + test_G_format(); + test_s_format(); + test_plus_flag(); + test_minus_flag(); + test_space_flag(); + test_hash_flag(); + test_min_width_flag(); + test_prec_flag(); + test_h_flag(); + test_hh_flag(); + test_l_flag(); + test_ll_flag(); + test_sizv(); + test_all_inputs(); + + testIocShutdownOk(); + testdbCleanup(); + + return testDone(); +} diff --git a/modules/database/test/std/rec/printfTest.db b/modules/database/test/std/rec/printfTest.db new file mode 100644 index 000000000..1f4de2e6e --- /dev/null +++ b/modules/database/test/std/rec/printfTest.db @@ -0,0 +1,53 @@ +record(printf, "test_printf_rec") { + field(SIZV, 45) + field(INP0, "test_printf_inp0_rec") + field(INP1, "test_printf_inp1_rec") + field(INP2, "test_printf_inp2_rec") + field(INP3, "test_printf_inp3_rec") + field(INP4, "test_printf_inp4_rec") + field(INP5, "test_printf_inp5_rec") + field(INP6, "test_printf_inp6_rec") + field(INP7, "test_printf_inp7_rec") + field(INP8, "test_printf_inp8_rec") + field(INP9, "test_printf_inp9_rec") +} + +record(ai, "test_printf_inp0_rec") { + field(FLNK, "test_printf_rec") +} + +record(stringin, "test_printf_inp1_rec") { + field(FLNK, "test_printf_rec") +} + +record(int64in, "test_printf_inp2_rec") { + field(FLNK, "test_printf_rec") +} + +record(ai, "test_printf_inp3_rec") { + field(FLNK, "test_printf_rec") +} + +record(ai, "test_printf_inp4_rec") { + field(FLNK, "test_printf_rec") +} + +record(ai, "test_printf_inp5_rec") { + field(FLNK, "test_printf_rec") +} + +record(ai, "test_printf_inp6_rec") { + field(FLNK, "test_printf_rec") +} + +record(ai, "test_printf_inp7_rec") { + field(FLNK, "test_printf_rec") +} + +record(ai, "test_printf_inp8_rec") { + field(FLNK, "test_printf_rec") +} + +record(ai, "test_printf_inp9_rec") { + field(FLNK, "test_printf_rec") +} From b2c80efd332854220e31496b0107b412167744ea Mon Sep 17 00:00:00 2001 From: Minijackson Date: Wed, 8 Mar 2023 11:30:09 +0000 Subject: [PATCH 07/19] release notes: link release notes from submodules' fixes #226 --- documentation/RELEASE_NOTES.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 984011fe5..381b4b8dc 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -9,7 +9,14 @@ important to read more than just the first section to understand everything that has changed in each release. The PVA submodules each have their own individual sets of release notes which -should also be read to understand what has changed since earlier releases. +should also be read to understand what has changed since earlier releases: + +- [normativeTypes](https://github.com/epics-base/normativeTypesCPP/blob/master/documentation/RELEASE_NOTES.md) +- [pvAccess](http://epics-base.github.io/pvAccessCPP/pvarelease_notes.html) +- [pvData](http://epics-base.github.io/pvDataCPP/release_notes.html) +- [pvDatabase](https://github.com/epics-base/pvDatabaseCPP/blob/master/documentation/RELEASE_NOTES.md) +- [pva2pva](https://epics-base.github.io/pva2pva/release_notes.html) +- [pvaClient](https://github.com/epics-base/pvaClientCPP/blob/master/documentation/RELEASE_NOTES.md) **This version of EPICS has not been released yet.** From a01c671399f0fd1329a3d03188eeefbba04c1697 Mon Sep 17 00:00:00 2001 From: Henrique Silva Date: Wed, 8 Mar 2023 17:19:38 +0100 Subject: [PATCH 08/19] Add missing HYST field documentation to longin record --- modules/database/src/std/rec/longinRecord.dbd.pod | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/database/src/std/rec/longinRecord.dbd.pod b/modules/database/src/std/rec/longinRecord.dbd.pod index 498cf61c6..59d5c7a34 100644 --- a/modules/database/src/std/rec/longinRecord.dbd.pod +++ b/modules/database/src/std/rec/longinRecord.dbd.pod @@ -70,6 +70,10 @@ The possible alarm conditions for long inputs are the SCAN, READ, and limit alarms. The SCAN and READ alarms are called by the record or device support routines. +The HYST field controls hysteresis to prevent alarm chattering from an input +signal that is close to one of the limits and suffers from significant readout +noise. + The limit alarms are configured by the user in the HIHI, LOLO, HIGH, and LOW fields using numerical values. For each of these fields, there is a corresponding severity field which can be either NO_ALARM, MINOR, or MAJOR. The From 5c9903115719dde7f265d5201d4f15d1a00938de Mon Sep 17 00:00:00 2001 From: Henrique Silva Date: Wed, 8 Mar 2023 17:22:17 +0100 Subject: [PATCH 09/19] Add missing AFTC documentation to records Fixes #313 --- modules/database/src/std/rec/calcRecord.dbd.pod | 9 +++++++-- modules/database/src/std/rec/longinRecord.dbd.pod | 7 ++++++- modules/database/src/std/rec/mbbiRecord.dbd.pod | 7 ++++++- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/modules/database/src/std/rec/calcRecord.dbd.pod b/modules/database/src/std/rec/calcRecord.dbd.pod index 053ce85c5..23893feeb 100644 --- a/modules/database/src/std/rec/calcRecord.dbd.pod +++ b/modules/database/src/std/rec/calcRecord.dbd.pod @@ -460,12 +460,17 @@ conditions. The HYST field defines an alarm deadband for each limit. +The AFTC field sets the time constant on a low-pass filter that delays the +reporting of limit alarms until the signal has been within the alarm range for +that number of seconds (the default AFTC value of zero retains the previous +behavior). + See L for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. -=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST +=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST, AFTC =head3 Monitor Parameters @@ -496,7 +501,7 @@ manner for the VAL field. =cut - include "dbCommon.dbd" + include "dbCommon.dbd" field(VAL,DBF_DOUBLE) { prompt("Result") promptgroup("50 - Output") diff --git a/modules/database/src/std/rec/longinRecord.dbd.pod b/modules/database/src/std/rec/longinRecord.dbd.pod index 59d5c7a34..94b8cf7f5 100644 --- a/modules/database/src/std/rec/longinRecord.dbd.pod +++ b/modules/database/src/std/rec/longinRecord.dbd.pod @@ -74,6 +74,11 @@ The HYST field controls hysteresis to prevent alarm chattering from an input signal that is close to one of the limits and suffers from significant readout noise. +The AFTC field sets the time constant on a low-pass filter that delays the +reporting of limit alarms until the signal has been within the alarm range for +that number of seconds (the default AFTC value of zero retains the previous +behavior). + The limit alarms are configured by the user in the HIHI, LOLO, HIGH, and LOW fields using numerical values. For each of these fields, there is a corresponding severity field which can be either NO_ALARM, MINOR, or MAJOR. The @@ -81,7 +86,7 @@ HYST field can be used to specify a deadband around each limit. L lists the fields related to alarms that are common to all record types. -=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST +=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST, AFTC =head3 Monitor Parameters diff --git a/modules/database/src/std/rec/mbbiRecord.dbd.pod b/modules/database/src/std/rec/mbbiRecord.dbd.pod index a2c526074..b3c7628f3 100644 --- a/modules/database/src/std/rec/mbbiRecord.dbd.pod +++ b/modules/database/src/std/rec/mbbiRecord.dbd.pod @@ -405,6 +405,11 @@ and state alarms. The state alarms are configured in the below severity fields. These fields have the usual possible values for severity fields: NO_ALARM, MINOR, and MAJOR. +The AFTC field sets the time constant on a low-pass filter that delays the +reporting of limit alarms until the signal has been within the alarm range for +that number of seconds (the default AFTC value of zero retains the previous +behavior). + The unknown state severity (UNSV) field, if set to MINOR or MAJOR, triggers an alarm when the record support routine cannot find a matching value in the state value fields for C<<< rval >>>. @@ -420,7 +425,7 @@ for a complete explanation of record alarms and of the standard fields. L lists other fields related to alarms that are common to all record types. -=fields UNSV, COSV, ZRSV, ONSV, TWSV, THSV, FRSV, FVSV, SXSV, SVSV, EISV, NISV, TESV, ELSV, TVSV, TTSV, FTSV, FFSV +=fields UNSV, COSV, ZRSV, ONSV, TWSV, THSV, FRSV, FVSV, SXSV, SVSV, EISV, NISV, TESV, ELSV, TVSV, TTSV, FTSV, FFSV, AFTC =cut From 059d32a975a67093dbc9a6359ef6a765c8659da8 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Thu, 25 May 2023 15:30:28 +0200 Subject: [PATCH 10/19] db: init struct members in dbChannel.c found by static code analysis (cppcheck @ sonarqube) --- modules/database/src/ioc/db/dbChannel.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c index a806f3cfa..fd682c93e 100644 --- a/modules/database/src/ioc/db/dbChannel.c +++ b/modules/database/src/ioc/db/dbChannel.c @@ -575,15 +575,10 @@ long dbChannelOpen(dbChannel *chan) } /* Set up type probe */ - probe.type = dbfl_type_val; - probe.ctx = dbfl_context_read; + memset(&probe, 0, sizeof(probe)); probe.field_type = dbChannelExportType(chan); probe.no_elements = dbChannelElements(chan); probe.field_size = dbChannelFieldSize(chan); - probe.sevr = NO_ALARM; - probe.stat = NO_ALARM; - probe.time.secPastEpoch = 0; - probe.time.nsec = 0; p = probe; From d691acc001315b8c2022920da24de66a94287f65 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 26 May 2023 09:10:11 +0200 Subject: [PATCH 11/19] ca: init local variable in catime.c found by static code analysis (cppcheck @ sonarqube) --- modules/ca/src/client/catime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/ca/src/client/catime.c b/modules/ca/src/client/catime.c index 45c484e50..474ac525f 100644 --- a/modules/ca/src/client/catime.c +++ b/modules/ca/src/client/catime.c @@ -450,7 +450,7 @@ void timeIt ( tf *pfunc, ti *pItems, unsigned iterations, epicsTimeStamp end_time; epicsTimeStamp start_time; double delay; - unsigned inlineIter; + unsigned inlineIter = 0; epicsTimeGetCurrent ( &start_time ); (*pfunc) ( pItems, iterations, &inlineIter ); From 688195a2734f9990641707bd122a19c7f9dfe697 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 26 May 2023 09:11:24 +0200 Subject: [PATCH 12/19] libCom: handle realloc() failures correctly in macEnv.c found by static code analysis (cppcheck @ sonarqube) --- modules/libcom/src/macLib/macEnv.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/libcom/src/macLib/macEnv.c b/modules/libcom/src/macLib/macEnv.c index dc8eed2ba..2eeb033ce 100644 --- a/modules/libcom/src/macLib/macEnv.c +++ b/modules/libcom/src/macLib/macEnv.c @@ -30,6 +30,7 @@ macDefExpand(const char *str, MAC_HANDLE *macros) static const char * pairs[] = { "", "environ", NULL, NULL }; long destCapacity = 128; char *dest = NULL; + char *newdest = NULL; int n; if (macros) { @@ -61,8 +62,11 @@ macDefExpand(const char *str, MAC_HANDLE *macros) } else { size_t unused = destCapacity - ++n; - if (unused >= 20) - dest = realloc(dest, n); + if (unused >= 20) { + newdest = realloc(dest, n); + if (newdest) + dest = newdest; + } } done: From 38c99df2e02554340c1d59bc6493d7317f0fc652 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 26 May 2023 09:20:04 +0200 Subject: [PATCH 13/19] libCom: fix possible memory leaks in iocLogServer.c found by static code analysis (cppcheck @ sonarqube) --- modules/libcom/src/log/iocLogServer.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/libcom/src/log/iocLogServer.c b/modules/libcom/src/log/iocLogServer.c index 809052bf4..674175aac 100644 --- a/modules/libcom/src/log/iocLogServer.c +++ b/modules/libcom/src/log/iocLogServer.c @@ -113,8 +113,8 @@ int main(void) pserver->pfdctx = (void *) fdmgr_init(); if (!pserver->pfdctx) { - free(pserver); fprintf(stderr, "iocLogServer: %s\n", strerror(errno)); + free(pserver); return IOCLS_ERROR; } @@ -149,6 +149,7 @@ int main(void) fprintf (stderr, "iocLogServer: a server is already installed on port %u?\n", (unsigned)ioc_log_port); + free(pserver); return IOCLS_ERROR; } @@ -158,6 +159,7 @@ int main(void) char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf(stderr, "iocLogServer: listen err %s\n", sockErrBuf); + free(pserver); return IOCLS_ERROR; } @@ -174,6 +176,7 @@ int main(void) char sockErrBuf[64]; epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); fprintf(stderr, "iocLogServer: ioctl FIONBIO err %s\n", sockErrBuf); + free(pserver); return IOCLS_ERROR; } @@ -190,6 +193,7 @@ int main(void) "File access problems to `%s' because `%s'\n", ioc_log_file_name, strerror(errno)); + free(pserver); return IOCLS_ERROR; } @@ -202,6 +206,7 @@ int main(void) if (status < 0) { fprintf(stderr, "iocLogServer: failed to add read callback\n"); + free(pserver); return IOCLS_ERROR; } From b51d1de283112bffbbc77f04bc366bdf0ef76c31 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 26 May 2023 09:23:54 +0200 Subject: [PATCH 14/19] libCom: fix possible memory leak in RTEMS-posix/osdMessageQueue.c found by static code analysis (cppcheck @ sonarqube) --- modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c b/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c index 7c7ea8aad..30ec31092 100644 --- a/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c +++ b/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c @@ -47,6 +47,7 @@ epicsMessageQueueCreate(unsigned int capacity, unsigned int maximumMessageSize) id->id = mq_open(id->name, O_RDWR | O_CREAT | O_EXCL, 0644, &the_attr); if (id->id <0) { fprintf (stderr, "Can't create message queue: %s\n", strerror (errno)); + free(id); return NULL; } return id; From 6636b4b9e7086643ef188ec0db4462203834036e Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Wed, 31 May 2023 19:11:22 +0200 Subject: [PATCH 15/19] libCom: check calloc() failure in RTEMS-posix/osdMessageQueue.c --- modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c b/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c index 30ec31092..7ccdc878d 100644 --- a/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c +++ b/modules/libcom/src/osi/os/RTEMS-posix/osdMessageQueue.c @@ -41,6 +41,10 @@ epicsMessageQueueCreate(unsigned int capacity, unsigned int maximumMessageSize) { struct mq_attr the_attr; epicsMessageQueueId id = (epicsMessageQueueId)calloc(1, sizeof(*id)); + if (!id) { + fprintf (stderr, "Can't allocate message queue: %s\n", strerror(errno)); + return NULL; + } sprintf(id->name, "MQ_%0d", epicsAtomicIncrIntT(&idCnt)); the_attr.mq_maxmsg = capacity; the_attr.mq_msgsize = maximumMessageSize; From d87fd0db0124faf450cff93226ae6a2cc02f02bf Mon Sep 17 00:00:00 2001 From: Minijackson Date: Fri, 16 Jun 2023 14:10:59 +0200 Subject: [PATCH 16/19] Fix MAKEFLAGS parsing with Make 4.4+ Since Make version 4.4, MAKEFLAGS also contains long options and overridden variables on the command-line[1]. [1]: https://git.savannah.gnu.org/cgit/make.git/tree/NEWS?h=4.4#n67 This means that parsing by filtering out '--%' doesn't work reliably anymore, since it doesn't remove overrides: Running 'make VAR=quacks' gives 'MAKEFLAGS=" -- VAR=quacks"', and 'checkflags' would understand that flags -q, -s, ... were set. This would get transmitted below into 'QUIET_FLAGS' and 'QUESTION_FLAG', then passed to the 'genVersionHeader.pl' as '-i' and '-q'. The result would be that 'genVersionHeader.pl' would never create the version header (only check for its up-to-date status), leading to confusing errors: ../misc/epicsRelease.c:25:32: error: expected ')' before 'EPICS_VCS_VERSION' 25 | printf ( "## %s\n", "Rev. " EPICS_VCS_VERSION ); | ~ ^~~~~~~~~~~~~~~~~~ The NEWS file[1] recommends using 'firstword', but unfortunately this is not compatible with GNUMake < 3.82. --- configure/CONFIG_COMMON | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure/CONFIG_COMMON b/configure/CONFIG_COMMON index 19b26c24c..742e54ae4 100644 --- a/configure/CONFIG_COMMON +++ b/configure/CONFIG_COMMON @@ -83,8 +83,9 @@ IOCS_APPL_TOP = $(INSTALL_ABSOLUTE) #------------------------------------------------------- # How to portably check the flags to make +makeflags := $(firstword $(filter-out -,$(filter-out --%,$(MAKEFLAGS)))) define checkflags - make-$1 = $(findstring $1,$(filter-out --%,$(MAKEFLAGS))) + make-$1 := $(findstring $1,$(makeflags)) endef # This is extensible to most single letter flags: $(foreach flag,s q, $(eval $(call checkflags,$(flag)))) From 80e62031e9c1baab28ff8e23f923b374063bfaab Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sun, 23 Jul 2023 08:43:32 -0700 Subject: [PATCH 17/19] doc --- documentation/RELEASE_NOTES.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 381b4b8dc..e0ec809b8 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -22,6 +22,12 @@ should also be read to understand what has changed since earlier releases: ## Changes made on the 7.0 branch since 7.0.7 +### Add `$EPICS_CLI_TIMEOUT` + +Add support for CA tools timeout from environment variable `$EPICS_CLI_TIMEOUT` +which sets the default the default timeout for `caget` et al. +The `-w` argument continues to take precedence. + ### Fixed leak from a non-EPICS thread on WIN32 On Windows targets, if a thread not created by `epicsThreadCreate*()` directly From 524f81b8bd147bb714c9ea7b7462b8912a134246 Mon Sep 17 00:00:00 2001 From: Rolf Keitel Date: Tue, 25 Jul 2023 11:36:44 -0500 Subject: [PATCH 18/19] Doc updates to PINI, PHAS & EVNT --- modules/database/src/ioc/db/dbCommon.dbd.pod | 29 ++++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/modules/database/src/ioc/db/dbCommon.dbd.pod b/modules/database/src/ioc/db/dbCommon.dbd.pod index c8ae7e6ba..5366d477a 100644 --- a/modules/database/src/ioc/db/dbCommon.dbd.pod +++ b/modules/database/src/ioc/db/dbCommon.dbd.pod @@ -91,20 +91,25 @@ For example these rates are all valid: 1 second 2 Hertz -The B field specifies record processing at initialization. If it is set -to YES during database configuration, the record is processed once at IOC -initialization (before the normal scan tasks are started). +The B field specifies record processing at initialization. It can have the +values NO, YES, RUN, RUNNING, PAUSE, and PAUSED. If it is set to YES during +database configuration, the record is processed once at IOC initialization +(before the normal scan tasks are started). For the other values see +L for more details. -The B field orders the records within a specific SCAN group. This is not -meaningful for passive records. All records of a specified phase are processed -before those with higher phase number. It is generally better practice to use -linked passive records to enforce the order of processing rather than a phase -number. +The B field orders the records processed within a specific SCAN group or +PINI processing phase. All records of a specified phase are processed before +those with higher phase number. It is generally better practice to use linked +passive records to enforce the order of processing rather than a phase number. +If the PINI field is set to NO, the PHAS field is not meaningful for passive +records. -The B field specifies an event number. This event number is used if the -SCAN field is set to C. All records with scan type C and the -same EVNT value will be processed when a call to post_event for EVNT is made. -The call to post_event is: post_event(short event_number). +The B field specifies an event name or number. This event identifier is +used if the SCAN field is set to C. All records with scan type C +and the same EVNT value will be processed when that event is signalled by other +software running in the IOC, either by calling C for +numeric events, or by calling C with an event handle previously +looked up using C for named events. The B field specifies the scheduling priority for processing records with SCAN=C and asynchronous record completion tasks. From 00dc55b8a2f8426e531b99b66264c6eeff1e340b Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Tue, 8 Aug 2023 10:29:09 +0200 Subject: [PATCH 19/19] Fix usage info in .tools/make-tar.sh --- .tools/make-tar.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.tools/make-tar.sh b/.tools/make-tar.sh index 928006288..276d8e204 100755 --- a/.tools/make-tar.sh +++ b/.tools/make-tar.sh @@ -20,9 +20,9 @@ maybedie() { usage() { cat <&2 -usage: $0 [-v] [-s] [ []] +usage: $0 [-v] [-d] [ []] - may be any git revision spec. (tag, branch, or commit id). + may be any git revision spec. (tag or, using -d, branch or commit id). If provided, must end with ".tar", ".tar.gz" or ".tar.bz2". If is omitted, "base-.tar.gz" will be used.