Merge commit '504191441d8fe' into 7.0.5-troubleshoot
Conflicts: modules/database/test/std/link/Makefile modules/libcom/src/osi/os/vxWorks/osdThread.h
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -162,7 +162,7 @@ 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
<!-- Insert new items immediately below here ... -->
|
||||
|
||||
|
||||
-----
|
||||
|
||||
## EPICS Release 7.0.5
|
||||
|
||||
### Fix aai's Device Support Initialization
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
Documentation index
|
||||
|
||||
@ul
|
||||
@li @ref releasenotes
|
||||
@li @ref install
|
||||
@li @ref recordrefmanual
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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? */
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -14,11 +14,12 @@
|
||||
/*
|
||||
* Author: Ralph Lange <Ralph.Lange@bessy.de>
|
||||
*
|
||||
* based on dbConvert.c
|
||||
* based on dbConvert.c, see copyNoConvert
|
||||
* written by: Bob Dalesio, Marty Kraimer
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -13,16 +13,14 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <freeList.h>
|
||||
#include <dbAccess.h>
|
||||
#include <dbExtractArray.h>
|
||||
#include <db_field_log.h>
|
||||
#include <dbLock.h>
|
||||
#include <recSup.h>
|
||||
#include <epicsExit.h>
|
||||
#include <special.h>
|
||||
#include <chfPlugin.h>
|
||||
#include <epicsExport.h>
|
||||
#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;
|
||||
|
||||
@@ -12,21 +12,44 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <chfPlugin.h>
|
||||
#include <dbLock.h>
|
||||
#include <db_field_log.h>
|
||||
#include <epicsExport.h>
|
||||
#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;
|
||||
|
||||
@@ -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")) \
|
||||
|
||||
@@ -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"); \
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -47,16 +47,25 @@ testHarness_SRCS += epicsRunLinkTests.c
|
||||
linkTestHarness_SRCS += $(testHarness_SRCS)
|
||||
linkTestHarness_SRCS_RTEMS += rtemsTestHarness.c
|
||||
|
||||
TESTPROD_vxWorks = linkTestHarness
|
||||
TESTPROD_RTEMS = linkTestHarness
|
||||
PROD_SRCS_RTEMS += rtemsTestData.c
|
||||
|
||||
PROD_vxWorks = linkTestHarness
|
||||
PROD_RTEMS = linkTestHarness
|
||||
|
||||
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)
|
||||
|
||||
@@ -157,7 +157,7 @@ static void testCalc()
|
||||
|
||||
MAIN(lnkCalcTest)
|
||||
{
|
||||
testPlan(0);
|
||||
testPlan(30);
|
||||
|
||||
testCalc();
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -194,6 +194,7 @@ void epicsMutexOsdShow(struct epicsMutexOSD * pmutex, unsigned int level)
|
||||
|
||||
void epicsMutexOsdShowAll(void)
|
||||
{
|
||||
#if defined _POSIX_THREAD_PRIO_INHERIT
|
||||
int proto = -1;
|
||||
int ret = pthread_mutexattr_getprotocol(&globalAttrRecursive, &proto);
|
||||
if(ret) {
|
||||
@@ -201,4 +202,7 @@ void epicsMutexOsdShowAll(void)
|
||||
} else {
|
||||
printf("PI is%s enabled\n", proto==PTHREAD_PRIO_INHERIT ? "" : " not");
|
||||
}
|
||||
#else
|
||||
printf("PI not supported\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,13 +8,22 @@
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#ifndef osdThreadh
|
||||
#define osdThreadh
|
||||
#ifndef INC_osdThread_H
|
||||
#define INC_osdThread_H
|
||||
|
||||
/* VxWorks 6.9 and later can support joining threads */
|
||||
#include <epicsVersion.h>
|
||||
|
||||
#if (!defined(_WRS_VXWORKS_MAJOR) || _WRS_VXWORKS_MAJOR == 6 && _WRS_VXWORKS_MINOR < 9)
|
||||
#undef EPICS_THREAD_CAN_JOIN
|
||||
#ifdef _WRS_VXWORKS_MAJOR
|
||||
# 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)
|
||||
#endif
|
||||
|
||||
#endif /* osdThreadh */
|
||||
#if VXWORKS_VERSION_INT < VERSION_INT(6, 9, 4, 1)
|
||||
/* VxWorks 6.9.4.1 and later can support joining threads */
|
||||
# undef EPICS_THREAD_CAN_JOIN
|
||||
#endif
|
||||
|
||||
#endif /* INC_osdThread_H */
|
||||
|
||||
Submodule modules/normativeTypes updated: 1250a3c236...7a2d264f2c
Submodule modules/pvAccess updated: 64c284cd41...e1c1a4bc1b
Submodule modules/pvData updated: b1c8303870...d3b4976ea2
Submodule modules/pvDatabase updated: 09423edeab...93a259cbde
Submodule modules/pva2pva updated: b8389ac6a1...ad8b77e19f
Submodule modules/pvaClient updated: bc9ac8422c...efb2631905
Reference in New Issue
Block a user