diff --git a/.github/workflows/ci-scripts-build.yml b/.github/workflows/ci-scripts-build.yml index 83bdd9dde..5ad79311e 100644 --- a/.github/workflows/ci-scripts-build.yml +++ b/.github/workflows/ci-scripts-build.yml @@ -98,6 +98,12 @@ jobs: configuration: default name: "Ub-20 gcc-8" + - os: ubuntu-20.04 + cmp: gcc-9 + utoolchain: "9" + configuration: default + name: "Ub-20 gcc-9" + - os: ubuntu-20.04 cmp: clang configuration: default @@ -118,6 +124,11 @@ jobs: configuration: static name: "Win2019 MSC-19, static" + - os: windows-2019 + cmp: gcc + configuration: default + name: "Win2019 mingw" + steps: - uses: actions/checkout@v2 with: @@ -144,6 +155,7 @@ jobs: - name: Run main module tests run: python .ci/cue.py test - name: Upload tapfiles Artifact + if: ${{ always() }} uses: actions/upload-artifact@v2 with: name: tapfiles ${{ matrix.name }} diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION index a32f28c54..b0c57b763 100644 --- a/configure/CONFIG_BASE_VERSION +++ b/configure/CONFIG_BASE_VERSION @@ -52,11 +52,11 @@ EPICS_MODIFICATION = 5 # EPICS_PATCH_LEVEL must be a number (win32 resource file requirement) # Not included in the official EPICS version number if zero -EPICS_PATCH_LEVEL = 0 +EPICS_PATCH_LEVEL = 1 # Immediately after an official release the EPICS_PATCH_LEVEL is incremented # and the -DEV suffix is added (similar to the Maven -SNAPSHOT versions) -EPICS_DEV_SNAPSHOT= +EPICS_DEV_SNAPSHOT=-DEV # No changes should be needed below here diff --git a/configure/CONFIG_CA_VERSION b/configure/CONFIG_CA_VERSION index 3f8780e25..af570b0e7 100644 --- a/configure/CONFIG_CA_VERSION +++ b/configure/CONFIG_CA_VERSION @@ -2,11 +2,11 @@ EPICS_CA_MAJOR_VERSION = 4 EPICS_CA_MINOR_VERSION = 13 -EPICS_CA_MAINTENANCE_VERSION = 8 +EPICS_CA_MAINTENANCE_VERSION = 9 # Development flag, set to zero for release versions -EPICS_CA_DEVELOPMENT_FLAG = 0 +EPICS_CA_DEVELOPMENT_FLAG = 1 # Immediately after a release the MAINTENANCE_VERSION # will be incremented and the DEVELOPMENT_FLAG set to 1 diff --git a/configure/CONFIG_DATABASE_VERSION b/configure/CONFIG_DATABASE_VERSION index c62f11daa..2205a5386 100644 --- a/configure/CONFIG_DATABASE_VERSION +++ b/configure/CONFIG_DATABASE_VERSION @@ -2,11 +2,11 @@ EPICS_DATABASE_MAJOR_VERSION = 3 EPICS_DATABASE_MINOR_VERSION = 19 -EPICS_DATABASE_MAINTENANCE_VERSION = 0 +EPICS_DATABASE_MAINTENANCE_VERSION = 1 # Development flag, set to zero for release versions -EPICS_DATABASE_DEVELOPMENT_FLAG = 0 +EPICS_DATABASE_DEVELOPMENT_FLAG = 1 # Immediately after a release the MAINTENANCE_VERSION # will be incremented and the DEVELOPMENT_FLAG set to 1 diff --git a/configure/CONFIG_LIBCOM_VERSION b/configure/CONFIG_LIBCOM_VERSION index 27dc8d943..96f25ebaa 100644 --- a/configure/CONFIG_LIBCOM_VERSION +++ b/configure/CONFIG_LIBCOM_VERSION @@ -2,11 +2,11 @@ EPICS_LIBCOM_MAJOR_VERSION = 3 EPICS_LIBCOM_MINOR_VERSION = 19 -EPICS_LIBCOM_MAINTENANCE_VERSION = 0 +EPICS_LIBCOM_MAINTENANCE_VERSION = 1 # Development flag, set to zero for release versions -EPICS_LIBCOM_DEVELOPMENT_FLAG = 0 +EPICS_LIBCOM_DEVELOPMENT_FLAG = 1 # Immediately after a release the MAINTENANCE_VERSION # will be incremented and the DEVELOPMENT_FLAG set to 1 diff --git a/configure/RULES.Db b/configure/RULES.Db index 42a57430c..406c1eb16 100644 --- a/configure/RULES.Db +++ b/configure/RULES.Db @@ -196,12 +196,12 @@ endif #--------------------------------------------------------------- # build dependancies, clean rule -inc: $(COMMON_INC) $(INSTALL_INC) +inc: $(COMMON_INC) $(INSTALL_INC) $(COMMON_DBDS) $(COMMON_DBDCATS) \ + $(INSTALL_DBDS) $(INSTALL_DBD_INSTALLS) -build: $(COMMON_DBDS) $(COMMON_DBS) $(COMMON_DBDCATS) \ - $(INSTALL_DBDS) $(INSTALL_DBS) \ +build: $(COMMON_DBS) $(INSTALL_DBS) \ $(DBDDEPENDS_FILES) $(TARGETS) \ - $(INSTALL_DB_INSTALLS) $(INSTALL_DBD_INSTALLS) + $(INSTALL_DB_INSTALLS) clean: db_clean diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index e4236f378..a2cb624fd 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -120,17 +120,20 @@ endif #--------------------------------------------------------------- # Include defines and rules for prod, library and test* targets -#ifneq (,$(strip $(PROD) $(TESTPROD) $(LIBRARY) $(TESTLIBRARY) $(LOADABLE_LIBRARY) )) +ifneq (,$(strip $(PROD) $(TESTPROD) $(LIBRARY) $(TESTLIBRARY) \ + $(LOADABLE_LIBRARY))) include $(CONFIG)/RULES_TARGET -#endif +endif #--------------------------------------------------------------- # Read dependency files +ifneq (inc,$(strip $(MAKECMDGOALS))) ifneq (,$(strip $(HDEPENDS_FILES))) $(filter-out $(wildcard *$(DEP)), $(HDEPENDS_FILES)): | $(COMMON_INC) -include $(HDEPENDS_FILES) endif +endif #--------------------------------------------------------------- # Products and Object libraries @@ -178,12 +181,13 @@ build: inc build: $(OBJSNAME) $(LIBTARGETS) $(PRODTARGETS) $(TESTPRODTARGETS) \ $(TARGETS) $(TESTSCRIPTS) $(INSTALL_LIB_INSTALLS) -inc: $(COMMON_INC) $(INSTALL_INC) $(INSTALL_CONFIGS) +inc: $(COMMON_INC) $(INSTALL_INC) $(INSTALL_CONFIGS) $(INSTALLS_CFG) \ + $(INSTALL_HTMLS) $(INSTALLS_PERL_MODULES) $(INSTALL_SCRIPTS) buildInstall: \ - $(INSTALL_SCRIPTS) $(INSTALL_PROD) $(INSTALL_MUNCHS) \ + $(INSTALL_PROD) $(INSTALL_MUNCHS) \ $(INSTALL_TCLLIBS) $(INSTALL_TCLINDEX) \ - $(INSTALL_HTMLS) $(INSTALL_DOCS) \ + $(INSTALL_DOCS) \ $(INSTALL_OBJS) \ $(INSTALL_TEMPLATE) \ $(INSTALL_BIN_INSTALLS) diff --git a/configure/RULES_TARGET b/configure/RULES_TARGET index 5d77b105e..06f8a447d 100644 --- a/configure/RULES_TARGET +++ b/configure/RULES_TARGET @@ -21,7 +21,6 @@ $(foreach target, $(PROD) $(TESTPROD) $(LIBRARY) $(TESTLIBRARY) $(LOADABLE_LIBRA #----------------------------------------------------------------------- -# This define block requires GNU make 3.81 define PROD_template ifeq ($$(strip $$($(1)_OBJS) $$($(1)_SRCS) $$(PRODUCT_OBJS)),) $(1)_OBJS = $(1)$$(OBJ) diff --git a/configure/RULES_TOP b/configure/RULES_TOP index ec0597a49..60b52e0de 100644 --- a/configure/RULES_TOP +++ b/configure/RULES_TOP @@ -73,7 +73,7 @@ help: @echo "Usage: gnumake [options] [target] ..." @echo "Targets supported by all Makefiles:" @echo " all - Same as install (default rule)" - @echo " inc - Installs header files" + @echo " inc - Installs header, dbd and html files" @echo " build - Builds and installs all targets" @echo " install - Builds and installs all targets" @echo " buildInstall - Same as install (deprecated)" diff --git a/documentation/Doxyfile@ b/documentation/Doxyfile@ index 482564264..bf53ee0ce 100644 --- a/documentation/Doxyfile@ +++ b/documentation/Doxyfile@ @@ -107,7 +107,7 @@ BRIEF_MEMBER_DESC = YES # brief descriptions will be completely suppressed. # The default value is: YES. -REPEAT_BRIEF = YES +REPEAT_BRIEF = NO # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found @@ -143,7 +143,7 @@ ALWAYS_DETAILED_SEC = NO # operators of the base classes will not be shown. # The default value is: NO. -INLINE_INHERITED_MEMB = NO +INLINE_INHERITED_MEMB = YES # If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the @@ -162,8 +162,8 @@ FULL_PATH_NAMES = YES # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = \ - ../ +STRIP_FROM_PATH = @TOP@/include \ + .. # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -172,7 +172,8 @@ STRIP_FROM_PATH = \ # specify the list of include paths that are normally passed to the compiler # using the -I flag. -STRIP_FROM_INC_PATH = ../ +STRIP_FROM_INC_PATH = @TOP@/include \ + .. # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't @@ -227,7 +228,7 @@ SEPARATE_MEMBER_PAGES = NO # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. -TAB_SIZE = 4 +TAB_SIZE = 8 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: @@ -241,12 +242,6 @@ TAB_SIZE = 4 ALIASES = -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all @@ -420,7 +415,7 @@ LOOKUP_CACHE_SIZE = 0 # normally produced when WARNINGS is set to YES. # The default value is: NO. -EXTRACT_ALL = YES +EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will # be included in the documentation. @@ -548,7 +543,7 @@ INLINE_INFO = YES # name. If set to NO the members will appear in declaration order. # The default value is: YES. -SORT_MEMBER_DOCS = YES +SORT_MEMBER_DOCS = NO # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member @@ -767,7 +762,8 @@ WARN_LOGFILE = INPUT = ../mainpage.dox \ ../RELEASE_NOTES.md \ ../README.md \ - ../RecordReference.md + ../RecordReference.md \ + @TOP@/include # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -787,48 +783,10 @@ INPUT_ENCODING = UTF-8 # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. -FILE_PATTERNS = *.c \ - *.cc \ - *.cxx \ - *.cpp \ - *.c++ \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ - *.inl \ - *.idl \ - *.ddl \ - *.odl \ - *.h \ - *.hh \ - *.hxx \ +FILE_PATTERNS = *.h \ *.hpp \ - *.h++ \ - *.cs \ - *.d \ - *.php \ - *.php4 \ - *.php5 \ - *.phtml \ - *.inc \ - *.m \ - *.markdown \ *.md \ - *.mm \ - *.dox \ - *.py \ - *.f90 \ - *.f \ - *.for \ - *.tcl \ - *.vhd \ - *.vhdl \ - *.ucf \ - *.qsf \ - *.as \ - *.js + *.dox # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -843,7 +801,8 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = @TOP@/include/pv \ + @TOP@/include/pva # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -859,7 +818,7 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = O.* +EXCLUDE_PATTERNS = /O.*/ # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the @@ -2028,7 +1987,7 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = +PREDEFINED = __cplusplus # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The @@ -2047,7 +2006,7 @@ EXPAND_AS_DEFINED = # The default value is: YES. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -SKIP_FUNCTION_MACROS = YES +SKIP_FUNCTION_MACROS = NO #--------------------------------------------------------------------------- # Configuration options related to external references @@ -2094,12 +2053,6 @@ EXTERNAL_GROUPS = YES EXTERNAL_PAGES = YES -# The PERL_PATH should be the absolute path and name of the perl script -# interpreter (i.e. the result of 'which perl'). -# The default file (with absolute path) is: /usr/bin/perl. - -PERL_PATH = /usr/bin/perl - #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- @@ -2113,15 +2066,6 @@ PERL_PATH = /usr/bin/perl CLASS_DIAGRAMS = YES -# You can define message sequence charts within doxygen comments using the \msc -# command. Doxygen will then run the mscgen tool (see: -# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the -# documentation. The MSCGEN_PATH tag allows you to specify the directory where -# the mscgen tool resides. If left empty the tool is assumed to be found in the -# default search path. - -MSCGEN_PATH = - # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. diff --git a/documentation/Makefile b/documentation/Makefile index 8148687cb..bd527e7db 100644 --- a/documentation/Makefile +++ b/documentation/Makefile @@ -1,22 +1,24 @@ TOP = .. include $(TOP)/configure/CONFIG -ifdef T_A +ifeq ($(T_A),$(EPICS_HOST_ARCH)) DOXYGEN=doxygen -EXPAND = Doxyfile +EXPAND = Doxyfile@ EXPAND_ME += EPICS_VERSION EXPAND_ME += EPICS_REVISION EXPAND_ME += EPICS_MODIFICATION EXPAND_ME += EPICS_PATCH_LEVEL +EXPAND_ME += OS_CLASS CMPLR_CLASS ME = documentation/O.$(T_A)/html +GH_FILES = $(ME)/ $(ME)/.nojekyll $(ME)/*.* $(ME)/*/*.* install: doxygen -doxygen: Doxyfile +doxygen: Doxyfile ../mainpage.dox $(DOXYGEN) rsync -av $(TOP)/html/ html/ @@ -24,10 +26,10 @@ doxygen: Doxyfile commit: doxygen $(TOUCH) html/.nojekyll - (cd $(TOP) && $(CURDIR)/../commit-gh.sh $(ME)/ $(ME)/.nojekyll $(ME)/*.* $(ME)/*/*.*) + (cd $(TOP) && $(CURDIR)/../commit-gh.sh $(GH_FILES)) .PHONY: commit -endif # T_A +endif # EPICS_HOST_ARCH include $(TOP)/configure/RULES diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index dfb93e613..49cdd74ef 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -11,6 +11,15 @@ 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. +**This version of EPICS has not been released yet.** + +## Changes made on the 7.0 branch since 7.0.5 + + + + +----- + ## EPICS Release 7.0.5 ### Fix aai's Device Support Initialization @@ -448,7 +457,7 @@ work with older Base releases. This would also be a good time to modify the device support to use the type-safe device support entry tables that were introduced in Base-3.16.2 -- see -[#type-safe-device-and-driver-support-tables](this entry below) for the +[this entry below](#type-safe-device-and-driver-support-tables) for the description of that change, which is also optional for now. Look at the aiRecord for example. Near the top of the generated `aiRecord.h` diff --git a/documentation/mainpage.dox b/documentation/mainpage.dox index f13fdcf97..472f1d999 100644 --- a/documentation/mainpage.dox +++ b/documentation/mainpage.dox @@ -3,7 +3,6 @@ Documentation index -@ul @li @ref releasenotes @li @ref install @li @ref recordrefmanual diff --git a/modules/ca/src/perl/Makefile b/modules/ca/src/perl/Makefile index 532780d4c..05500ad05 100644 --- a/modules/ca/src/perl/Makefile +++ b/modules/ca/src/perl/Makefile @@ -38,6 +38,7 @@ ifeq ($(wildcard $(PERL_h)),) endif endif +ifneq (inc,$(strip $(MAKECMDGOALS))) ifeq ($(T_A),$(EPICS_HOST_ARCH)) # No cross-builds (wrong Perl!) ifeq ($(strip $(XSUBPP)),) $(warning Perl's xsubpp program was not found.) @@ -62,6 +63,7 @@ endif endif endif endif +endif Cap5_SRCS = Cap5.xs Cap5_LIBS = ca Com diff --git a/modules/database/src/ioc/as/asIocRegister.c b/modules/database/src/ioc/as/asIocRegister.c index 200da3e00..8efb4c9eb 100644 --- a/modules/database/src/ioc/as/asIocRegister.c +++ b/modules/database/src/ioc/as/asIocRegister.c @@ -120,7 +120,9 @@ static void ascarCallFunc(const iocshArgBuf *args) } /* asDumpHash */ -static const iocshFuncDef asDumpHashFuncDef = {"asDumpHash",0,0}; +static const iocshFuncDef asDumpHashFuncDef = {"asDumpHash",0,0, + "Show the contents of the hash table used " + "to locate UAGs and HAGs.\n"}; static void asDumpHashCallFunc(const iocshArgBuf *args) { asDumpHash(); diff --git a/modules/database/src/ioc/bpt/Makefile b/modules/database/src/ioc/bpt/Makefile index ea1db1f14..a383d0864 100644 --- a/modules/database/src/ioc/bpt/Makefile +++ b/modules/database/src/ioc/bpt/Makefile @@ -19,7 +19,10 @@ BPT_DBD += bptTypeJdegC.dbd BPT_DBD += bptTypeJdegF.dbd BPT_DBD += bptTypeKdegC.dbd BPT_DBD += bptTypeKdegF.dbd + +ifneq (inc,$(strip $(MAKECMDGOALS))) DBD += $(BPT_DBD) +endif PROD_HOST += makeBpt diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index d7e5d0890..bac208f02 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -339,7 +339,7 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options, dbCommon *pcommon; char *pbuffer = *poriginal; - if (!pfl || pfl->type == dbfl_type_rec) + if (!pfl) field_type = paddr->field_type; else field_type = pfl->field_type; @@ -349,7 +349,7 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options, if( (*options) & DBR_STATUS ) { unsigned short *pushort = (unsigned short *)pbuffer; - if (!pfl || pfl->type == dbfl_type_rec) { + if (!pfl) { *pushort++ = pcommon->stat; *pushort++ = pcommon->sevr; } else { @@ -383,7 +383,7 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options, if( (*options) & DBR_TIME ) { epicsUInt32 *ptime = (epicsUInt32 *)pbuffer; - if (!pfl || pfl->type == dbfl_type_rec) { + if (!pfl) { *ptime++ = pcommon->time.secPastEpoch; *ptime++ = pcommon->time.nsec; } else { @@ -904,22 +904,23 @@ long dbGet(DBADDR *paddr, short dbrType, if (nRequest && *nRequest == 0) return 0; - if (!pfl || pfl->type == dbfl_type_rec) { + if (!pfl) { field_type = paddr->field_type; no_elements = capacity = paddr->no_elements; - - /* Update field info from record - * may modify paddr->pfield - */ - if (paddr->pfldDes->special == SPC_DBADDR && - (prset = dbGetRset(paddr)) && - prset->get_array_info) { - status = prset->get_array_info(paddr, &no_elements, &offset); - } else - offset = 0; } else { field_type = pfl->field_type; no_elements = capacity = pfl->no_elements; + } + + /* Update field info from record (if neccessary); + * may modify paddr->pfield. + */ + if (!dbfl_has_copy(pfl) && + paddr->pfldDes->special == SPC_DBADDR && + (prset = dbGetRset(paddr)) && + prset->get_array_info) { + status = prset->get_array_info(paddr, &no_elements, &offset); + } else { offset = 0; } @@ -951,7 +952,7 @@ long dbGet(DBADDR *paddr, short dbrType, goto done; } - if (!pfl || pfl->type == dbfl_type_rec) { + if (!dbfl_has_copy(pfl)) { status = dbFastGetConvertRoutine[field_type][dbrType] (paddr->pfield, pbuf, paddr); } else { @@ -964,11 +965,9 @@ long dbGet(DBADDR *paddr, short dbrType, localAddr.field_type = pfl->field_type; localAddr.field_size = pfl->field_size; + /* not used by dbFastConvert: */ localAddr.no_elements = pfl->no_elements; - if (pfl->type == dbfl_type_val) - localAddr.pfield = (char *) &pfl->u.v.field; - else - localAddr.pfield = (char *) pfl->u.r.field; + localAddr.pfield = dbfl_pfield(pfl); status = dbFastGetConvertRoutine[field_type][dbrType] (localAddr.pfield, pbuf, &localAddr); } @@ -979,6 +978,8 @@ long dbGet(DBADDR *paddr, short dbrType, if (nRequest) { if (no_elements < *nRequest) *nRequest = no_elements; + if (capacity < *nRequest) + *nRequest = capacity; n = *nRequest; } else { n = 1; @@ -995,8 +996,8 @@ long dbGet(DBADDR *paddr, short dbrType, } /* convert data into the caller's buffer */ if (n <= 0) { - ;/*do nothing*/ - } else if (!pfl || pfl->type == dbfl_type_rec) { + ; /*do nothing */ + } else if (!dbfl_has_copy(pfl)) { status = convert(paddr, pbuf, n, capacity, offset); } else { DBADDR localAddr = *paddr; /* Structure copy */ @@ -1008,11 +1009,9 @@ long dbGet(DBADDR *paddr, short dbrType, localAddr.field_type = pfl->field_type; localAddr.field_size = pfl->field_size; + /* not used by dbConvert, it uses the passed capacity instead: */ localAddr.no_elements = pfl->no_elements; - if (pfl->type == dbfl_type_val) - localAddr.pfield = (char *) &pfl->u.v.field; - else - localAddr.pfield = (char *) pfl->u.r.field; + localAddr.pfield = dbfl_pfield(pfl); status = convert(&localAddr, pbuf, n, capacity, offset); } diff --git a/modules/database/src/ioc/db/dbChannel.c b/modules/database/src/ioc/db/dbChannel.c index b6f53f797..c71d8426f 100644 --- a/modules/database/src/ioc/db/dbChannel.c +++ b/modules/database/src/ioc/db/dbChannel.c @@ -52,14 +52,12 @@ typedef struct parseContext { static void *dbChannelFreeList; static void *chFilterFreeList; -static void *dbchStringFreeList; void dbChannelExit(void) { freeListCleanup(dbChannelFreeList); freeListCleanup(chFilterFreeList); - freeListCleanup(dbchStringFreeList); - dbChannelFreeList = chFilterFreeList = dbchStringFreeList = NULL; + dbChannelFreeList = chFilterFreeList = NULL; } void dbChannelInit (void) @@ -69,7 +67,6 @@ void dbChannelInit (void) freeListInitPvt(&dbChannelFreeList, sizeof(dbChannel), 128); freeListInitPvt(&chFilterFreeList, sizeof(chFilter), 64); - freeListInitPvt(&dbchStringFreeList, sizeof(epicsOldString), 128); db_init_event_freelists(); } @@ -449,28 +446,6 @@ static long parseArrayRange(dbChannel* chan, const char *pname, const char **ppn return status; } -/* Stolen from dbAccess.c: */ -static short mapDBFToDBR[DBF_NTYPES] = - { - /* DBF_STRING => */DBR_STRING, - /* DBF_CHAR => */DBR_CHAR, - /* DBF_UCHAR => */DBR_UCHAR, - /* DBF_SHORT => */DBR_SHORT, - /* DBF_USHORT => */DBR_USHORT, - /* DBF_LONG => */DBR_LONG, - /* DBF_ULONG => */DBR_ULONG, - /* DBF_INT64 => */DBR_INT64, - /* DBF_UINT64 => */DBR_UINT64, - /* DBF_FLOAT => */DBR_FLOAT, - /* DBF_DOUBLE => */DBR_DOUBLE, - /* DBF_ENUM, => */DBR_ENUM, - /* DBF_MENU, => */DBR_ENUM, - /* DBF_DEVICE => */DBR_ENUM, - /* DBF_INLINK => */DBR_STRING, - /* DBF_OUTLINK => */DBR_STRING, - /* DBF_FWDLINK => */DBR_STRING, - /* DBF_NOACCESS => */DBR_NOACCESS }; - dbChannel * dbChannelCreate(const char *name) { const char *pname = name; @@ -743,37 +718,24 @@ void dbChannelDelete(dbChannel *chan) freeListFree(dbChannelFreeList, chan); } -static void freeArray(db_field_log *pfl) { - if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) { - freeListFree(dbchStringFreeList, pfl->u.r.field); - } else { - free(pfl->u.r.field); - } -} - -void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan) +/* + * Helper function to adjust no_elements, offset, and pfield + * when copying an array from a record. + */ +void dbChannelGetArrayInfo(dbChannel *chan, + void **pfield, long *no_elements, long *offset) { - void *p; - struct dbCommon *prec = dbChannelRecord(chan); - - if (pfl->type != dbfl_type_rec) return; - - pfl->type = dbfl_type_ref; - pfl->stat = prec->stat; - pfl->sevr = prec->sevr; - pfl->time = prec->time; - pfl->field_type = chan->addr.field_type; - pfl->no_elements = chan->addr.no_elements; - pfl->field_size = chan->addr.field_size; - pfl->u.r.dtor = freeArray; - pfl->u.r.pvt = pvt; - if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) { - p = freeListCalloc(dbchStringFreeList); - } else { - p = calloc(pfl->no_elements, pfl->field_size); + rset *prset; + if (dbChannelSpecial(chan) == SPC_DBADDR && + (prset = dbGetRset(&chan->addr)) && + prset->get_array_info) + { + void *pfieldsave = dbChannelField(chan); + /* it is expected that this call always succeeds */ + prset->get_array_info(&chan->addr, no_elements, offset); + *pfield = dbChannelField(chan); + dbChannelField(chan) = pfieldsave; } - if (p) dbGet(&chan->addr, mapDBFToDBR[pfl->field_type], p, NULL, &pfl->no_elements, NULL); - pfl->u.r.field = p; } /* FIXME: Do these belong in a different file? */ diff --git a/modules/database/src/ioc/db/dbChannel.h b/modules/database/src/ioc/db/dbChannel.h index 1ca02bbb6..ec86e9e28 100644 --- a/modules/database/src/ioc/db/dbChannel.h +++ b/modules/database/src/ioc/db/dbChannel.h @@ -65,8 +65,8 @@ typedef struct dbChannel { /* Prototype for the channel event function that is called in filter stacks * * When invoked the scan lock for the record associated with 'chan' _may_ be locked. - * If pLog->type==dbfl_type_rec then dbScanLock() must be called before copying - * data out of the associated record. + * Unless dbfl_has_copy(pLog), it must call dbScanLock before accessing the data, + * as this indicates the data is still owned by the record. * * This function has ownership of the field log pLog, if it wishes to discard * this update it should free the field log with db_delete_field_log() and @@ -225,7 +225,8 @@ DBCORE_API void dbRegisterFilter(const char *key, const chFilterIf *fif, void *p DBCORE_API db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn); DBCORE_API db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn); DBCORE_API const chFilterPlugin * dbFindFilter(const char *key, size_t len); -DBCORE_API void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan); +DBCORE_API void dbChannelGetArrayInfo(dbChannel *chan, + void **pfield, long *no_elements, long *offset); #ifdef __cplusplus } diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c index 437cb6d5d..1db01f2cd 100644 --- a/modules/database/src/ioc/db/dbEvent.c +++ b/modules/database/src/ioc/db/dbEvent.c @@ -668,27 +668,21 @@ int db_post_extra_labor (dbEventCtx ctx) return DB_EVENT_OK; } -/* - * DB_CREATE_EVENT_LOG() - * - * NOTE: This assumes that the db scan lock is already applied - * (as it copies data from the record) - */ -db_field_log* db_create_event_log (struct evSubscrip *pevent) +static db_field_log* db_create_field_log (struct dbChannel *chan, int use_val) { db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList); if (pLog) { - struct dbChannel *chan = pevent->chan; struct dbCommon *prec = dbChannelRecord(chan); - pLog->ctx = dbfl_context_event; - if (pevent->useValque) { + pLog->stat = prec->stat; + pLog->sevr = prec->sevr; + pLog->time = prec->time; + pLog->field_type = dbChannelFieldType(chan); + pLog->field_size = dbChannelFieldSize(chan); + pLog->no_elements = dbChannelElements(chan); + + if (use_val) { pLog->type = dbfl_type_val; - pLog->stat = prec->stat; - pLog->sevr = prec->sevr; - pLog->time = prec->time; - pLog->field_type = dbChannelFieldType(chan); - pLog->no_elements = dbChannelElements(chan); /* * use memcpy to avoid a bus error on * union copy of char in the db at an odd @@ -698,23 +692,46 @@ db_field_log* db_create_event_log (struct evSubscrip *pevent) dbChannelField(chan), dbChannelFieldSize(chan)); } else { - pLog->type = dbfl_type_rec; + pLog->type = dbfl_type_ref; + + /* don't make a copy yet, just reference the field value */ + pLog->u.r.field = dbChannelField(chan); + /* indicate field value still owned by record */ + pLog->u.r.dtor = NULL; + /* no private data yet, may be set by a filter */ + pLog->u.r.pvt = NULL; } } return pLog; } +/* + * DB_CREATE_EVENT_LOG() + * + * NOTE: This assumes that the db scan lock is already applied + * (as it calls rset->get_array_info) + */ +db_field_log* db_create_event_log (struct evSubscrip *pevent) +{ + db_field_log *pLog = db_create_field_log(pevent->chan, pevent->useValque); + if (pLog) { + pLog->ctx = dbfl_context_event; + } + return pLog; +} + /* * DB_CREATE_READ_LOG() * */ db_field_log* db_create_read_log (struct dbChannel *chan) { - db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList); - + db_field_log *pLog = db_create_field_log(chan, + dbChannelElements(chan) == 1 && + dbChannelSpecial(chan) != SPC_DBADDR && + dbChannelFieldSize(chan) <= sizeof(union native_value)); if (pLog) { pLog->ctx = dbfl_context_read; - pLog->type = dbfl_type_rec; } return pLog; } @@ -737,20 +754,6 @@ static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog) LOCKEVQUE (ev_que); - /* - * if we have an event on the queue and both the last - * event on the queue and the current event are emtpy - * (i.e. of type dbfl_type_rec), simply ignore duplicate - * events (saving empty events serves no purpose) - */ - if (pevent->npend > 0u && - (*pevent->pLastLog)->type == dbfl_type_rec && - pLog->type == dbfl_type_rec) { - db_delete_field_log(pLog); - UNLOCKEVQUE (ev_que); - return; - } - /* * add to task local event que */ diff --git a/modules/database/src/ioc/db/dbExtractArray.c b/modules/database/src/ioc/db/dbExtractArray.c index a7dcf4d0c..197e66c3a 100644 --- a/modules/database/src/ioc/db/dbExtractArray.c +++ b/modules/database/src/ioc/db/dbExtractArray.c @@ -14,11 +14,12 @@ /* * Author: Ralph Lange * - * based on dbConvert.c + * based on dbConvert.c, see copyNoConvert * written by: Bob Dalesio, Marty Kraimer */ #include +#include #include "epicsTypes.h" @@ -26,61 +27,30 @@ #include "dbAddr.h" #include "dbExtractArray.h" -void dbExtractArrayFromRec(const dbAddr *paddr, void *pto, - long nRequest, long no_elements, long offset, long increment) +void dbExtractArray(const void *pfrom, void *pto, short field_size, + long nRequest, long no_elements, long offset, long increment) { char *pdst = (char *) pto; - char *psrc = (char *) paddr->pfield; - long nUpperPart; - int i; - short srcSize = paddr->field_size; - short dstSize = srcSize; - char isString = (paddr->field_type == DBF_STRING); + const char *psrc = (char *) pfrom; - if (nRequest > no_elements) nRequest = no_elements; - if (isString && srcSize > MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE; - - if (increment == 1 && dstSize == srcSize) { - nUpperPart = nRequest < no_elements - offset ? nRequest : no_elements - offset; - memcpy(pdst, &psrc[offset * srcSize], dstSize * nUpperPart); - if (nRequest > nUpperPart) - memcpy(&pdst[dstSize * nUpperPart], psrc, dstSize * (nRequest - nUpperPart)); - if (isString) - for (i = 1; i <= nRequest; i++) - pdst[dstSize*i-1] = '\0'; - } else { - for (; nRequest > 0; nRequest--, pdst += dstSize, offset += increment) { - offset %= no_elements; - memcpy(pdst, &psrc[offset*srcSize], dstSize); - if (isString) pdst[dstSize-1] = '\0'; - } - } -} - -void dbExtractArrayFromBuf(const void *pfrom, void *pto, - short field_size, short field_type, - long nRequest, long no_elements, long offset, long increment) -{ - char *pdst = (char *) pto; - char *psrc = (char *) pfrom; - int i; - short srcSize = field_size; - short dstSize = srcSize; - char isString = (field_type == DBF_STRING); - - if (nRequest > no_elements) nRequest = no_elements; - if (offset > no_elements - 1) offset = no_elements - 1; - if (isString && dstSize >= MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE - 1; + /* assert preconditions */ + assert(nRequest >= 0); + assert(no_elements >= 0); + assert(increment > 0); + assert(0 <= offset); + assert(offset < no_elements); if (increment == 1) { - memcpy(pdst, &psrc[offset * srcSize], dstSize * nRequest); - if (isString) - for (i = 1; i <= nRequest; i++) - pdst[dstSize*i] = '\0'; + long nUpperPart = + nRequest < no_elements - offset ? nRequest : no_elements - offset; + memcpy(pdst, psrc + (offset * field_size), field_size * nUpperPart); + if (nRequest > nUpperPart) + memcpy(pdst + (field_size * nUpperPart), psrc, + field_size * (nRequest - nUpperPart)); } else { - for (; nRequest > 0; nRequest--, pdst += srcSize, offset += increment) { - memcpy(pdst, &psrc[offset*srcSize], dstSize); - if (isString) pdst[dstSize] = '\0'; + for (; nRequest > 0; nRequest--, pdst += field_size, offset += increment) { + offset %= no_elements; + memcpy(pdst, psrc + (offset * field_size), field_size); } } } diff --git a/modules/database/src/ioc/db/dbExtractArray.h b/modules/database/src/ioc/db/dbExtractArray.h index b44bd15fc..9755ac570 100644 --- a/modules/database/src/ioc/db/dbExtractArray.h +++ b/modules/database/src/ioc/db/dbExtractArray.h @@ -22,11 +22,36 @@ extern "C" { #endif -epicsShareFunc void dbExtractArrayFromRec(const DBADDR *paddr, void *pto, - long nRequest, long no_elements, long offset, long increment); -epicsShareFunc void dbExtractArrayFromBuf(const void *pfrom, void *pto, - short field_size, short field_type, - long nRequest, long no_elements, long offset, long increment); +/** @brief Make a copy of parts of an array. + * + * The source array may or may not be a record field. + * + * The increment parameter is used to support array filters; it + * means: copy only every increment'th element, starting at offset. + * + * The offset and no_elements parameters are used to support the + * circular buffer feature of record fields: elements before offset + * are treated as if they came right after no_elements. + * + * This function does not do any conversion on the array elements. + * + * Preconditions: + * nRequest >= 0, no_elements >= 0, increment > 0 + * 0 <= offset < no_elements + * pto points to a buffer with at least field_size*nRequest bytes + * pfrom points to a buffer with exactly field_size*no_elements bytes + * + * @param pfrom Pointer to source array. + * @param pto Pointer to target array. + * @param field_size Size of an array element. + * @param nRequest Number of elements to copy. + * @param no_elements Number of elements in source array. + * @param offset Wrap-around point in source array. + * @param increment Copy only every increment'th element. + */ +epicsShareFunc void dbExtractArray(const void *pfrom, void *pto, + short field_size, long nRequest, long no_elements, long offset, + long increment); #ifdef __cplusplus } diff --git a/modules/database/src/ioc/db/dbUnitTest.c b/modules/database/src/ioc/db/dbUnitTest.c index 65c817bbb..0b40d929c 100644 --- a/modules/database/src/ioc/db/dbUnitTest.c +++ b/modules/database/src/ioc/db/dbUnitTest.c @@ -112,12 +112,13 @@ union anybuf { long testdbVPutField(const char* pv, short dbrType, va_list ap) { - DBADDR addr; + dbChannel *chan = dbChannelCreate(pv); union anybuf pod; + long ret = S_dbLib_recNotFound; - if (dbNameToAddr(pv, &addr)) { - testFail("Missing PV \"%s\"", pv); - return S_dbLib_recNotFound; + if(!chan || (ret=dbChannelOpen(chan))) { + testFail("Channel error (%p, %ld) : %s", chan, ret, pv); + goto done; } switch(dbrType) { @@ -125,14 +126,18 @@ long testdbVPutField(const char* pv, short dbrType, va_list ap) const char *uarg = va_arg(ap,char*); strncpy(pod.valStr, uarg, sizeof(pod.valStr)); pod.valStr[sizeof(pod.valStr)-1] = '\0'; - return dbPutField(&addr, dbrType, pod.valStr, 1); + ret = dbChannelPutField(chan, dbrType, pod.valStr, 1); + break; } /* The Type parameter takes into consideration * the C language rules for promotion of argument types * in variadic functions. */ -#define OP(DBR,Type,mem) case DBR: {pod.val.mem = va_arg(ap,Type); break;} +#define OP(DBR,Type,mem) case DBR: \ + pod.val.mem = va_arg(ap,Type); \ + ret = dbChannelPutField(chan, dbrType, pod.bytes, 1); \ + break; OP(DBR_CHAR, int, int8); OP(DBR_UCHAR, int, uInt8); OP(DBR_SHORT, int, int16); @@ -147,11 +152,15 @@ long testdbVPutField(const char* pv, short dbrType, va_list ap) #undef OP default: testFail("invalid DBR: dbPutField(\"%s\", %d, ...)", - addr.precord->name, dbrType); - return S_db_badDbrtype; + dbChannelName(chan), dbrType); + ret = S_db_badDbrtype; + break; } - return dbPutField(&addr, dbrType, pod.bytes, 1); +done: + if(chan) + dbChannelDelete(chan); + return ret; } void testdbPutFieldOk(const char* pv, int dbrType, ...) @@ -190,23 +199,35 @@ void testdbGetFieldEqual(const char* pv, int dbrType, ...) void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap) { - DBADDR addr; + dbChannel *chan = dbChannelCreate(pv); + db_field_log *pfl = NULL; long nReq = 1; union anybuf pod; - long status; + long status = S_dbLib_recNotFound; - if(dbNameToAddr(pv, &addr)) { - testFail("Missing PV \"%s\"", pv); - return; + if(!chan || (status=dbChannelOpen(chan))) { + testFail("Channel error (%p, %ld) : %s", chan, status, pv); + goto done; } - status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq, NULL); + if(ellCount(&chan->filters)) { + pfl = db_create_read_log(chan); + if (!pfl) { + testFail("can't db_create_read_log w/ %s", pv); + goto done; + } + + pfl = dbChannelRunPreChain(chan, pfl); + pfl = dbChannelRunPostChain(chan, pfl); + } + + status = dbChannelGetField(chan, dbrType, pod.bytes, NULL, &nReq, pfl); if (status) { testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status)); - return; + goto done; } else if(nReq==0) { testFail("dbGetField(\"%s\", %d, ...) -> zero length", pv, dbrType); - return; + goto done; } switch(dbrType) { @@ -236,35 +257,56 @@ void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap) default: testFail("dbGetField(\"%s\", %d) -> unsupported dbf", pv, dbrType); } + +done: + db_delete_field_log(pfl); + if(chan) + dbChannelDelete(chan); } void testdbPutArrFieldOk(const char* pv, short dbrType, unsigned long count, const void *pbuf) { - DBADDR addr; + dbChannel *chan = dbChannelCreate(pv); long status; - if (dbNameToAddr(pv, &addr)) { - testFail("Missing PV \"%s\"", pv); - return; + if(!chan || (status=dbChannelOpen(chan))) { + testFail("Channel error (%p, %ld) : %s", chan, status, pv); + goto done; } - status = dbPutField(&addr, dbrType, pbuf, count); + status = dbChannelPutField(chan, dbrType, pbuf, count); testOk(status==0, "dbPutField(\"%s\", dbr=%d, count=%lu, ...) -> %ld", pv, dbrType, count, status); + +done: + if(chan) + dbChannelDelete(chan); } void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsigned long cnt, const void *pbufraw) { - DBADDR addr; + dbChannel *chan = dbChannelCreate(pv); + db_field_log *pfl = NULL; const long vSize = dbValueSize(dbfType); const long nStore = vSize * nRequest; - long status; + long status = S_dbLib_recNotFound; char *gbuf, *gstore; const char *pbuf = pbufraw; - if(dbNameToAddr(pv, &addr)) { - testFail("Missing PV \"%s\"", pv); - return; + if(!chan || (status=dbChannelOpen(chan))) { + testFail("Channel error (%p, %ld) : %s", chan, status, pv); + goto done; + } + + if(ellCount(&chan->filters)) { + pfl = db_create_read_log(chan); + if (!pfl) { + testFail("can't db_create_read_log w/ %s", pv); + goto done; + } + + pfl = dbChannelRunPreChain(chan, pfl); + pfl = dbChannelRunPostChain(chan, pfl); } gbuf = gstore = malloc(nStore); @@ -273,7 +315,7 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign return; } - status = dbGetField(&addr, dbfType, gbuf, NULL, &nRequest, NULL); + status = dbChannelGetField(chan, dbfType, gbuf, NULL, &nRequest, pfl); if (status) { testFail("dbGetField(\"%s\", %d, ...) -> %#lx", pv, dbfType, status); @@ -318,7 +360,10 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign testOk(match, "dbGetField(\"%s\", dbrType=%d, nRequest=%ld ...) match", pv, dbfType, nRequest); } +done: free(gstore); + if(chan) + dbChannelDelete(chan); } dbCommon* testdbRecordPtr(const char* pv) diff --git a/modules/database/src/ioc/db/db_field_log.h b/modules/database/src/ioc/db/db_field_log.h index 5a40f5fad..e517d529f 100644 --- a/modules/database/src/ioc/db/db_field_log.h +++ b/modules/database/src/ioc/db/db_field_log.h @@ -57,20 +57,31 @@ union native_value { struct db_field_log; typedef void (dbfl_freeFunc)(struct db_field_log *pfl); -/* Types of db_field_log: rec = use record, val = val inside, ref = reference inside */ +/* + * A db_field_log has one of two types: + * + * dbfl_type_ref - Reference to value + * Used for variable size (array) data types. Meta-data + * is stored in the field log, but value data is stored externally. + * Only the dbfl_ref side of the data union is valid. + * + * dbfl_type_val - Internal value + * Used to store small scalar data. Meta-data and value are + * present in this structure and no external references are used. + * Only the dbfl_val side of the data union is valid. + */ typedef enum dbfl_type { - dbfl_type_rec = 0, dbfl_type_val, dbfl_type_ref } dbfl_type; /* Context of db_field_log: event = subscription update, read = read reply */ typedef enum dbfl_context { - dbfl_context_read = 0, + dbfl_context_read, dbfl_context_event } dbfl_context; -#define dbflTypeStr(t) (t==dbfl_type_val?"val":t==dbfl_type_rec?"rec":"ref") +#define dbflTypeStr(t) (t==dbfl_type_val?"val":"ref") struct dbfl_val { union native_value field; /* Field value */ @@ -82,6 +93,8 @@ struct dbfl_val { * db_delete_field_log(). Any code which changes a dbfl_type_ref * field log to another type, or to reference different data, * must explicitly call the dtor function. + * If the dtor is NULL and no_elements > 0, then this means the array + * data is still owned by a record. See the macro dbfl_has_copy below. */ struct dbfl_ref { dbfl_freeFunc *dtor; /* Callback to free filter-allocated resources */ @@ -89,8 +102,11 @@ struct dbfl_ref { void *field; /* Field value */ }; +/* + * Note that field_size may be larger than MAX_STRING_SIZE. + */ typedef struct db_field_log { - unsigned int type:2; /* type (union) selector */ + unsigned int type:1; /* type (union) selector */ /* ctx is used for all types */ unsigned int ctx:1; /* context (operation type) */ /* the following are used for value and reference types */ @@ -98,8 +114,8 @@ typedef struct db_field_log { unsigned short stat; /* Alarm Status */ unsigned short sevr; /* Alarm Severity */ short field_type; /* DBF type of data */ - short field_size; /* Data size */ - long no_elements; /* No of array elements */ + short field_size; /* Size of a single element */ + long no_elements; /* No of valid array elements */ union { struct dbfl_val v; struct dbfl_ref r; @@ -107,27 +123,19 @@ typedef struct db_field_log { } db_field_log; /* - * A db_field_log will in one of three types: - * - * dbfl_type_rec - Reference to record - * The field log stores no data itself. Data must instead be taken - * via the dbChannel* which must always be provided when along - * with the field log. - * For this type only the 'type' and 'ctx' members are used. - * - * dbfl_type_ref - Reference to outside value - * Used for variable size (array) data types. Meta-data - * is stored in the field log, but value data is stored externally - * (see struct dbfl_ref). - * For this type all meta-data members are used. The dbfl_ref side of the - * data union is used. - * - * dbfl_type_val - Internal value - * Used to store small scalar data. Meta-data and value are - * present in this structure and no external references are used. - * For this type all meta-data members are used. The dbfl_val side of the - * data union is used. + * Whether a db_field_log* owns the field data. If this is the case, then the + * db_field_log is fully decoupled from the record: there is no need to lock + * the record when accessing the data, nor to call any rset methods (like + * get_array_info) because this has already been done when we made the copy. A + * special case here is that of no (remaining) data (i.e. no_elements==0). In + * this case, making a copy is redundant, so we have no dtor. But conceptually + * the db_field_log still owns the (empty) data. */ +#define dbfl_has_copy(p)\ + ((p) && ((p)->type==dbfl_type_val || (p)->u.r.dtor || (p)->no_elements==0)) + +#define dbfl_pfield(p)\ + ((p)->type==dbfl_type_val ? &p->u.v.field : p->u.r.field) #ifdef __cplusplus } diff --git a/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c b/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c index 1a1960636..2f33ccff4 100644 --- a/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c +++ b/modules/database/src/ioc/dbStatic/dbStaticIocRegister.c @@ -34,8 +34,10 @@ static void dbDumpPathCallFunc(const iocshArgBuf *args) static const iocshArg dbDumpRecordArg2 = { "interest level",iocshArgInt}; static const iocshArg * const dbDumpRecordArgs[] = {&argPdbbase, &argRecType, &dbDumpRecordArg2}; -static const iocshFuncDef dbDumpRecordFuncDef = - {"dbDumpRecord",3,dbDumpRecordArgs}; +static const iocshFuncDef dbDumpRecordFuncDef = {"dbDumpRecord",3,dbDumpRecordArgs, + "Dump information about the recordTypeName with 'interest level' details.\n" + "Example: dbDumpRecord ai 2\n" + "If last argument(s) are missing, dump all recordType information in the database.\n"}; static void dbDumpRecordCallFunc(const iocshArgBuf *args) { dbDumpRecord(*iocshPpdbbase,args[1].sval,args[2].ival); @@ -45,7 +47,10 @@ static void dbDumpRecordCallFunc(const iocshArgBuf *args) static const iocshArg dbDumpMenuArg1 = { "menuName",iocshArgString}; static const iocshArg * const dbDumpMenuArgs[] = { &argPdbbase, &dbDumpMenuArg1}; -static const iocshFuncDef dbDumpMenuFuncDef = {"dbDumpMenu",2,dbDumpMenuArgs}; +static const iocshFuncDef dbDumpMenuFuncDef = {"dbDumpMenu",2,dbDumpMenuArgs, + "Dump information about the available menuNames and choices defined withing each menuName.\n" + "Example: dbDumpMenu pdbbase menuAlarmStat \n" + "If last argument(s) are missing, dump all menuNames information in the database.\n"}; static void dbDumpMenuCallFunc(const iocshArgBuf *args) { dbDumpMenu(*iocshPpdbbase,args[1].sval); @@ -54,8 +59,10 @@ static void dbDumpMenuCallFunc(const iocshArgBuf *args) /* dbDumpRecordType */ static const iocshArg * const dbDumpRecordTypeArgs[] = {&argPdbbase, &argRecType}; -static const iocshFuncDef dbDumpRecordTypeFuncDef = - {"dbDumpRecordType",2,dbDumpRecordTypeArgs}; +static const iocshFuncDef dbDumpRecordTypeFuncDef = {"dbDumpRecordType",2,dbDumpRecordTypeArgs, + "Dump information about available fields in the recortTypeName sorted by index and name.\n" + "Example: dbDumpRecordType pdbbase calcout\n" + "If last argument(s) are missing, dump fields information for all records in the database.\n"}; static void dbDumpRecordTypeCallFunc(const iocshArgBuf *args) { dbDumpRecordType(*iocshPpdbbase,args[1].sval); @@ -65,7 +72,11 @@ static void dbDumpRecordTypeCallFunc(const iocshArgBuf *args) static const iocshArg dbDumpFieldArg2 = { "fieldName",iocshArgString}; static const iocshArg * const dbDumpFieldArgs[] = {&argPdbbase, &argRecType,&dbDumpFieldArg2}; -static const iocshFuncDef dbDumpFieldFuncDef = {"dbDumpField",3,dbDumpFieldArgs}; +static const iocshFuncDef dbDumpFieldFuncDef = {"dbDumpField",3,dbDumpFieldArgs, + "Dump information about the fieldName in the recordTypeName.\n" + "Example: dbDumpField pdbbase calcout A\n" + "If last argument(s) are missing, dump information\n" + "about all fieldName in all recordTypeName in the database.\n"}; static void dbDumpFieldCallFunc(const iocshArgBuf *args) { dbDumpField(*iocshPpdbbase,args[1].sval,args[2].sval); @@ -74,7 +85,11 @@ static void dbDumpFieldCallFunc(const iocshArgBuf *args) /* dbDumpDevice */ static const iocshArg * const dbDumpDeviceArgs[] = { &argPdbbase, &argRecType}; -static const iocshFuncDef dbDumpDeviceFuncDef = {"dbDumpDevice",2,dbDumpDeviceArgs}; +static const iocshFuncDef dbDumpDeviceFuncDef = {"dbDumpDevice",2,dbDumpDeviceArgs, + "Dump device support information for the recordTypeName.\n" + "Example: dbDumpDevice pdbbase ai\n" + "If last argument(s) are missing, dump device support\n" + "information for all records in the database.\n"}; static void dbDumpDeviceCallFunc(const iocshArgBuf *args) { dbDumpDevice(*iocshPpdbbase,args[1].sval); @@ -82,7 +97,9 @@ static void dbDumpDeviceCallFunc(const iocshArgBuf *args) /* dbDumpDriver */ static const iocshArg * const dbDumpDriverArgs[] = { &argPdbbase}; -static const iocshFuncDef dbDumpDriverFuncDef = {"dbDumpDriver",1,dbDumpDriverArgs}; +static const iocshFuncDef dbDumpDriverFuncDef = {"dbDumpDriver",1,dbDumpDriverArgs, + "Dump device support information.\n" + "Example: dbDumpDriver pdbbase\n"}; static void dbDumpDriverCallFunc(const iocshArgBuf *args) { dbDumpDriver(*iocshPpdbbase); @@ -98,7 +115,10 @@ static void dbDumpLinkCallFunc(const iocshArgBuf *args) /* dbDumpRegistrar */ static const iocshArg * const dbDumpRegistrarArgs[] = { &argPdbbase}; -static const iocshFuncDef dbDumpRegistrarFuncDef = {"dbDumpRegistrar",1,dbDumpRegistrarArgs}; +static const iocshFuncDef dbDumpRegistrarFuncDef = {"dbDumpRegistrar",1,dbDumpRegistrarArgs, + "Dump list of registered functions including ones for subroutine records,\n" + "and ones that can be invoked from iocsh.\n" + "Example: dbDumpRegistrar pdbbase\n"}; static void dbDumpRegistrarCallFunc(const iocshArgBuf *args) { dbDumpRegistrar(*iocshPpdbbase); @@ -106,7 +126,9 @@ static void dbDumpRegistrarCallFunc(const iocshArgBuf *args) /* dbDumpFunction */ static const iocshArg * const dbDumpFunctionArgs[] = { &argPdbbase}; -static const iocshFuncDef dbDumpFunctionFuncDef = {"dbDumpFunction",1,dbDumpFunctionArgs}; +static const iocshFuncDef dbDumpFunctionFuncDef = {"dbDumpFunction",1,dbDumpFunctionArgs, + "Dump list of registered subroutine functions.\n" + "Example: dbDumpFunction pddbase\n"}; static void dbDumpFunctionCallFunc(const iocshArgBuf *args) { dbDumpFunction(*iocshPpdbbase); @@ -114,7 +136,9 @@ static void dbDumpFunctionCallFunc(const iocshArgBuf *args) /* dbDumpVariable */ static const iocshArg * const dbDumpVariableArgs[] = { &argPdbbase}; -static const iocshFuncDef dbDumpVariableFuncDef = {"dbDumpVariable",1,dbDumpVariableArgs}; +static const iocshFuncDef dbDumpVariableFuncDef = {"dbDumpVariable",1,dbDumpVariableArgs, + "Dump list of variables used in the database.\n" + "Example: dbDumpVariable pddbase\n"}; static void dbDumpVariableCallFunc(const iocshArgBuf *args) { dbDumpVariable(*iocshPpdbbase); @@ -124,8 +148,7 @@ static void dbDumpVariableCallFunc(const iocshArgBuf *args) static const iocshArg dbDumpBreaktableArg1 = { "tableName",iocshArgString}; static const iocshArg * const dbDumpBreaktableArgs[] = {&argPdbbase,&dbDumpBreaktableArg1}; -static const iocshFuncDef dbDumpBreaktableFuncDef = - {"dbDumpBreaktable",2,dbDumpBreaktableArgs}; +static const iocshFuncDef dbDumpBreaktableFuncDef = {"dbDumpBreaktable",2,dbDumpBreaktableArgs}; static void dbDumpBreaktableCallFunc(const iocshArgBuf *args) { dbDumpBreaktable(*iocshPpdbbase,args[1].sval); @@ -145,8 +168,7 @@ static void dbPvdDumpCallFunc(const iocshArgBuf *args) static const iocshArg dbPvdTableSizeArg0 = { "size",iocshArgInt}; static const iocshArg * const dbPvdTableSizeArgs[1] = {&dbPvdTableSizeArg0}; -static const iocshFuncDef dbPvdTableSizeFuncDef = - {"dbPvdTableSize",1,dbPvdTableSizeArgs}; +static const iocshFuncDef dbPvdTableSizeFuncDef = {"dbPvdTableSize",1,dbPvdTableSizeArgs}; static void dbPvdTableSizeCallFunc(const iocshArgBuf *args) { dbPvdTableSize(args[0].ival); @@ -154,8 +176,7 @@ static void dbPvdTableSizeCallFunc(const iocshArgBuf *args) /* dbReportDeviceConfig */ static const iocshArg * const dbReportDeviceConfigArgs[] = {&argPdbbase}; -static const iocshFuncDef dbReportDeviceConfigFuncDef = { - "dbReportDeviceConfig",1,dbReportDeviceConfigArgs}; +static const iocshFuncDef dbReportDeviceConfigFuncDef = {"dbReportDeviceConfig",1,dbReportDeviceConfigArgs}; static void dbReportDeviceConfigCallFunc(const iocshArgBuf *args) { dbReportDeviceConfig(*iocshPpdbbase,stdout); diff --git a/modules/database/src/ioc/dbtemplate/msi.cpp b/modules/database/src/ioc/dbtemplate/msi.cpp index 2e8c15747..6445c22f1 100644 --- a/modules/database/src/ioc/dbtemplate/msi.cpp +++ b/modules/database/src/ioc/dbtemplate/msi.cpp @@ -301,11 +301,11 @@ static void makeSubstitutions(inputData * const inputPvt, char *pstart; char *pend; int cmdind=-1; - int i; + size_t i; for (i = 0; i < NELEMENTS(cmdNames); i++) { if (strstr(command, cmdNames[i])) { - cmdind = i; + cmdind = (int)i; } } if (cmdind < 0) goto endcmd; diff --git a/modules/database/src/ioc/misc/miscIocRegister.c b/modules/database/src/ioc/misc/miscIocRegister.c index f88db7586..8c1180402 100644 --- a/modules/database/src/ioc/misc/miscIocRegister.c +++ b/modules/database/src/ioc/misc/miscIocRegister.c @@ -20,35 +20,43 @@ #include "miscIocRegister.h" /* iocInit */ -static const iocshFuncDef iocInitFuncDef = {"iocInit",0,NULL}; +static const iocshFuncDef iocInitFuncDef = {"iocInit",0,NULL, + "Initializes the various epics components and starts the IOC running.\n"}; static void iocInitCallFunc(const iocshArgBuf *args) { iocshSetError(iocInit()); } /* iocBuild */ -static const iocshFuncDef iocBuildFuncDef = {"iocBuild",0,NULL}; +static const iocshFuncDef iocBuildFuncDef = {"iocBuild",0,NULL, + "First step of the IOC initialization, puts the IOC into a ready-to-run (quiescent) state.\n" + "Needs iocRun() to make the IOC live.\n"}; static void iocBuildCallFunc(const iocshArgBuf *args) { iocshSetError(iocBuild()); } /* iocRun */ -static const iocshFuncDef iocRunFuncDef = {"iocRun",0,NULL}; +static const iocshFuncDef iocRunFuncDef = {"iocRun",0,NULL, + "Bring the IOC out of its initial quiescent state to the running state.\n" + "See more: iocBuild, iocPause"}; static void iocRunCallFunc(const iocshArgBuf *args) { iocshSetError(iocRun()); } /* iocPause */ -static const iocshFuncDef iocPauseFuncDef = {"iocPause",0,NULL}; +static const iocshFuncDef iocPauseFuncDef = {"iocPause",0,NULL, + "Brings a running IOC to a quiescent state with all record processing frozen.\n" + "See more: iocBuild, iocRub, iocInit"}; static void iocPauseCallFunc(const iocshArgBuf *args) { iocshSetError(iocPause()); } /* coreRelease */ -static const iocshFuncDef coreReleaseFuncDef = {"coreRelease",0,NULL}; +static const iocshFuncDef coreReleaseFuncDef = {"coreRelease",0,NULL, + "Print release information for iocCore.\n"}; static void coreReleaseCallFunc(const iocshArgBuf *args) { coreRelease (); @@ -75,7 +83,11 @@ void miscIocRegister(void) #ifndef SYSTEM_UNAVAILABLE static const iocshArg systemArg0 = { "command string",iocshArgString}; static const iocshArg * const systemArgs[] = {&systemArg0}; -static const iocshFuncDef systemFuncDef = {"system",1,systemArgs}; +static const iocshFuncDef systemFuncDef = {"system",1,systemArgs, + "Send command string to the system command interpreter for execution.\n" + "Not available on all OSs.\n" + "To enable this command, add registrar(iocshSystemCommand) to an application dbd file,\n" + "or include system.dbd\n"}; static void systemCallFunc(const iocshArgBuf *args) { iocshSetError(system(args[0].sval)); diff --git a/modules/database/src/ioc/rsrv/rsrvIocRegister.c b/modules/database/src/ioc/rsrv/rsrvIocRegister.c index 05b2e7d6b..533d67b52 100644 --- a/modules/database/src/ioc/rsrv/rsrvIocRegister.c +++ b/modules/database/src/ioc/rsrv/rsrvIocRegister.c @@ -20,7 +20,12 @@ /* casr */ static const iocshArg casrArg0 = { "level",iocshArgInt}; static const iocshArg * const casrArgs[1] = {&casrArg0}; -static const iocshFuncDef casrFuncDef = {"casr",1,casrArgs}; +static const iocshFuncDef casrFuncDef = {"casr",1,casrArgs, + "Channel Access Server Report with following levels:\n" + " 0 - server’s protocol version level and summary for each attached client\n" + " 1 - extends report with information about connected clients and beacons\n" + " 2 - extends report with specific channel names and UDP search requests\n" + " 3+ - expert\n"}; static void casrCallFunc(const iocshArgBuf *args) { casr(args[0].ival); diff --git a/modules/database/src/std/filters/arr.c b/modules/database/src/std/filters/arr.c index 813848076..ffe3fce8f 100644 --- a/modules/database/src/std/filters/arr.c +++ b/modules/database/src/std/filters/arr.c @@ -13,16 +13,14 @@ #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "chfPlugin.h" +#include "dbAccessDefs.h" +#include "dbExtractArray.h" +#include "db_field_log.h" +#include "dbLock.h" +#include "epicsExit.h" +#include "freeList.h" +#include "epicsExport.h" typedef struct myStruct { epicsInt32 start; @@ -46,6 +44,8 @@ static void * allocPvt(void) myStruct *my = (myStruct*) freeListCalloc(myStructFreeList); if (!my) return NULL; + /* defaults */ + my->start = 0; my->incr = 1; my->end = -1; return (void *) my; @@ -77,8 +77,6 @@ static void freeArray(db_field_log *pfl) static long wrapArrayIndices(long *start, const long increment, long *end, const long no_elements) { - long len = 0; - if (*start < 0) *start = no_elements + *start; if (*start < 0) *start = 0; if (*start > no_elements) *start = no_elements; @@ -87,85 +85,53 @@ static long wrapArrayIndices(long *start, const long increment, long *end, if (*end < 0) *end = 0; if (*end >= no_elements) *end = no_elements - 1; - if (*end - *start >= 0) len = 1 + (*end - *start) / increment; - return len; + if (*end - *start >= 0) + return 1 + (*end - *start) / increment; + else + return 0; } static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { myStruct *my = (myStruct*) pvt; - struct dbCommon *prec; - rset *prset; + int must_lock; long start = my->start; long end = my->end; - long nTarget = 0; + long nTarget; + void *pTarget; long offset = 0; - long nSource = dbChannelElements(chan); - long capacity = nSource; - void *pdst; + long nSource = pfl->no_elements; + void *pSource = pfl->u.r.field; switch (pfl->type) { case dbfl_type_val: - /* Only filter arrays */ + /* TODO Treat scalars as arrays with 1 element */ break; - case dbfl_type_rec: - /* Extract from record */ - if (dbChannelSpecial(chan) == SPC_DBADDR && - nSource > 1 && - (prset = dbGetRset(&chan->addr)) && - prset->get_array_info) - { - void *pfieldsave = dbChannelField(chan); - prec = dbChannelRecord(chan); - dbScanLock(prec); - prset->get_array_info(&chan->addr, &nSource, &offset); - nTarget = wrapArrayIndices(&start, my->incr, &end, nSource); - pfl->type = dbfl_type_ref; - pfl->stat = prec->stat; - pfl->sevr = prec->sevr; - pfl->time = prec->time; - pfl->field_type = dbChannelFieldType(chan); - pfl->field_size = dbChannelFieldSize(chan); - pfl->no_elements = nTarget; - if (nTarget) { - pdst = freeListCalloc(my->arrayFreeList); - if (pdst) { - pfl->u.r.dtor = freeArray; - pfl->u.r.pvt = my->arrayFreeList; - offset = (offset + start) % dbChannelElements(chan); - dbExtractArrayFromRec(&chan->addr, pdst, nTarget, capacity, - offset, my->incr); - pfl->u.r.field = pdst; - } - } - dbScanUnlock(prec); - dbChannelField(chan) = pfieldsave; - } - break; - - /* Extract from buffer */ case dbfl_type_ref: - pdst = NULL; - nSource = pfl->no_elements; - nTarget = wrapArrayIndices(&start, my->incr, &end, nSource); - pfl->no_elements = nTarget; - if (nTarget) { - /* Copy the data out */ - void *psrc = pfl->u.r.field; - - pdst = freeListCalloc(my->arrayFreeList); - if (!pdst) break; - offset = start; - dbExtractArrayFromBuf(psrc, pdst, pfl->field_size, pfl->field_type, - nTarget, nSource, offset, my->incr); + must_lock = !pfl->u.r.dtor; + if (must_lock) { + dbScanLock(dbChannelRecord(chan)); + dbChannelGetArrayInfo(chan, &pSource, &nSource, &offset); } - if (pfl->u.r.dtor) pfl->u.r.dtor(pfl); - if (nTarget) { + nTarget = wrapArrayIndices(&start, my->incr, &end, nSource); + if (nTarget > 0) { + /* copy the data */ + pTarget = freeListCalloc(my->arrayFreeList); + if (!pTarget) break; + /* must do the wrap-around with the original no_elements */ + offset = (offset + start) % pfl->no_elements; + dbExtractArray(pSource, pTarget, pfl->field_size, + nTarget, pfl->no_elements, offset, my->incr); + if (pfl->u.r.dtor) pfl->u.r.dtor(pfl); + pfl->u.r.field = pTarget; pfl->u.r.dtor = freeArray; pfl->u.r.pvt = my->arrayFreeList; - pfl->u.r.field = pdst; } + /* adjust no_elements (even if zero elements remain) */ + pfl->no_elements = nTarget; + if (must_lock) + dbScanUnlock(dbChannelRecord(chan)); break; } return pfl; diff --git a/modules/database/src/std/filters/ts.c b/modules/database/src/std/filters/ts.c index 8a07c3f2f..0feadb0f5 100644 --- a/modules/database/src/std/filters/ts.c +++ b/modules/database/src/std/filters/ts.c @@ -12,21 +12,44 @@ */ #include +#include +#include -#include -#include -#include -#include +#include "chfPlugin.h" +#include "db_field_log.h" +#include "dbExtractArray.h" +#include "dbLock.h" +#include "epicsExport.h" + +/* + * The size of the data is different for each channel, and can even + * change at runtime, so a freeList doesn't make much sense here. + */ +static void freeArray(db_field_log *pfl) { + free(pfl->u.r.field); +} static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { epicsTimeStamp now; epicsTimeGetCurrent(&now); - /* If string or array, must make a copy (to ensure coherence between time and data) */ - if (pfl->type == dbfl_type_rec) { - dbScanLock(dbChannelRecord(chan)); - dbChannelMakeArrayCopy(pvt, pfl, chan); - dbScanUnlock(dbChannelRecord(chan)); + /* If reference and not already copied, + must make a copy (to ensure coherence between time and data) */ + if (pfl->type == dbfl_type_ref && !pfl->u.r.dtor) { + void *pTarget = calloc(pfl->no_elements, pfl->field_size); + void *pSource = pfl->u.r.field; + if (pTarget) { + long offset = 0; + long nSource = pfl->no_elements; + dbScanLock(dbChannelRecord(chan)); + dbChannelGetArrayInfo(chan, &pSource, &nSource, &offset); + dbExtractArray(pSource, pTarget, pfl->field_size, + nSource, pfl->no_elements, offset, 1); + pfl->u.r.field = pTarget; + pfl->u.r.dtor = freeArray; + pfl->u.r.pvt = pvt; + dbScanUnlock(dbChannelRecord(chan)); + } } pfl->time = now; diff --git a/modules/database/src/tools/DBD/Menu.pm b/modules/database/src/tools/DBD/Menu.pm index 38a2aba99..5480b3952 100644 --- a/modules/database/src/tools/DBD/Menu.pm +++ b/modules/database/src/tools/DBD/Menu.pm @@ -67,13 +67,16 @@ sub toDeclaration { my $name = $this->name; my $macro_name = "${name}_NUM_CHOICES"; my @choices = map { - sprintf " %-31s /* %s */", @{$_}[0], escapeCcomment(@{$_}[1]); + sprintf " %-31s /**< \@brief State string \"%s\" */", + @{$_}[0], escapeCcomment(@{$_}[1]); } $this->choices; my $num = scalar @choices; return "#ifndef $macro_name\n" . + "/** \@brief Enumerated type from menu $name */\n" . "typedef enum {\n" . join(",\n", @choices) . "\n} $name;\n" . + "/** \@brief Number of states defined for menu $name */\n" . "#define $macro_name $num\n" . "#endif\n\n"; } diff --git a/modules/database/src/tools/DBD/Recfield.pm b/modules/database/src/tools/DBD/Recfield.pm index cc49bcc46..b4bd5d318 100644 --- a/modules/database/src/tools/DBD/Recfield.pm +++ b/modules/database/src/tools/DBD/Recfield.pm @@ -185,7 +185,7 @@ sub toDeclaration { my $name = $this->C_name; my $result = sprintf " %-19s %-12s", $ctype, "$name;"; my $prompt = $this->attribute('prompt'); - $result .= "/* $prompt */" if defined $prompt; + $result .= "/**< \@brief $prompt */" if defined $prompt; return $result; } @@ -217,7 +217,7 @@ sub toDeclaration { my $size = $this->attribute('size'); my $result = sprintf " %-19s %-12s", 'char', "${name}[${size}];"; my $prompt = $this->attribute('prompt'); - $result .= "/* $prompt */" if defined $prompt; + $result .= "/**< \@brief $prompt */" if defined $prompt; return $result; } @@ -540,7 +540,7 @@ sub toDeclaration { my $extra = $this->attribute('extra'); my $result = sprintf " %-31s ", "$extra;"; my $prompt = $this->attribute('prompt'); - $result .= "/* $prompt */" if defined $prompt; + $result .= "/**< \@brief $prompt */" if defined $prompt; return $result; } diff --git a/modules/database/src/tools/DBD/Recordtype.pm b/modules/database/src/tools/DBD/Recordtype.pm index a602f5da4..1e626fdf8 100644 --- a/modules/database/src/tools/DBD/Recordtype.pm +++ b/modules/database/src/tools/DBD/Recordtype.pm @@ -132,10 +132,15 @@ sub toDeclaration { $_->toDeclaration } $this->fields; my $name = $this->name; - $name .= "Record" unless $name eq "dbCommon"; - return "typedef struct $name {\n" . - join("\n", @fields) . - "\n} $name;\n\n"; + my $doc = $name; + if ($name ne 'dbCommon') { + $name .= 'Record'; + $doc .= ' record type.'; + } + return "/** \@brief Declaration of $doc */\n" . + "typedef struct $name {\n" . + join("\n", @fields) . + "\n} $name;\n\n"; } 1; diff --git a/modules/database/src/tools/dbdToMenuH.pl b/modules/database/src/tools/dbdToMenuH.pl index 10f1904fb..71352edff 100644 --- a/modules/database/src/tools/dbdToMenuH.pl +++ b/modules/database/src/tools/dbdToMenuH.pl @@ -11,6 +11,8 @@ use FindBin qw($Bin); use lib ("$Bin/../../lib/perl"); +use strict; + use EPICS::Getopts; use File::Basename; use DBD; @@ -57,7 +59,9 @@ if ($opt_D) { print map { "$_:\n" } @uniqfiles; } else { open OUTFILE, ">$outfile" or die "$tool: Can't open $outfile: $!\n"; - print OUTFILE "/* $outbase generated from $inbase */\n\n", + print OUTFILE "/** \@file $outbase\n", + " * \@brief Declarations generated from $inbase\n", + " */\n\n", "#ifndef $guard_name\n", "#define $guard_name\n\n"; my $menus = $dbd->menus; diff --git a/modules/database/src/tools/dbdToRecordtypeH.pl b/modules/database/src/tools/dbdToRecordtypeH.pl index 76bae7fbf..0bb3839fe 100644 --- a/modules/database/src/tools/dbdToRecordtypeH.pl +++ b/modules/database/src/tools/dbdToRecordtypeH.pl @@ -61,13 +61,19 @@ if ($opt_D) { # Output dependencies only, to stdout print "$outfile: ", join(" \\\n ", @uniqfiles), "\n\n"; print map { "$_:\n" } @uniqfiles; } else { + our ($rn, $rtyp) = each %{$rtypes}; + my $rtn = $rn; + $rtn .= 'Record' if $rn ne 'dbCommon'; + open OUTFILE, ">$outfile" or die "$tool: Can't open $outfile: $!\n"; - print OUTFILE "/* $outbase generated from $inbase */\n\n", + print OUTFILE "/** \@file $outbase\n", + " * \@brief Declarations for the \@ref $rtn \"$rn\" record type.\n", + " *\n", + " * This header was generated from $inbase\n", + " */\n\n", "#ifndef $guard_name\n", "#define $guard_name\n\n"; - our ($rn, $rtyp) = each %{$rtypes}; - print OUTFILE $rtyp->toCdefs; my @menu_fields = grep { diff --git a/modules/database/test/ioc/db/dbChArrTest.cpp b/modules/database/test/ioc/db/dbChArrTest.cpp index 90702b52e..9965852a8 100644 --- a/modules/database/test/ioc/db/dbChArrTest.cpp +++ b/modules/database/test/ioc/db/dbChArrTest.cpp @@ -131,7 +131,7 @@ static void check(short dbr_type) { memset(buf, 0, sizeof(buf)); \ (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \ pfl = db_create_read_log(pch); \ - testOk(pfl && pfl->type == dbfl_type_rec, "Valid pfl, type = rec"); \ + testOk(pfl && pfl->type == dbfl_type_ref, "Valid pfl, type = ref"); \ testOk(!dbChannelGetField(pch, DBR_LONG, buf, NULL, &req, pfl), "Got Field value"); \ testOk(req == Size, "Got %ld elements (expected %d)", req, Size); \ if (!testOk(!memcmp(buf, Expected, sizeof(Expected)), "Data correct")) \ diff --git a/modules/database/test/std/filters/arrTest.cpp b/modules/database/test/std/filters/arrTest.cpp index bd83bd8ab..dfbbf463f 100644 --- a/modules/database/test/std/filters/arrTest.cpp +++ b/modules/database/test/std/filters/arrTest.cpp @@ -73,9 +73,9 @@ static int fl_equals_array(short type, const db_field_log *pfl1, void *p2) { } break; case DBR_STRING: - if (strtol(&((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], NULL, 0) != ((epicsInt32*)p2)[i]) { + if (strtol(&((const char*)pfl1->u.r.field)[i*pfl1->field_size], NULL, 0) != ((epicsInt32*)p2)[i]) { testDiag("at index=%d: field log has '%s', should be '%d'", - i, &((const char*)pfl1->u.r.field)[i*MAX_STRING_SIZE], ((epicsInt32*)p2)[i]); + i, &((const char*)pfl1->u.r.field)[i*pfl1->field_size], ((epicsInt32*)p2)[i]); return 0; } break; @@ -120,7 +120,7 @@ static void testHead (const char *title, const char *typ = "") { off = Offset; \ (void) dbPutField(&offaddr, DBR_LONG, &off, 1); \ pfl = db_create_read_log(pch); \ - testOk(pfl->type == dbfl_type_rec, "original field log has type rec"); \ + testOk(pfl->type == dbfl_type_ref, "original field log has type ref"); \ pfl2 = dbChannelRunPostChain(pch, pfl); \ testOk(pfl2 == pfl, "call does not drop or replace field_log"); \ testOk(pfl->type == dbfl_type_ref, "filtered field log has type ref"); \ diff --git a/modules/database/test/std/filters/dbndTest.c b/modules/database/test/std/filters/dbndTest.c index e7897e28a..0206865fb 100644 --- a/modules/database/test/std/filters/dbndTest.c +++ b/modules/database/test/std/filters/dbndTest.c @@ -130,7 +130,7 @@ MAIN(dbndTest) dbEventCtx evtctx; int logsFree, logsFinal; - testPlan(77); + testPlan(72); testdbPrepare(); @@ -171,12 +171,9 @@ MAIN(dbndTest) "dbnd has one filter with argument in pre chain"); testOk((ellCount(&pch->post_chain) == 0), "dbnd has no filter in post chain"); - /* Field logs of type ref and rec: pass any update */ - - testHead("Field logs of type ref and rec"); - fl1.type = dbfl_type_rec; - mustPassTwice(pch, &fl1, "abs field_log=rec", 0., 0); + /* Field logs of type ref: pass any update */ + testHead("Field logs of type ref"); fl1.type = dbfl_type_ref; mustPassTwice(pch, &fl1, "abs field_log=ref", 0., 0); diff --git a/modules/database/test/std/link/Makefile b/modules/database/test/std/link/Makefile index 2971ffe2d..2948157fd 100644 --- a/modules/database/test/std/link/Makefile +++ b/modules/database/test/std/link/Makefile @@ -47,6 +47,8 @@ testHarness_SRCS += epicsRunLinkTests.c linkTestHarness_SRCS += $(testHarness_SRCS) linkTestHarness_SRCS_RTEMS += rtemsTestHarness.c +PROD_SRCS_RTEMS += rtemsTestData.c + TESTPROD_vxWorks = linkTestHarness TESTPROD_RTEMS = linkTestHarness @@ -54,9 +56,16 @@ TESTSPEC_vxWorks = linkTestHarness.munch; epicsRunLinkTests TESTSPEC_RTEMS = linkTestHarness.boot; epicsRunLinkTests TESTSCRIPTS_HOST += $(TESTS:%=%.t) +ifneq ($(filter $(T_A),$(CROSS_COMPILER_RUNTEST_ARCHS)),) + TESTPROD = $(TESTPROD_HOST) + TESTSCRIPTS_RTEMS += $(TESTS:%=%.t) +endif include $(TOP)/configure/RULES ioRecord$(DEP): $(COMMON_DIR)/ioRecord.h lnkStateTest$(DEP): $(COMMON_DIR)/ioRecord.h lnkCalcTest$(DEP): $(COMMON_DIR)/ioRecord.h + +rtemsTestData.c : $(TESTFILES) $(TOOLS)/epicsMakeMemFs.pl + $(PERL) $(TOOLS)/epicsMakeMemFs.pl $@ epicsRtemsFSImage $(TESTFILES) diff --git a/modules/database/test/std/link/lnkCalcTest.c b/modules/database/test/std/link/lnkCalcTest.c index 5297a53c5..b573e345d 100644 --- a/modules/database/test/std/link/lnkCalcTest.c +++ b/modules/database/test/std/link/lnkCalcTest.c @@ -157,7 +157,7 @@ static void testCalc() MAIN(lnkCalcTest) { - testPlan(0); + testPlan(30); testCalc(); diff --git a/modules/database/test/tools/Menu.plt b/modules/database/test/tools/Menu.plt index 25b0c2020..8e667c833 100644 --- a/modules/database/test/tools/Menu.plt +++ b/modules/database/test/tools/Menu.plt @@ -30,10 +30,12 @@ is_deeply $menu->choice(2), undef, 'Third choice undefined'; like $menu->toDeclaration, qr/ ^ \s* \# \s* ifndef \s+ test_NUM_CHOICES \s* \n + \s* \/\*\* [^*]* \*\/ \s* \n \s* typedef \s+ enum \s+ \{ \s* \n - \s* ch1 \s+ \/\* [^*]* \*\/, \s* \n - \s* ch2 \s+ \/\* [^*]* \*\/ \s* \n + \s* ch1 \s+ \/\*\* [^*]* \*\/, \s* \n + \s* ch2 \s+ \/\*\* [^*]* \*\/ \s* \n \s* \} \s* test \s* ; \s* \n + \s* \/\*\* [^*]* \*\/ \s* \n \s* \# \s* define \s+ test_NUM_CHOICES \s+ 2 \s* \n \s* \# \s* endif \s* \n \s* $ /x, 'C declaration'; diff --git a/modules/libcom/src/error/errlog.h b/modules/libcom/src/error/errlog.h index 425e78ac6..02f2c0816 100644 --- a/modules/libcom/src/error/errlog.h +++ b/modules/libcom/src/error/errlog.h @@ -11,6 +11,20 @@ #ifndef INC_errlog_H #define INC_errlog_H +/** \file errlog.h + * \brief Functions for interacting with the errlog task + * + * This file contains functions for passing error messages with varying severity, + * registering and un-registering listeners and modifying the log buffer size and + * max message size. + * + * Some of these functions are similar to the standard C library functions printf + * and vprintf. For details on the arguments and return codes it is useful to consult + * any book that describes the standard C library such as + * `The C Programming Language ANSI C Edition` by Kernighan and Ritchie. + * + */ + #include #include #include @@ -22,8 +36,15 @@ extern "C" { #endif +/** + * errlogListener function type. + * + * This is used when adding or removing log listeners in ::errlogAddListener + * and ::errlogRemoveListeners. + */ typedef void (*errlogListener)(void *pPrivate, const char *message); +/** errlog severity enums */ typedef enum { errlogInfo, errlogMinor, @@ -45,37 +66,171 @@ LIBCOM_API extern int errVerbose; LIBCOM_API extern const char * errlogSevEnumString[]; #endif -/* errMessage is a macro so it can get the file and line number */ +/** + * errMessage is a macro so it can get the file and line number. It prints the message, + * the status symbol and string values, and the name of the task which invoked errMessage. + * It also prints the name of the source file and the line number from which the call was issued. + * + * The status code used for the 1st argument is: + * - 0: Find latest vxWorks or Unix error (errno value). + * - -1: Don’t report status. + * - Other: Use this status code and lookup the string value + * + * \param S Status code + * \param PM The message to print + */ #define errMessage(S, PM) \ errPrintf(S, __FILE__, __LINE__, "%s", PM) -/* epicsPrintf and epicsVprintf are old names for errlog routines*/ + +/** epicsPrintf is an old name for errlog routines */ #define epicsPrintf errlogPrintf + +/** epicsVprintf is an old name for errlog routines */ #define epicsVprintf errlogVprintf +/** + * errlogPrintf is like the printf function provided by the standard C library, except + * that the output is sent to the errlog task. Unless configured not to, the output + * will appear on the console as well. + */ LIBCOM_API int errlogPrintf(const char *pformat, ...) EPICS_PRINTF_STYLE(1,2); + +/** + * errlogVprintf is like the vprintf function provided by the standard C library, except + * that the output is sent to the errlog task. Unless configured not to, the output + * will appear on the console as well. + */ LIBCOM_API int errlogVprintf(const char *pformat, va_list pvar); + +/** + * This function is like ::errlogPrintf except that it adds the severity to the beginning + * of the message in the form `sevr=` where value is one of the enumerated + * severities in ::errlogSevEnum. Also the message is suppressed if severity is less than + * the current severity to suppress. + * + * \param severity One of the severity enums from ::errlogSevEnum + * \param pFormat The message to log or print + * \return int Consult printf documentation in C standard library + */ LIBCOM_API int errlogSevPrintf(const errlogSevEnum severity, const char *pformat, ...) EPICS_PRINTF_STYLE(2,3); + +/** + * This function is like ::errlogVprintf except that it adds the severity to the beginning + * of the message in the form `sevr=` where value is one of the enumerated + * severities in ::errlogSevEnum. Also the message is suppressed if severity is less than + * the current severity to suppress. If epicsThreadIsOkToBlock is true, which is + * true during iocInit, errlogSevVprintf does NOT send output to the + * errlog task. + * + * \param severity One of the severity enums from ::errlogSevEnum + * \param pFormat The message to log or print + * \param pvar va_list + * \return int Consult printf documentation in C standard library + */ LIBCOM_API int errlogSevVprintf(const errlogSevEnum severity, const char *pformat, va_list pvar); + +/** + * Sends message to the errlog task. + * + * \param message The message to send + */ LIBCOM_API int errlogMessage(const char *message); +/** + * Gets the string value of severity. + * + * \param severity The severity from ::errlogSevEnum + * \return The string value + */ LIBCOM_API const char * errlogGetSevEnumString(errlogSevEnum severity); + +/** + * Sets the severity to log + * + * \param severity The severity from ::errlogSevEnum + */ LIBCOM_API void errlogSetSevToLog(errlogSevEnum severity); + +/** + * Gets the current severity to log + * + * \return ::errlogSevEnum + */ LIBCOM_API errlogSevEnum errlogGetSevToLog(void); +/** + * Any code can receive errlog message. This function will add a listener callback. + * + * \param listener Function pointer of type ::errlogListener + * \param pPrivate This will be passed as the first argument of listener() + */ LIBCOM_API void errlogAddListener(errlogListener listener, void *pPrivate); + +/** + * This function will remove a listener callback. + * + * \param listener Function pointer of type ::errlogListener + * \param pPrivate This will be passed as the first argument of listener() + */ LIBCOM_API int errlogRemoveListeners(errlogListener listener, void *pPrivate); +/** + * Normally the errlog system displays all messages on the console. + * During error message storms this function can be used to suppress console messages. + * A argument of 0 suppresses the messages, any other value lets messages go to the console. + * + * \param yesno (0=No, 1=Yes) + * \return 0 + */ LIBCOM_API int eltc(int yesno); + +/** + * Sets a new stream to write the messages to + * + * \param stream Pointer to file handle + * \return 0 + */ LIBCOM_API int errlogSetConsole(FILE *stream); +/** + * Can be used to initialize the error logging system with a larger buffer. The default buffer size is 1280 bytes. + * + * \param bufsize The desired buffer size + */ LIBCOM_API int errlogInit(int bufsize); + +/** + * errlogInit2 can be used to initialize the error logging system with a larger buffer and maximum message size. + * The default buffer size is 1280 bytes, and the default maximum message size is 256. + * + * \param bufsize The desired buffer size + * \param maxMsgSize The desired max message size + */ LIBCOM_API int errlogInit2(int bufsize, int maxMsgSize); + +/** Wakes up the errlog task and then waits until all messages are flushed from the queue. */ LIBCOM_API void errlogFlush(void); +/** + * Routine errPrintf is normally called as follows: + * `errPrintf(status, __FILE__, __LINE__,"",...); ` + * + * Where status is defined as: + * - 0: Find latest vxWorks or Unix error. + * - -1: Don’t report status. + * - Other: Use this status code and lookup the string value + * + * \param status See above + * \param __FILE__ As shown or NULL if the file name and line number should not be printed. + * \param __LINE__ As shown + * + * The remaining arguments are just like the arguments to the C printf routine. + * ::errVerbose determines if the filename and line number are shown. + */ LIBCOM_API void errPrintf(long status, const char *pFileName, int lineno, const char *pformat, ...) EPICS_PRINTF_STYLE(4,5); @@ -83,6 +238,13 @@ LIBCOM_API int errlogPrintfNoConsole(const char *pformat, ...) EPICS_PRINTF_STYLE(1,2); LIBCOM_API int errlogVprintfNoConsole(const char *pformat,va_list pvar); +/** + * Lookup the status code and return the string value in pBuf + * + * \param status The status code to lookup + * \param pBuf The char buffer to write the string value into + * \param bufLength The max size of pBuf + */ LIBCOM_API void errSymLookup(long status, char *pBuf, size_t bufLength); #ifdef __cplusplus diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp index b626e1530..474436c72 100644 --- a/modules/libcom/src/iocsh/iocsh.cpp +++ b/modules/libcom/src/iocsh/iocsh.cpp @@ -163,7 +163,12 @@ const iocshCmdDef * epicsStdCall iocshFindCommand(const char *name) static const iocshArg varCmdArg0 = { "[variable", iocshArgString}; static const iocshArg varCmdArg1 = { "[value]]", iocshArgString}; static const iocshArg *varCmdArgs[2] = {&varCmdArg0, &varCmdArg1}; -static const iocshFuncDef varFuncDef = {"var", 2, varCmdArgs}; +static const iocshFuncDef varFuncDef = {"var", 2, varCmdArgs, + "Print all, print single variable or set value to single variable\n" + " (default) - print all variables and their values" + " defined in database definitions files\n" + " variable - if only parameter print value for this variable\n" + " value - set the value to variable\n"}; void epicsStdCall iocshRegisterVariable (const iocshVarDef *piocshVarDef) { @@ -1139,7 +1144,10 @@ static void varCallFunc(const iocshArgBuf *args) /* iocshCmd */ static const iocshArg iocshCmdArg0 = { "command",iocshArgString}; static const iocshArg *iocshCmdArgs[1] = {&iocshCmdArg0}; -static const iocshFuncDef iocshCmdFuncDef = {"iocshCmd",1,iocshCmdArgs}; +static const iocshFuncDef iocshCmdFuncDef = {"iocshCmd",1,iocshCmdArgs, + "Takes a single IOC shell command and executes it\n" + " * This function is most useful to execute a single IOC shell command\n" + " from vxWorks or RTEMS startup script (or command line)\n"}; static void iocshCmdCallFunc(const iocshArgBuf *args) { iocshCmd(args[0].sval); @@ -1149,7 +1157,9 @@ static void iocshCmdCallFunc(const iocshArgBuf *args) static const iocshArg iocshLoadArg0 = { "pathname",iocshArgString}; static const iocshArg iocshLoadArg1 = { "macros", iocshArgString}; static const iocshArg *iocshLoadArgs[2] = {&iocshLoadArg0, &iocshLoadArg1}; -static const iocshFuncDef iocshLoadFuncDef = {"iocshLoad",2,iocshLoadArgs}; +static const iocshFuncDef iocshLoadFuncDef = {"iocshLoad",2,iocshLoadArgs, + "Execute IOC shell commands provided in file from first parameter\n" + " * (optional) replace macros within the file with provided values\n"}; static void iocshLoadCallFunc(const iocshArgBuf *args) { iocshLoad(args[0].sval, args[1].sval); @@ -1159,7 +1169,10 @@ static void iocshLoadCallFunc(const iocshArgBuf *args) static const iocshArg iocshRunArg0 = { "command",iocshArgString}; static const iocshArg iocshRunArg1 = { "macros", iocshArgString}; static const iocshArg *iocshRunArgs[2] = {&iocshRunArg0, &iocshRunArg1}; -static const iocshFuncDef iocshRunFuncDef = {"iocshRun",2,iocshRunArgs}; +static const iocshFuncDef iocshRunFuncDef = {"iocshRun",2,iocshRunArgs, + "Takes a single IOC shell command, replaces macros and executes it\n" + " * This function is most useful to execute a single IOC shell command\n" + " from vxWorks or RTEMS startup script (or command line)\n"}; static void iocshRunCallFunc(const iocshArgBuf *args) { iocshRun(args[0].sval, args[1].sval); diff --git a/modules/libcom/src/iocsh/libComRegister.c b/modules/libcom/src/iocsh/libComRegister.c index 12ec7a9fe..1846bcee3 100644 --- a/modules/libcom/src/iocsh/libComRegister.c +++ b/modules/libcom/src/iocsh/libComRegister.c @@ -46,7 +46,9 @@ void date(const char *format) static const iocshArg dateArg0 = { "format",iocshArgString}; static const iocshArg * const dateArgs[] = {&dateArg0}; -static const iocshFuncDef dateFuncDef = {"date", 1, dateArgs}; +static const iocshFuncDef dateFuncDef = {"date", 1, dateArgs, + "Print current date and time\n" + " (default) - '%Y/%m/%d %H:%M:%S.%06f'\n"}; static void dateCallFunc (const iocshArgBuf *args) { date(args[0].sval); @@ -64,7 +66,8 @@ IOCSH_STATIC_FUNC void echo(char* str) static const iocshArg echoArg0 = { "string",iocshArgString}; static const iocshArg * const echoArgs[1] = {&echoArg0}; -static const iocshFuncDef echoFuncDef = {"echo",1,echoArgs}; +static const iocshFuncDef echoFuncDef = {"echo",1,echoArgs, + "Print string after expanding macros and environment variables\n"}; static void echoCallFunc(const iocshArgBuf *args) { echo(args[0].sval); @@ -73,7 +76,8 @@ static void echoCallFunc(const iocshArgBuf *args) /* chdir */ static const iocshArg chdirArg0 = { "directory name",iocshArgString}; static const iocshArg * const chdirArgs[1] = {&chdirArg0}; -static const iocshFuncDef chdirFuncDef = {"cd",1,chdirArgs}; +static const iocshFuncDef chdirFuncDef = {"cd",1,chdirArgs, + "Change directory to new directory provided as parameter\n"}; static void chdirCallFunc(const iocshArgBuf *args) { if (args[0].sval == NULL || @@ -83,7 +87,8 @@ static void chdirCallFunc(const iocshArgBuf *args) } /* print current working directory */ -static const iocshFuncDef pwdFuncDef = { "pwd", 0, 0 }; +static const iocshFuncDef pwdFuncDef = {"pwd", 0, 0, + "Print name of current/working directory\n"}; static void pwdCallFunc (const iocshArgBuf *args) { char buf[256]; @@ -97,7 +102,8 @@ static void pwdCallFunc (const iocshArgBuf *args) static const iocshArg epicsEnvSetArg0 = { "name",iocshArgString}; static const iocshArg epicsEnvSetArg1 = { "value",iocshArgString}; static const iocshArg * const epicsEnvSetArgs[2] = {&epicsEnvSetArg0,&epicsEnvSetArg1}; -static const iocshFuncDef epicsEnvSetFuncDef = {"epicsEnvSet",2,epicsEnvSetArgs}; +static const iocshFuncDef epicsEnvSetFuncDef = {"epicsEnvSet",2,epicsEnvSetArgs, + "Set environment variable name to value\n"}; static void epicsEnvSetCallFunc(const iocshArgBuf *args) { char *name = args[0].sval; @@ -117,7 +123,8 @@ static void epicsEnvSetCallFunc(const iocshArgBuf *args) /* epicsEnvUnset */ static const iocshArg epicsEnvUnsetArg0 = { "name",iocshArgString}; static const iocshArg * const epicsEnvUnsetArgs[1] = {&epicsEnvUnsetArg0}; -static const iocshFuncDef epicsEnvUnsetFuncDef = {"epicsEnvUnset",1,epicsEnvUnsetArgs}; +static const iocshFuncDef epicsEnvUnsetFuncDef = {"epicsEnvUnset",1,epicsEnvUnsetArgs, + "Remove variable name from the environment\n"}; static void epicsEnvUnsetCallFunc(const iocshArgBuf *args) { char *name = args[0].sval; @@ -135,14 +142,16 @@ IOCSH_STATIC_FUNC void epicsParamShow() epicsPrtEnvParams (); } -static const iocshFuncDef epicsParamShowFuncDef = {"epicsParamShow",0,NULL}; +static const iocshFuncDef epicsParamShowFuncDef = {"epicsParamShow",0,NULL, + "Show the environment variable parameters used by iocCore\n"}; static void epicsParamShowCallFunc(const iocshArgBuf *args) { epicsParamShow (); } /* epicsPrtEnvParams */ -static const iocshFuncDef epicsPrtEnvParamsFuncDef = {"epicsPrtEnvParams",0,0}; +static const iocshFuncDef epicsPrtEnvParamsFuncDef = {"epicsPrtEnvParams",0,0, + "Show the environment variable parameters used by iocCore\n"}; static void epicsPrtEnvParamsCallFunc(const iocshArgBuf *args) { epicsPrtEnvParams (); @@ -151,21 +160,29 @@ static void epicsPrtEnvParamsCallFunc(const iocshArgBuf *args) /* epicsEnvShow */ static const iocshArg epicsEnvShowArg0 = { "[name]",iocshArgString}; static const iocshArg * const epicsEnvShowArgs[1] = {&epicsEnvShowArg0}; -static const iocshFuncDef epicsEnvShowFuncDef = {"epicsEnvShow",1,epicsEnvShowArgs}; +static const iocshFuncDef epicsEnvShowFuncDef = {"epicsEnvShow",1,epicsEnvShowArgs, + "Show environment variables on your system\n" + " (default) - show all environment variables\n" + " name - show value of specific environment variable\n"}; static void epicsEnvShowCallFunc(const iocshArgBuf *args) { epicsEnvShow (args[0].sval); } /* registryDump */ -static const iocshFuncDef registryDumpFuncDef = {"registryDump",0,NULL}; +static const iocshFuncDef registryDumpFuncDef = {"registryDump",0,NULL, + "Dump a hash table of EPICS registry\n"}; static void registryDumpCallFunc(const iocshArgBuf *args) { registryDump (); } /* iocLogInit */ -static const iocshFuncDef iocLogInitFuncDef = {"iocLogInit",0}; +static const iocshFuncDef iocLogInitFuncDef = {"iocLogInit",0,0, + "Initialize IOC logging\n" + " * EPICS environment variable 'EPICS_IOC_LOG_INET' has to be defined\n" + " * Logging controled via 'iocLogDisable' variable\n" + " see 'setIocLogDisable' command\n"}; static void iocLogInitCallFunc(const iocshArgBuf *args) { iocLogInit (); @@ -179,7 +196,10 @@ IOCSH_STATIC_FUNC void setIocLogDisable(int val) static const iocshArg iocLogDisableArg0 = {"(0,1)=>(false,true)",iocshArgInt}; static const iocshArg * const iocLogDisableArgs[1] = {&iocLogDisableArg0}; -static const iocshFuncDef iocLogDisableFuncDef = {"setIocLogDisable",1,iocLogDisableArgs}; +static const iocshFuncDef iocLogDisableFuncDef = {"setIocLogDisable",1,iocLogDisableArgs, + "Controls the 'iocLogDisable' variable\n" + " 0 - enable logging\n" + " 1 - disable logging\n"}; static void iocLogDisableCallFunc(const iocshArgBuf *args) { setIocLogDisable(args[0].ival); @@ -188,7 +208,8 @@ static void iocLogDisableCallFunc(const iocshArgBuf *args) /* iocLogShow */ static const iocshArg iocLogShowArg0 = {"level",iocshArgInt}; static const iocshArg * const iocLogShowArgs[1] = {&iocLogShowArg0}; -static const iocshFuncDef iocLogShowFuncDef = {"iocLogShow",1,iocLogShowArgs}; +static const iocshFuncDef iocLogShowFuncDef = {"iocLogShow",1,iocLogShowArgs, + "Determine if a IOC Log Prefix has been set\n"}; static void iocLogShowCallFunc(const iocshArgBuf *args) { iocLogShow (args[0].ival); @@ -197,17 +218,22 @@ static void iocLogShowCallFunc(const iocshArgBuf *args) /* eltc */ static const iocshArg eltcArg0 = {"(0,1)=>(false,true)",iocshArgInt}; static const iocshArg * const eltcArgs[1] = {&eltcArg0}; -static const iocshFuncDef eltcFuncDef = {"eltc",1,eltcArgs}; +static const iocshFuncDef eltcFuncDef = {"eltc",1,eltcArgs, + "Control display of error log messages on console\n" + " 0 - no\n" + " 1 - yes (default)\n"}; static void eltcCallFunc(const iocshArgBuf *args) { eltc(args[0].ival); } /* errlogInit */ -static const iocshArg errlogInitArg0 = { "bufsize",iocshArgInt}; +static const iocshArg errlogInitArg0 = { "bufSize",iocshArgInt}; static const iocshArg * const errlogInitArgs[1] = {&errlogInitArg0}; static const iocshFuncDef errlogInitFuncDef = - {"errlogInit",1,errlogInitArgs}; + {"errlogInit",1,errlogInitArgs, + "Initialize error log client buffer size\n" + " bufSize - size of circular buffer (default = 1280 bytes)\n"}; static void errlogInitCallFunc(const iocshArgBuf *args) { errlogInit(args[0].ival); @@ -219,7 +245,10 @@ static const iocshArg errlogInit2Arg1 = { "maxMsgSize",iocshArgInt}; static const iocshArg * const errlogInit2Args[] = {&errlogInit2Arg0, &errlogInit2Arg1}; static const iocshFuncDef errlogInit2FuncDef = - {"errlogInit2", 2, errlogInit2Args}; + {"errlogInit2", 2, errlogInit2Args, + "Initialize error log client buffer size and maximum message size\n" + " bufSize - size of circular buffer (default = 1280 bytes)\n" + " maxMsgSize - maximum size of error message (default = 256 bytes)\n"}; static void errlogInit2CallFunc(const iocshArgBuf *args) { errlogInit2(args[0].ival, args[1].ival); @@ -233,7 +262,8 @@ IOCSH_STATIC_FUNC void errlog(const char *message) static const iocshArg errlogArg0 = { "message",iocshArgString}; static const iocshArg * const errlogArgs[1] = {&errlogArg0}; -static const iocshFuncDef errlogFuncDef = {"errlog",1,errlogArgs}; +static const iocshFuncDef errlogFuncDef = {"errlog",1,errlogArgs, + "Send message to errlog\n"}; static void errlogCallFunc(const iocshArgBuf *args) { errlog(args[0].sval); @@ -243,7 +273,8 @@ static void errlogCallFunc(const iocshArgBuf *args) /* iocLogPrefix */ static const iocshArg iocLogPrefixArg0 = { "prefix",iocshArgString}; static const iocshArg * const iocLogPrefixArgs[1] = {&iocLogPrefixArg0}; -static const iocshFuncDef iocLogPrefixFuncDef = {"iocLogPrefix",1,iocLogPrefixArgs}; +static const iocshFuncDef iocLogPrefixFuncDef = {"iocLogPrefix",1,iocLogPrefixArgs, + "Create the prefix for all messages going into IOC log\n"}; static void iocLogPrefixCallFunc(const iocshArgBuf *args) { iocLogPrefix(args[0].sval); @@ -253,7 +284,8 @@ static void iocLogPrefixCallFunc(const iocshArgBuf *args) static const iocshArg epicsThreadShowAllArg0 = { "level",iocshArgInt}; static const iocshArg * const epicsThreadShowAllArgs[1] = {&epicsThreadShowAllArg0}; static const iocshFuncDef epicsThreadShowAllFuncDef = - {"epicsThreadShowAll",1,epicsThreadShowAllArgs}; + {"epicsThreadShowAll",1,epicsThreadShowAllArgs, + "Display info about all threads\n"}; static void epicsThreadShowAllCallFunc(const iocshArgBuf *args) { epicsThreadShowAll(args[0].ival); @@ -262,7 +294,8 @@ static void epicsThreadShowAllCallFunc(const iocshArgBuf *args) /* epicsThreadShow */ static const iocshArg threadArg0 = { "[-level] [thread ...]", iocshArgArgv}; static const iocshArg * const threadArgs[1] = { &threadArg0 }; -static const iocshFuncDef threadFuncDef = {"epicsThreadShow",1,threadArgs}; +static const iocshFuncDef threadFuncDef = {"epicsThreadShow",1,threadArgs, + "Display info about the specified thread\n"}; static void threadCallFunc(const iocshArgBuf *args) { int i = 1; @@ -308,7 +341,8 @@ static void threadCallFunc(const iocshArgBuf *args) static const iocshArg taskwdShowArg0 = { "level",iocshArgInt}; static const iocshArg * const taskwdShowArgs[1] = {&taskwdShowArg0}; static const iocshFuncDef taskwdShowFuncDef = - {"taskwdShow",1,taskwdShowArgs}; + {"taskwdShow",1,taskwdShowArgs, + "Show number of tasks and monitors registered\n"}; static void taskwdShowCallFunc(const iocshArgBuf *args) { taskwdShow(args[0].ival); @@ -320,7 +354,10 @@ static const iocshArg epicsMutexShowAllArg1 = { "level",iocshArgInt}; static const iocshArg * const epicsMutexShowAllArgs[2] = {&epicsMutexShowAllArg0,&epicsMutexShowAllArg1}; static const iocshFuncDef epicsMutexShowAllFuncDef = - {"epicsMutexShowAll",2,epicsMutexShowAllArgs}; + {"epicsMutexShowAll",2,epicsMutexShowAllArgs, + "Display information about all epicsMutex semaphores\n" + " onlyLocked - non-zero to show only locked semaphores\n" + " level - desired information level to report\n"}; static void epicsMutexShowAllCallFunc(const iocshArgBuf *args) { epicsMutexShowAll(args[0].ival,args[1].ival); @@ -330,7 +367,8 @@ static void epicsMutexShowAllCallFunc(const iocshArgBuf *args) static const iocshArg epicsThreadSleepArg0 = { "seconds",iocshArgDouble}; static const iocshArg * const epicsThreadSleepArgs[1] = {&epicsThreadSleepArg0}; static const iocshFuncDef epicsThreadSleepFuncDef = - {"epicsThreadSleep",1,epicsThreadSleepArgs}; + {"epicsThreadSleep",1,epicsThreadSleepArgs, + "Pause execution of IOC shell for seconds\n"}; static void epicsThreadSleepCallFunc(const iocshArgBuf *args) { epicsThreadSleep(args[0].dval); @@ -339,7 +377,10 @@ static void epicsThreadSleepCallFunc(const iocshArgBuf *args) /* epicsThreadResume */ static const iocshArg epicsThreadResumeArg0 = { "[thread ...]", iocshArgArgv}; static const iocshArg * const epicsThreadResumeArgs[1] = { &epicsThreadResumeArg0 }; -static const iocshFuncDef epicsThreadResumeFuncDef = {"epicsThreadResume",1,epicsThreadResumeArgs}; +static const iocshFuncDef epicsThreadResumeFuncDef = {"epicsThreadResume",1,epicsThreadResumeArgs, + "Resume a suspended thread.\n" + "Only do this if you know that it is safe to " + "resume a suspended thread\n"}; static void epicsThreadResumeCallFunc(const iocshArgBuf *args) { int i; @@ -381,14 +422,19 @@ static void epicsThreadResumeCallFunc(const iocshArgBuf *args) /* generalTimeReport */ static const iocshArg generalTimeReportArg0 = { "interest_level", iocshArgArgv}; static const iocshArg * const generalTimeReportArgs[1] = { &generalTimeReportArg0 }; -static const iocshFuncDef generalTimeReportFuncDef = {"generalTimeReport",1,generalTimeReportArgs}; +static const iocshFuncDef generalTimeReportFuncDef = {"generalTimeReport",1,generalTimeReportArgs, + "Display time providers and their priority levels" + " for current and event times\n"}; static void generalTimeReportCallFunc(const iocshArgBuf *args) { generalTimeReport(args[0].ival); } /* installLastResortEventProvider */ -static const iocshFuncDef installLastResortEventProviderFuncDef = {"installLastResortEventProvider", 0, NULL}; +static const iocshFuncDef installLastResortEventProviderFuncDef = {"installLastResortEventProvider", 0, NULL, + "Installs the optional Last Resort event provider" + " at priority 999,\nwhich returns the current time" + " for every event number\n"}; static void installLastResortEventProviderCallFunc(const iocshArgBuf *args) { installLastResortEventProvider(); diff --git a/modules/libcom/src/misc/epicsStdlib.h b/modules/libcom/src/misc/epicsStdlib.h index 219a1fb0a..e3cb69f91 100644 --- a/modules/libcom/src/misc/epicsStdlib.h +++ b/modules/libcom/src/misc/epicsStdlib.h @@ -13,6 +13,20 @@ #ifndef INC_epicsStdlib_H #define INC_epicsStdlib_H +/** + * \file epicsStdlib.h + * \brief Functions to convert strings to primative types + * + * These routines convert a string into an integer of the indicated type and + * number base, or into a floating point type. The units pointer argument may + * be NULL, but if not it will be left pointing to the first non-whitespace + * character following the numeric string, or to the terminating zero byte. + * + * The return value from these routines is a status code, zero meaning OK. + * For the macro functions beginning with `epicsScan` the return code is 0 + * or 1 (0=failure or 1=success, similar to the sscanf() function). + */ + #include #include @@ -25,57 +39,167 @@ extern "C" { #endif +/** Return code for `No digits to convert` */ #define S_stdlib_noConversion (M_stdlib | 1) /* No digits to convert */ +/** Return code for `Extraneous characters` */ #define S_stdlib_extraneous (M_stdlib | 2) /* Extraneous characters */ +/** Return code for `Too small to represent` */ #define S_stdlib_underflow (M_stdlib | 3) /* Too small to represent */ +/** Return code for `Too large to represent` */ #define S_stdlib_overflow (M_stdlib | 4) /* Too large to represent */ +/** Return code for `Number base not supported` */ #define S_stdlib_badBase (M_stdlib | 5) /* Number base not supported */ - -LIBCOM_API int +/** + * \brief Convert a string to a long type + * + * \param str Pointer to a constant character array + * \param to Pointer to the specified type (this will be set during the conversion) + * \param base The number base to use + * \param units Pointer to a char * (this will be set with the units string) + * \return Status code (0=OK, see macro definitions for possible errors) + */ +LIBCOM_API int epicsParseLong(const char *str, long *to, int base, char **units); + +/** + * \brief Convert a string to a unsigned long type + * \copydetails epicsParseLong + */ LIBCOM_API int epicsParseULong(const char *str, unsigned long *to, int base, char **units); + +/** + * \brief Convert a string to a long long type + * \copydetails epicsParseLong + */ LIBCOM_API int epicsParseLLong(const char *str, long long *to, int base, char **units); + +/** + * \brief Convert a string to a unsigned long long type + * \copydetails epicsParseLong + */ LIBCOM_API int epicsParseULLong(const char *str, unsigned long long *to, int base, char **units); + +/** + * \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) + * \return Status code (0=OK, see macro definitions for possible errors) + */ LIBCOM_API int epicsParseDouble(const char *str, double *to, char **units); +/** + * \brief Convert a string to a float type + * \copydetails epicsParseDouble + */ LIBCOM_API int epicsParseFloat(const char *str, float *to, char **units); +/** + * \brief Convert a string to an epicsInt8 type + * \copydetails epicsParseLong + */ LIBCOM_API int epicsParseInt8(const char *str, epicsInt8 *to, int base, char **units); + +/** + * \brief Convert a string to an epicsUInt8 type + * \copydetails epicsParseLong + */ LIBCOM_API int epicsParseUInt8(const char *str, epicsUInt8 *to, int base, char **units); + +/** + * \brief Convert a string to an epicsInt16 type + * \copydetails epicsParseLong + */ LIBCOM_API int epicsParseInt16(const char *str, epicsInt16 *to, int base, char **units); + +/** + * \brief Convert a string to an epicsUInt16 type + * \copydetails epicsParseLong + */ LIBCOM_API int epicsParseUInt16(const char *str, epicsUInt16 *to, int base, char **units); +/** + * \brief Convert a string to an epicsInt32 type + * \copydetails epicsParseLong + */ LIBCOM_API int epicsParseInt32(const char *str, epicsInt32 *to, int base, char **units); + +/** + * \brief Convert a string to an epicsUInt32 type + * \copydetails epicsParseLong + */ LIBCOM_API int epicsParseUInt32(const char *str, epicsUInt32 *to, int base, char **units); +/** + * \brief Convert a string to an epicsInt64 type + * \copydetails epicsParseLong + */ LIBCOM_API int epicsParseInt64(const char *str, epicsInt64 *to, int base, char **units); + +/** + * \brief Convert a string to an epicsUInt64 type + * \copydetails epicsParseLong + */ LIBCOM_API int epicsParseUInt64(const char *str, epicsUInt64 *to, int base, char **units); +/** Macro utilizing ::epicsParseFloat to convert */ #define epicsParseFloat32(str, to, units) epicsParseFloat(str, to, units) +/** Macro utilizing ::epicsParseDouble to convert */ #define epicsParseFloat64(str, to, units) epicsParseDouble(str, to, units) /* These macros return 1 if successful, 0 on failure. * This is analagous to the return value from sscanf() */ + +/** + * Macro utilizing ::epicsParseLong to convert + * \return 0=failure, 1=success + */ #define epicsScanLong(str, to, base) (!epicsParseLong(str, to, base, NULL)) + +/** + * Macro utilizing ::epicsParseULong to convert + * \return 0=failure, 1=success + */ #define epicsScanULong(str, to, base) (!epicsParseULong(str, to, base, NULL)) + +/** + * Macro utilizing ::epicsParseLLong to convert + * \return 0=failure, 1=success + */ #define epicsScanLLong(str, to, base) (!epicsParseLLong(str, to, base, NULL)) + +/** + * Macro utilizing ::epicsParseULLong to convert + * \return 0=failure, 1=success + */ #define epicsScanULLong(str, to, base) (!epicsParseULLong(str, to, base, NULL)) + +/** + * Macro utilizing ::epicsParseFloat to convert + * \return 0=failure, 1=success + */ #define epicsScanFloat(str, to) (!epicsParseFloat(str, to, NULL)) + +/** + * Macro utilizing ::epicsParseDouble to convert + * \return 0=failure, 1=success + */ #define epicsScanDouble(str, to) (!epicsParseDouble(str, to, NULL)) #ifdef __cplusplus diff --git a/modules/libcom/src/misc/epicsTypes.h b/modules/libcom/src/misc/epicsTypes.h index 1beb5d2e4..09e550d78 100644 --- a/modules/libcom/src/misc/epicsTypes.h +++ b/modules/libcom/src/misc/epicsTypes.h @@ -8,9 +8,11 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* - * Author: Jeff Hill - * Date: 5-95 +/** + * \file epicsTypes.h + * \author: Jeff Hill + * + * \brief The core data types used by epics */ #ifndef INC_epicsTypes_H @@ -32,9 +34,12 @@ typedef enum { epicsTrue = 1 } epicsBoolean EPICS_DEPRECATED; -/* +/** + * \name epicsTypes * Architecture Independent Data Types + * * These are sufficient for all our current archs + * @{ */ typedef char epicsInt8; typedef unsigned char epicsUInt8; @@ -49,25 +54,28 @@ typedef epicsUInt16 epicsEnum16; typedef float epicsFloat32; typedef double epicsFloat64; typedef epicsInt32 epicsStatus; + /** @} */ +#define MAX_STRING_SIZE 40 +/** + * \brief !! Dont use this - it may vanish in the future !! + */ typedef struct { unsigned length; char *pString; } epicsString; -/* - * !! Dont use this - it may vanish in the future !! +/** + * \brief !! Dont use this - it may vanish in the future !! * * Provided only for backwards compatibility with * db_access.h - * */ -#define MAX_STRING_SIZE 40 typedef char epicsOldString[MAX_STRING_SIZE]; -/* - * union of all types +/** + * \brief Union of all types * * Strings included here as pointers only so that we support * large string types. @@ -90,11 +98,11 @@ typedef union epics_any { epicsString string; } epicsAny; -/* - * Corresponding Type Codes +/** + * \brief Corresponding Type Codes * (this enum must start at zero) * - * !! Update epicsTypeToDBR_XXXX[] and DBR_XXXXToEpicsType + * !! Update \ref epicsTypeToDBR_XXXX[] and \ref DBR_XXXXToEpicsType * in db_access.h if you edit this enum !! */ typedef enum { @@ -116,8 +124,9 @@ typedef enum { #define invalidEpicsType(x) ((xlastEpicsType)) -/* - * The enumeration "epicsType" is an index to this array +/** + * \brief An array providing the names for each type + * The enumeration \ref epicsType is an index to this array * of type name strings. */ #ifdef epicsTypesGLOBAL @@ -138,8 +147,9 @@ const char *epicsTypeNames [lastEpicsType+1] = { LIBCOM_API extern const char *epicsTypeNames [lastEpicsType+1]; #endif /* epicsTypesGLOBAL */ -/* - * The enumeration "epicsType" is an index to this array +/** + * \brief An array providing the names for each type code + * The enumeration \ref epicsType is an index to this array * of type code name strings. */ #ifdef epicsTypesGLOBAL @@ -160,6 +170,11 @@ const char *epicsTypeCodeNames [lastEpicsType+1] = { LIBCOM_API extern const char *epicsTypeCodeNames [lastEpicsType+1]; #endif /* epicsTypesGLOBAL */ +/** + * \brief An array providing the sizes for each type + * The enumeration \ref epicsType is an index to this array + * of type code name strings. + */ #ifdef epicsTypesGLOBAL const unsigned epicsTypeSizes [lastEpicsType+1] = { sizeof (epicsInt8), @@ -178,10 +193,6 @@ const unsigned epicsTypeSizes [lastEpicsType+1] = { LIBCOM_API extern const unsigned epicsTypeSizes [lastEpicsType+1]; #endif /* epicsTypesGLOBAL */ -/* - * The enumeration "epicsType" is an index to this array - * of type class identifiers. - */ typedef enum { epicsIntC, epicsUIntC, @@ -191,6 +202,11 @@ typedef enum { epicsOldStringC } epicsTypeClass; +/** + * \brief An array providing the class of each type + * The enumeration \ref epicsType is an index to this array + * of type class identifiers. + */ #ifdef epicsTypesGLOBAL const epicsTypeClass epicsTypeClasses [lastEpicsType+1] = { epicsIntC, @@ -209,7 +225,11 @@ const epicsTypeClass epicsTypeClasses [lastEpicsType+1] = { LIBCOM_API extern const epicsTypeClass epicsTypeClasses [lastEpicsType+1]; #endif /* epicsTypesGLOBAL */ - +/** + * \brief An array providing the field name for each type + * The enumeration \ref epicsType is an index to this array + * of type code name strings. + */ #ifdef epicsTypesGLOBAL const char *epicsTypeAnyFieldName [lastEpicsType+1] = { "int8", diff --git a/modules/libcom/src/osi/os/WIN32/osdStrtod.h b/modules/libcom/src/osi/os/WIN32/osdStrtod.h index f7ad78a02..665abf646 100644 --- a/modules/libcom/src/osi/os/WIN32/osdStrtod.h +++ b/modules/libcom/src/osi/os/WIN32/osdStrtod.h @@ -13,11 +13,6 @@ extern "C" { #endif -/* - * epicsStrtod() for systems with broken strtod() routine - */ -LIBCOM_API double epicsStrtod(const char *str, char **endp); - /* * Microsoft apparently added strto[u]ll() in VS2013 * Older compilers have these equivalents though @@ -28,6 +23,19 @@ LIBCOM_API double epicsStrtod(const char *str, char **endp); # define strtoull _strtoui64 #endif +/* + * strtod works in MSVC 1900 and mingw, use + * the OS version in those and our own otherwise + */ +#if (_MSC_VER < 1900) && !defined(_MINGW) +/* + * epicsStrtod() for systems with broken strtod() routine + */ +LIBCOM_API double epicsStrtod(const char *str, char **endp); +#else +# define epicsStrtod strtod +#endif + #ifdef __cplusplus } #endif diff --git a/modules/libcom/src/osi/os/posix/osdEvent.c b/modules/libcom/src/osi/os/posix/osdEvent.c index db61c240b..b22a3430b 100644 --- a/modules/libcom/src/osi/os/posix/osdEvent.c +++ b/modules/libcom/src/osi/os/posix/osdEvent.c @@ -51,7 +51,7 @@ LIBCOM_API epicsEventId epicsEventCreate(epicsEventInitialState init) epicsEventId pevent = malloc(sizeof(*pevent)); if (pevent) { - int status = osdPosixMutexInit(&pevent->mutex, 0); + int status = osdPosixMutexInit(&pevent->mutex, PTHREAD_MUTEX_DEFAULT); pevent->isFull = (init == epicsEventFull); if (status) { diff --git a/modules/libcom/src/osi/os/posix/osdMutex.c b/modules/libcom/src/osi/os/posix/osdMutex.c index b6bf296d6..18cd08d95 100644 --- a/modules/libcom/src/osi/os/posix/osdMutex.c +++ b/modules/libcom/src/osi/os/posix/osdMutex.c @@ -203,6 +203,6 @@ void epicsMutexOsdShowAll(void) printf("PI is%s enabled\n", proto==PTHREAD_PRIO_INHERIT ? "" : " not"); } #else - printf("PI is not available\n"); + printf("PI not supported\n"); #endif } diff --git a/modules/libcom/src/osi/os/posix/osdPosixMutexPriv.h b/modules/libcom/src/osi/os/posix/osdPosixMutexPriv.h index 2b6846c91..87289be5f 100644 --- a/modules/libcom/src/osi/os/posix/osdPosixMutexPriv.h +++ b/modules/libcom/src/osi/os/posix/osdPosixMutexPriv.h @@ -18,7 +18,7 @@ extern "C" { #endif /* Returns ENOTSUP if requested mutextype is not supported */ -/* At the moment, only 0 (default non recursive mutex) and PTHREAD_MUTEX_RECURSIVE are supported */ +/* At the moment, only PTHREAD_MUTEX_DEFAULT and PTHREAD_MUTEX_RECURSIVE are supported */ int osdPosixMutexInit(pthread_mutex_t *,int mutextype); #ifdef __cplusplus diff --git a/modules/libcom/src/osi/os/posix/osdSpin.c b/modules/libcom/src/osi/os/posix/osdSpin.c index f039e7c34..0b924ee3a 100644 --- a/modules/libcom/src/osi/os/posix/osdSpin.c +++ b/modules/libcom/src/osi/os/posix/osdSpin.c @@ -123,7 +123,7 @@ epicsSpinId epicsSpinCreate(void) { if (!spin) goto fail; - status = osdPosixMutexInit(&spin->lock, 0); + status = osdPosixMutexInit(&spin->lock, PTHREAD_MUTEX_DEFAULT); checkStatus(status, "osdPosixMutexInit"); if (status) goto fail; diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c index b5db6afad..0942d7343 100644 --- a/modules/libcom/src/osi/os/posix/osdThread.c +++ b/modules/libcom/src/osi/os/posix/osdThread.c @@ -328,9 +328,9 @@ static void once(void) int status; pthread_key_create(&getpthreadInfo,0); - status = osdPosixMutexInit(&onceLock,0); + status = osdPosixMutexInit(&onceLock,PTHREAD_MUTEX_DEFAULT); checkStatusOnceQuit(status,"osdPosixMutexInit","epicsThreadInit"); - status = osdPosixMutexInit(&listLock,0); + status = osdPosixMutexInit(&listLock,PTHREAD_MUTEX_DEFAULT); checkStatusOnceQuit(status,"osdPosixMutexInit","epicsThreadInit"); pcommonAttr = calloc(1,sizeof(commonAttr)); if(!pcommonAttr) checkStatusOnceQuit(errno,"calloc","epicsThreadInit"); diff --git a/modules/libcom/src/osi/os/vxWorks/osdThread.h b/modules/libcom/src/osi/os/vxWorks/osdThread.h index 09ecbb553..25cce37ac 100644 --- a/modules/libcom/src/osi/os/vxWorks/osdThread.h +++ b/modules/libcom/src/osi/os/vxWorks/osdThread.h @@ -1,5 +1,5 @@ /*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* Copyright (c) 2021 The University of Chicago, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. @@ -8,23 +8,22 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ -#ifndef osdThreadh -#define osdThreadh - -/* VxWorks 6.9 and later can support joining threads */ +#ifndef INC_osdThread_H +#define INC_osdThread_H #include #ifdef _WRS_VXWORKS_MAJOR -#define VXWORKS_VERSION_INT VERSION_INT(_WRS_VXWORKS_MAJOR, \ - _WRS_VXWORKS_MINOR, _WRS_VXWORKS_MAINT, _WRS_VXWORKS_SVCPK) +# define VXWORKS_VERSION_INT VERSION_INT(_WRS_VXWORKS_MAJOR, \ + _WRS_VXWORKS_MINOR, _WRS_VXWORKS_MAINT, _WRS_VXWORKS_SVCPK) #else /* Version not available at compile-time, assume... */ -#define VXWORKS_VERSION_INT VERSION_INT(5, 5, 0, 0) +# define VXWORKS_VERSION_INT VERSION_INT(5, 5, 0, 0) #endif #if VXWORKS_VERSION_INT < VERSION_INT(6, 9, 4, 1) -# undef EPICS_THREAD_CAN_JOIN +/* VxWorks 6.9.4.1 and later can support joining threads */ +# undef EPICS_THREAD_CAN_JOIN #endif -#endif /* osdThreadh */ +#endif /* INC_osdThread_H */ diff --git a/modules/libcom/src/osi/osiClockTime.c b/modules/libcom/src/osi/osiClockTime.c index 5368898d7..8b9dfd77b 100644 --- a/modules/libcom/src/osi/osiClockTime.c +++ b/modules/libcom/src/osi/osiClockTime.c @@ -58,14 +58,26 @@ static void ClockTimeSync(void *dummy); /* ClockTime_Report iocsh command */ static const iocshArg ReportArg0 = { "interest_level", iocshArgArgv}; static const iocshArg * const ReportArgs[1] = { &ReportArg0 }; -static const iocshFuncDef ReportFuncDef = {"ClockTime_Report", 1, ReportArgs}; +static const iocshFuncDef ReportFuncDef = {"ClockTime_Report", 1, ReportArgs, + "Reports clock synchronization status:\n" + " - On vxWorks and RTEMS:\n" + " * synchronization state\n" + " * last synchronization time with provider\n" + " * synchronization interval\n" + " - On workstation (WIN,*NIX):\n" + " * minimal report\n"}; static void ReportCallFunc(const iocshArgBuf *args) { ClockTime_Report(args[0].ival); } /* ClockTime_Shutdown iocsh command */ -static const iocshFuncDef ShutdownFuncDef = {"ClockTime_Shutdown", 0, NULL}; +static const iocshFuncDef ShutdownFuncDef = {"ClockTime_Shutdown", 0, NULL, + "Stops the OS synchronization thread\n" + " - On vxWorks and RTEMS:\n" + " * OS clock will free run\n" + " - On workstation (WIN,*NIX):\n" + " * no change\n"}; static void ShutdownCallFunc(const iocshArgBuf *args) { ClockTime_Shutdown(NULL); diff --git a/modules/libcom/src/osi/osiNTPTime.c b/modules/libcom/src/osi/osiNTPTime.c index fa5217faf..bd9821e4c 100644 --- a/modules/libcom/src/osi/osiNTPTime.c +++ b/modules/libcom/src/osi/osiNTPTime.c @@ -64,14 +64,21 @@ static void NTPTimeSync(void *dummy); /* NTPTime_Report iocsh command */ static const iocshArg ReportArg0 = { "interest_level", iocshArgArgv}; static const iocshArg * const ReportArgs[1] = { &ReportArg0 }; -static const iocshFuncDef ReportFuncDef = {"NTPTime_Report", 1, ReportArgs}; +static const iocshFuncDef ReportFuncDef = {"NTPTime_Report", 1, ReportArgs, + "Display time provider synchronization state\n" + " interest_level - with level 1 it also shows:\n" + " * synchronization interval\n" + " * time when last synchronized\n" + " * nominal and measured system tick rates\n" + " * server address (vxWorks only)\n"}; static void ReportCallFunc(const iocshArgBuf *args) { NTPTime_Report(args[0].ival); } /* NTPTime_Shutdown iocsh command */ -static const iocshFuncDef ShutdownFuncDef = {"NTPTime_Shutdown", 0, NULL}; +static const iocshFuncDef ShutdownFuncDef = {"NTPTime_Shutdown", 0, NULL, + "Shuts down NTP time synchronization thread\n"}; static void ShutdownCallFunc(const iocshArgBuf *args) { NTPTime_Shutdown(NULL); diff --git a/modules/libcom/src/yajl/yajl_parser.c b/modules/libcom/src/yajl/yajl_parser.c index b400709a6..b134228d2 100644 --- a/modules/libcom/src/yajl/yajl_parser.c +++ b/modules/libcom/src/yajl/yajl_parser.c @@ -29,6 +29,8 @@ #include "yajl_encode.h" #include "yajl_bytestack.h" +#include + #ifndef LLONG_MAX #define LLONG_MAX 0x7FFFFFFFFFFFFFFFLL #define LLONG_MIN (-0x7FFFFFFFFFFFFFFFLL - 1) @@ -334,7 +336,7 @@ yajl_do_parse(yajl_handle hand, const unsigned char * jsonText, yajl_buf_clear(hand->decodeBuf); yajl_buf_append(hand->decodeBuf, buf, bufLen); buf = yajl_buf_data(hand->decodeBuf); - d = strtod((char *) buf, NULL); + d = epicsStrtod((char *) buf, NULL); if ((d == HUGE_VAL || d == -HUGE_VAL) && errno == ERANGE) { diff --git a/modules/libcom/test/Makefile b/modules/libcom/test/Makefile index bf463a3d1..556b8e329 100755 --- a/modules/libcom/test/Makefile +++ b/modules/libcom/test/Makefile @@ -337,3 +337,10 @@ include $(TOP)/configure/RULES rtemsTestData.c : $(TESTFILES) $(TOOLS)/epicsMakeMemFs.pl $(PERL) $(TOOLS)/epicsMakeMemFs.pl $@ epicsRtemsFSImage $(TESTFILES) + +epicsLoadTest$(DEP): epicsInstallDir.h + +# use INSTALL_LOCATION instead of FINAL_LOCATION since test executables are not installed. +epicsInstallDir.h: $(TOP)/configure/CONFIG_SITE* + $(ECHO) "INSTALL_LOCATION=$(INSTALL_LOCATION)" + $(PERL) $(TOP)/modules/database/src/std/softIoc/makeInstallDir.pl "$(INSTALL_LOCATION)" > $@ diff --git a/modules/libcom/test/epicsLoadTest.cpp b/modules/libcom/test/epicsLoadTest.cpp index 304c42d73..3ffc89949 100644 --- a/modules/libcom/test/epicsLoadTest.cpp +++ b/modules/libcom/test/epicsLoadTest.cpp @@ -16,6 +16,8 @@ #include "epicsFindSymbol.h" #include "epicsThread.h" +#include "epicsInstallDir.h" + namespace { void loadBad() @@ -52,9 +54,9 @@ void loadCA() std::ostringstream strm; // running in eg. modules/libcom/test/O.linux-x86_64-debug #ifdef _WIN32 - strm<<"..\\..\\..\\..\\bin\\"<unicode_escape_url($section); - $section = '_' unless length $section; - return $section; + my($self, $t) = @_; + for ($t) { + s/<[^>]+>//g; # Strip HTML. + s/&[^;]+;//g; # Strip entities. + s/^\s+//; s/\s+$//; # Strip white space. + s/^([^a-zA-Z]+)$/pod$1/; # Prepend "pod" if no valid chars. + s/^[^a-zA-Z]+//; # First char must be a letter. + s/[^-a-zA-Z0-9_:.]+/-/g; # All other chars must be valid. + s/[-:.]+$//; # Strip trailing punctuation. + } + return $t; } 1;