From 625a7adc69d36328a4f805e606e8bdd198df2e18 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:29 -0400 Subject: [PATCH 01/81] Add Lloyd Hilaiel's Yet Another JSON Library v 1.05 to libCom. This is BSD licensed, modified only enough to build under EPICS. --- src/libCom/Makefile | 1 + src/libCom/yajl/Makefile | 26 ++ src/libCom/yajl/yajl.c | 153 +++++++ src/libCom/yajl/yajl_alloc.c | 67 +++ src/libCom/yajl/yajl_alloc.h | 50 +++ src/libCom/yajl/yajl_buf.c | 120 +++++ src/libCom/yajl/yajl_buf.h | 73 +++ src/libCom/yajl/yajl_bytestack.h | 85 ++++ src/libCom/yajl/yajl_common.h | 76 ++++ src/libCom/yajl/yajl_encode.c | 180 ++++++++ src/libCom/yajl/yajl_encode.h | 44 ++ src/libCom/yajl/yajl_gen.c | 296 +++++++++++++ src/libCom/yajl/yajl_gen.h | 123 ++++++ src/libCom/yajl/yajl_lex.c | 738 +++++++++++++++++++++++++++++++ src/libCom/yajl/yajl_lex.h | 133 ++++++ src/libCom/yajl/yajl_parse.h | 179 ++++++++ src/libCom/yajl/yajl_parser.c | 446 +++++++++++++++++++ src/libCom/yajl/yajl_parser.h | 79 ++++ 18 files changed, 2869 insertions(+) create mode 100644 src/libCom/yajl/Makefile create mode 100644 src/libCom/yajl/yajl.c create mode 100644 src/libCom/yajl/yajl_alloc.c create mode 100644 src/libCom/yajl/yajl_alloc.h create mode 100644 src/libCom/yajl/yajl_buf.c create mode 100644 src/libCom/yajl/yajl_buf.h create mode 100644 src/libCom/yajl/yajl_bytestack.h create mode 100644 src/libCom/yajl/yajl_common.h create mode 100644 src/libCom/yajl/yajl_encode.c create mode 100644 src/libCom/yajl/yajl_encode.h create mode 100644 src/libCom/yajl/yajl_gen.c create mode 100644 src/libCom/yajl/yajl_gen.h create mode 100644 src/libCom/yajl/yajl_lex.c create mode 100644 src/libCom/yajl/yajl_lex.h create mode 100644 src/libCom/yajl/yajl_parse.h create mode 100644 src/libCom/yajl/yajl_parser.c create mode 100644 src/libCom/yajl/yajl_parser.h diff --git a/src/libCom/Makefile b/src/libCom/Makefile index 349c8dd26..c8f41b330 100644 --- a/src/libCom/Makefile +++ b/src/libCom/Makefile @@ -35,6 +35,7 @@ include $(LIBCOM)/ring/Makefile include $(LIBCOM)/taskwd/Makefile include $(LIBCOM)/timer/Makefile include $(LIBCOM)/yacc/Makefile +include $(LIBCOM)/yajl/Makefile # Library to build: LIBRARY=Com diff --git a/src/libCom/yajl/Makefile b/src/libCom/yajl/Makefile new file mode 100644 index 000000000..f6b58efe6 --- /dev/null +++ b/src/libCom/yajl/Makefile @@ -0,0 +1,26 @@ +#************************************************************************* +# Copyright (c) 2002 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. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/libCom/Makefile. + +SRC_DIRS += $(LIBCOM)/yacc + +# Yet Another JSON Library +SRC_DIRS += $(LIBCOM)/yajl +INC += yajl_common.h +INC += yajl_gen.h +INC += yajl_parse.h +# The other yajl_*.h files are for internal use only +SRCS += yajl.c +SRCS += yajl_alloc.c +SRCS += yajl_buf.c +SRCS += yajl_encode.c +SRCS += yajl_gen.c +SRCS += yajl_lex.c +SRCS += yajl_parser.c diff --git a/src/libCom/yajl/yajl.c b/src/libCom/yajl/yajl.c new file mode 100644 index 000000000..0f32eea05 --- /dev/null +++ b/src/libCom/yajl/yajl.c @@ -0,0 +1,153 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "yajl_parse.h" +#include "yajl_lex.h" +#include "yajl_parser.h" +#include "yajl_alloc.h" + +const char * +yajl_status_to_string(yajl_status stat) +{ + const char * statStr = "unknown"; + switch (stat) { + case yajl_status_ok: + statStr = "ok, no error"; + break; + case yajl_status_client_canceled: + statStr = "client canceled parse"; + break; + case yajl_status_insufficient_data: + statStr = "eof was met before the parse could complete"; + break; + case yajl_status_error: + statStr = "parse error"; + break; + } + return statStr; +} + +yajl_handle +yajl_alloc(const yajl_callbacks * callbacks, + const yajl_parser_config * config, + const yajl_alloc_funcs * afs, + void * ctx) +{ + unsigned int allowComments = 0; + unsigned int validateUTF8 = 0; + yajl_handle hand = NULL; + yajl_alloc_funcs afsBuffer; + + /* first order of business is to set up memory allocation routines */ + if (afs != NULL) { + if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL) + { + return NULL; + } + } else { + yajl_set_default_alloc_funcs(&afsBuffer); + afs = &afsBuffer; + } + + hand = (yajl_handle) YA_MALLOC(afs, sizeof(struct yajl_handle_t)); + + /* copy in pointers to allocation routines */ + memcpy((void *) &(hand->alloc), (void *) afs, sizeof(yajl_alloc_funcs)); + + if (config != NULL) { + allowComments = config->allowComments; + validateUTF8 = config->checkUTF8; + } + + hand->callbacks = callbacks; + hand->ctx = ctx; + hand->lexer = yajl_lex_alloc(&(hand->alloc), allowComments, validateUTF8); + hand->errorOffset = 0; + hand->decodeBuf = yajl_buf_alloc(&(hand->alloc)); + yajl_bs_init(hand->stateStack, &(hand->alloc)); + + yajl_bs_push(hand->stateStack, yajl_state_start); + + return hand; +} + +void +yajl_free(yajl_handle handle) +{ + yajl_bs_free(handle->stateStack); + yajl_buf_free(handle->decodeBuf); + yajl_lex_free(handle->lexer); + YA_FREE(&(handle->alloc), handle); +} + +yajl_status +yajl_parse(yajl_handle hand, const unsigned char * jsonText, + unsigned int jsonTextLen) +{ + unsigned int offset = 0; + yajl_status status; + status = yajl_do_parse(hand, &offset, jsonText, jsonTextLen); + return status; +} + +yajl_status +yajl_parse_complete(yajl_handle hand) +{ + /* The particular case we want to handle is a trailing number. + * Further input consisting of digits could cause our interpretation + * of the number to change (buffered "1" but "2" comes in). + * A very simple approach to this is to inject whitespace to terminate + * any number in the lex buffer. + */ + return yajl_parse(hand, (const unsigned char *)" ", 1); +} + +unsigned char * +yajl_get_error(yajl_handle hand, int verbose, + const unsigned char * jsonText, unsigned int jsonTextLen) +{ + return yajl_render_error_string(hand, jsonText, jsonTextLen, verbose); +} + +void +yajl_free_error(yajl_handle hand, unsigned char * str) +{ + /* use memory allocation functions if set */ + YA_FREE(&(hand->alloc), str); +} + +/* XXX: add utility routines to parse from file */ diff --git a/src/libCom/yajl/yajl_alloc.c b/src/libCom/yajl/yajl_alloc.c new file mode 100644 index 000000000..5b2544ecd --- /dev/null +++ b/src/libCom/yajl/yajl_alloc.c @@ -0,0 +1,67 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file yajl_alloc.h + * default memory allocation routines for yajl which use malloc/realloc and + * free + */ + +#include + +#define epicsExportSharedSymbols +#include "yajl_alloc.h" + +static void * yajl_internal_malloc(void *ctx, unsigned int sz) +{ + return malloc(sz); +} + +static void * yajl_internal_realloc(void *ctx, void * previous, + unsigned int sz) +{ + return realloc(previous, sz); +} + +static void yajl_internal_free(void *ctx, void * ptr) +{ + free(ptr); +} + +void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf) +{ + yaf->malloc = yajl_internal_malloc; + yaf->free = yajl_internal_free; + yaf->realloc = yajl_internal_realloc; + yaf->ctx = NULL; +} + diff --git a/src/libCom/yajl/yajl_alloc.h b/src/libCom/yajl/yajl_alloc.h new file mode 100644 index 000000000..988a6c50a --- /dev/null +++ b/src/libCom/yajl/yajl_alloc.h @@ -0,0 +1,50 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file yajl_alloc.h + * default memory allocation routines for yajl which use malloc/realloc and + * free + */ + +#ifndef __YAJL_ALLOC_H__ +#define __YAJL_ALLOC_H__ + +#include "yajl_common.h" + +#define YA_MALLOC(afs, sz) (afs)->malloc((afs)->ctx, (sz)) +#define YA_FREE(afs, ptr) (afs)->free((afs)->ctx, (ptr)) +#define YA_REALLOC(afs, ptr, sz) (afs)->realloc((afs)->ctx, (ptr), (sz)) + +void yajl_set_default_alloc_funcs(yajl_alloc_funcs * yaf); + +#endif diff --git a/src/libCom/yajl/yajl_buf.c b/src/libCom/yajl/yajl_buf.c new file mode 100644 index 000000000..3bd7ad67b --- /dev/null +++ b/src/libCom/yajl/yajl_buf.c @@ -0,0 +1,120 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "yajl_buf.h" + +#define YAJL_BUF_INIT_SIZE 2048 + +struct yajl_buf_t { + unsigned int len; + unsigned int used; + unsigned char * data; + yajl_alloc_funcs * alloc; +}; + +static +void yajl_buf_ensure_available(yajl_buf buf, unsigned int want) +{ + unsigned int need; + + assert(buf != NULL); + + /* first call */ + if (buf->data == NULL) { + buf->len = YAJL_BUF_INIT_SIZE; + buf->data = (unsigned char *) YA_MALLOC(buf->alloc, buf->len); + buf->data[0] = 0; + } + + need = buf->len; + + while (want >= (need - buf->used)) need <<= 1; + + if (need != buf->len) { + buf->data = (unsigned char *) YA_REALLOC(buf->alloc, buf->data, need); + buf->len = need; + } +} + +yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc) +{ + yajl_buf b = YA_MALLOC(alloc, sizeof(struct yajl_buf_t)); + memset((void *) b, 0, sizeof(struct yajl_buf_t)); + b->alloc = alloc; + return b; +} + +void yajl_buf_free(yajl_buf buf) +{ + assert(buf != NULL); + if (buf->data) YA_FREE(buf->alloc, buf->data); + YA_FREE(buf->alloc, buf); +} + +void yajl_buf_append(yajl_buf buf, const void * data, unsigned int len) +{ + yajl_buf_ensure_available(buf, len); + if (len > 0) { + assert(data != NULL); + memcpy(buf->data + buf->used, data, len); + buf->used += len; + buf->data[buf->used] = 0; + } +} + +void yajl_buf_clear(yajl_buf buf) +{ + buf->used = 0; + if (buf->data) buf->data[buf->used] = 0; +} + +const unsigned char * yajl_buf_data(yajl_buf buf) +{ + return buf->data; +} + +unsigned int yajl_buf_len(yajl_buf buf) +{ + return buf->used; +} + +void +yajl_buf_truncate(yajl_buf buf, unsigned int len) +{ + assert(len <= buf->used); + buf->used = len; +} diff --git a/src/libCom/yajl/yajl_buf.h b/src/libCom/yajl/yajl_buf.h new file mode 100644 index 000000000..e5b2c8231 --- /dev/null +++ b/src/libCom/yajl/yajl_buf.h @@ -0,0 +1,73 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __YAJL_BUF_H__ +#define __YAJL_BUF_H__ + +#include "yajl_common.h" +#include "yajl_alloc.h" + +/* + * Implementation/performance notes. If this were moved to a header + * only implementation using #define's where possible we might be + * able to sqeeze a little performance out of the guy by killing function + * call overhead. YMMV. + */ + +/** + * yajl_buf is a buffer with exponential growth. the buffer ensures that + * you are always null padded. + */ +typedef struct yajl_buf_t * yajl_buf; + +/* allocate a new buffer */ +yajl_buf yajl_buf_alloc(yajl_alloc_funcs * alloc); + +/* free the buffer */ +void yajl_buf_free(yajl_buf buf); + +/* append a number of bytes to the buffer */ +void yajl_buf_append(yajl_buf buf, const void * data, unsigned int len); + +/* empty the buffer */ +void yajl_buf_clear(yajl_buf buf); + +/* get a pointer to the beginning of the buffer */ +const unsigned char * yajl_buf_data(yajl_buf buf); + +/* get the length of the buffer */ +unsigned int yajl_buf_len(yajl_buf buf); + +/* truncate the buffer */ +void yajl_buf_truncate(yajl_buf buf, unsigned int len); + +#endif diff --git a/src/libCom/yajl/yajl_bytestack.h b/src/libCom/yajl/yajl_bytestack.h new file mode 100644 index 000000000..9ce192fcc --- /dev/null +++ b/src/libCom/yajl/yajl_bytestack.h @@ -0,0 +1,85 @@ +/* + * Copyright 2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * A header only implementation of a simple stack of bytes, used in YAJL + * to maintain parse state. + */ + +#ifndef __YAJL_BYTESTACK_H__ +#define __YAJL_BYTESTACK_H__ + +#include "yajl_common.h" + +#define YAJL_BS_INC 128 + +typedef struct yajl_bytestack_t +{ + unsigned char * stack; + unsigned int size; + unsigned int used; + yajl_alloc_funcs * yaf; +} yajl_bytestack; + +/* initialize a bytestack */ +#define yajl_bs_init(obs, _yaf) { \ + (obs).stack = NULL; \ + (obs).size = 0; \ + (obs).used = 0; \ + (obs).yaf = (_yaf); \ + } \ + + +/* initialize a bytestack */ +#define yajl_bs_free(obs) \ + if ((obs).stack) (obs).yaf->free((obs).yaf->ctx, (obs).stack); + +#define yajl_bs_current(obs) \ + (assert((obs).used > 0), (obs).stack[(obs).used - 1]) + +#define yajl_bs_push(obs, byte) { \ + if (((obs).size - (obs).used) == 0) { \ + (obs).size += YAJL_BS_INC; \ + (obs).stack = (obs).yaf->realloc((obs).yaf->ctx,\ + (void *) (obs).stack, (obs).size);\ + } \ + (obs).stack[((obs).used)++] = (byte); \ +} + +/* removes the top item of the stack, returns nothing */ +#define yajl_bs_pop(obs) { ((obs).used)--; } + +#define yajl_bs_set(obs, byte) \ + (obs).stack[((obs).used) - 1] = (byte); + + +#endif diff --git a/src/libCom/yajl/yajl_common.h b/src/libCom/yajl/yajl_common.h new file mode 100644 index 000000000..7cb657470 --- /dev/null +++ b/src/libCom/yajl/yajl_common.h @@ -0,0 +1,76 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __YAJL_COMMON_H__ +#define __YAJL_COMMON_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define YAJL_MAX_DEPTH 128 + +#define YAJL_API epicsShareFunc + +/** pointer to a malloc function, supporting client overriding memory + * allocation routines */ +typedef void * (*yajl_malloc_func)(void *ctx, unsigned int sz); + +/** pointer to a free function, supporting client overriding memory + * allocation routines */ +typedef void (*yajl_free_func)(void *ctx, void * ptr); + +/** pointer to a realloc function which can resize an allocation. */ +typedef void * (*yajl_realloc_func)(void *ctx, void * ptr, unsigned int sz); + +/** A structure which can be passed to yajl_*_alloc routines to allow the + * client to specify memory allocation functions to be used. */ +typedef struct +{ + /** pointer to a function that can allocate uninitialized memory */ + yajl_malloc_func malloc; + /** pointer to a function that can resize memory allocations */ + yajl_realloc_func realloc; + /** pointer to a function that can free memory allocated using + * reallocFunction or mallocFunction */ + yajl_free_func free; + /** a context pointer that will be passed to above allocation routines */ + void * ctx; +} yajl_alloc_funcs; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libCom/yajl/yajl_encode.c b/src/libCom/yajl/yajl_encode.c new file mode 100644 index 000000000..fb85d73d3 --- /dev/null +++ b/src/libCom/yajl/yajl_encode.c @@ -0,0 +1,180 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "yajl_encode.h" + +static void CharToHex(unsigned char c, char * hexBuf) +{ + const char * hexchar = "0123456789ABCDEF"; + hexBuf[0] = hexchar[c >> 4]; + hexBuf[1] = hexchar[c & 0x0F]; +} + +void +yajl_string_encode(yajl_buf buf, const unsigned char * str, + unsigned int len) +{ + unsigned int beg = 0; + unsigned int end = 0; + char hexBuf[7]; + hexBuf[0] = '\\'; hexBuf[1] = 'u'; hexBuf[2] = '0'; hexBuf[3] = '0'; + hexBuf[6] = 0; + + while (end < len) { + const char * escaped = NULL; + switch (str[end]) { + case '\r': escaped = "\\r"; break; + case '\n': escaped = "\\n"; break; + case '\\': escaped = "\\\\"; break; + /* case '/': escaped = "\\/"; break; */ + case '"': escaped = "\\\""; break; + case '\f': escaped = "\\f"; break; + case '\b': escaped = "\\b"; break; + case '\t': escaped = "\\t"; break; + default: + if ((unsigned char) str[end] < 32) { + CharToHex(str[end], hexBuf + 4); + escaped = hexBuf; + } + break; + } + if (escaped != NULL) { + yajl_buf_append(buf, str + beg, end - beg); + yajl_buf_append(buf, escaped, strlen(escaped)); + beg = ++end; + } else { + ++end; + } + } + yajl_buf_append(buf, str + beg, end - beg); +} + +static void hexToDigit(unsigned int * val, const unsigned char * hex) +{ + unsigned int i; + for (i=0;i<4;i++) { + unsigned char c = hex[i]; + if (c >= 'A') c = (c & ~0x20) - 7; + c -= '0'; + assert(!(c & 0xF0)); + *val = (*val << 4) | c; + } +} + +static void Utf32toUtf8(unsigned int codepoint, char * utf8Buf) +{ + if (codepoint < 0x80) { + utf8Buf[0] = (char) codepoint; + utf8Buf[1] = 0; + } else if (codepoint < 0x0800) { + utf8Buf[0] = (char) ((codepoint >> 6) | 0xC0); + utf8Buf[1] = (char) ((codepoint & 0x3F) | 0x80); + utf8Buf[2] = 0; + } else if (codepoint < 0x10000) { + utf8Buf[0] = (char) ((codepoint >> 12) | 0xE0); + utf8Buf[1] = (char) (((codepoint >> 6) & 0x3F) | 0x80); + utf8Buf[2] = (char) ((codepoint & 0x3F) | 0x80); + utf8Buf[3] = 0; + } else if (codepoint < 0x200000) { + utf8Buf[0] =(char)((codepoint >> 18) | 0xF0); + utf8Buf[1] =(char)(((codepoint >> 12) & 0x3F) | 0x80); + utf8Buf[2] =(char)(((codepoint >> 6) & 0x3F) | 0x80); + utf8Buf[3] =(char)((codepoint & 0x3F) | 0x80); + utf8Buf[4] = 0; + } else { + utf8Buf[0] = '?'; + utf8Buf[1] = 0; + } +} + +void yajl_string_decode(yajl_buf buf, const unsigned char * str, + unsigned int len) +{ + unsigned int beg = 0; + unsigned int end = 0; + + while (end < len) { + if (str[end] == '\\') { + char utf8Buf[5]; + const char * unescaped = "?"; + yajl_buf_append(buf, str + beg, end - beg); + switch (str[++end]) { + case 'r': unescaped = "\r"; break; + case 'n': unescaped = "\n"; break; + case '\\': unescaped = "\\"; break; + case '/': unescaped = "/"; break; + case '"': unescaped = "\""; break; + case 'f': unescaped = "\f"; break; + case 'b': unescaped = "\b"; break; + case 't': unescaped = "\t"; break; + case 'u': { + unsigned int codepoint = 0; + hexToDigit(&codepoint, str + ++end); + end+=3; + /* check if this is a surrogate */ + if ((codepoint & 0xFC00) == 0xD800) { + end++; + if (str[end] == '\\' && str[end + 1] == 'u') { + unsigned int surrogate = 0; + hexToDigit(&surrogate, str + end + 2); + codepoint = + (((codepoint & 0x3F) << 10) | + ((((codepoint >> 6) & 0xF) + 1) << 16) | + (surrogate & 0x3FF)); + end += 5; + } else { + unescaped = "?"; + break; + } + } + + Utf32toUtf8(codepoint, utf8Buf); + unescaped = utf8Buf; + break; + } + default: + assert("this should never happen" == NULL); + } + yajl_buf_append(buf, unescaped, strlen(unescaped)); + beg = ++end; + } else { + end++; + } + } + yajl_buf_append(buf, str + beg, end - beg); +} diff --git a/src/libCom/yajl/yajl_encode.h b/src/libCom/yajl/yajl_encode.h new file mode 100644 index 000000000..8bd01af5a --- /dev/null +++ b/src/libCom/yajl/yajl_encode.h @@ -0,0 +1,44 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __YAJL_ENCODE_H__ +#define __YAJL_ENCODE_H__ + +#include "yajl_buf.h" + +void yajl_string_encode(yajl_buf buf, const unsigned char * str, + unsigned int length); + +void yajl_string_decode(yajl_buf buf, const unsigned char * str, + unsigned int length); + +#endif diff --git a/src/libCom/yajl/yajl_gen.c b/src/libCom/yajl/yajl_gen.c new file mode 100644 index 000000000..7e353db42 --- /dev/null +++ b/src/libCom/yajl/yajl_gen.c @@ -0,0 +1,296 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include + +#define epicsExportSharedSymbols +#include "yajl_gen.h" +#include "yajl_buf.h" +#include "yajl_encode.h" + +typedef enum { + yajl_gen_start, + yajl_gen_map_start, + yajl_gen_map_key, + yajl_gen_map_val, + yajl_gen_array_start, + yajl_gen_in_array, + yajl_gen_complete, + yajl_gen_error +} yajl_gen_state; + +struct yajl_gen_t +{ + unsigned int depth; + unsigned int pretty; + const char * indentString; + yajl_gen_state state[YAJL_MAX_DEPTH]; + yajl_buf buf; + /* memory allocation routines */ + yajl_alloc_funcs alloc; +}; + +yajl_gen +yajl_gen_alloc(const yajl_gen_config * config, + const yajl_alloc_funcs * afs) +{ + yajl_gen g = NULL; + yajl_alloc_funcs afsBuffer; + + /* first order of business is to set up memory allocation routines */ + if (afs != NULL) { + if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL) + { + return NULL; + } + } else { + yajl_set_default_alloc_funcs(&afsBuffer); + afs = &afsBuffer; + } + + g = (yajl_gen) YA_MALLOC(afs, sizeof(struct yajl_gen_t)); + memset((void *) g, 0, sizeof(struct yajl_gen_t)); + /* copy in pointers to allocation routines */ + memcpy((void *) &(g->alloc), (void *) afs, sizeof(yajl_alloc_funcs)); + + if (config) { + g->pretty = config->beautify; + g->indentString = config->indentString ? config->indentString : " "; + } + g->buf = yajl_buf_alloc(&(g->alloc)); + + return g; +} + +void +yajl_gen_free(yajl_gen g) +{ + yajl_buf_free(g->buf); + YA_FREE(&(g->alloc), g); +} + +#define INSERT_SEP \ + if (g->state[g->depth] == yajl_gen_map_key || \ + g->state[g->depth] == yajl_gen_in_array) { \ + yajl_buf_append(g->buf, ",", 1); \ + if (g->pretty) yajl_buf_append(g->buf, "\n", 1); \ + } else if (g->state[g->depth] == yajl_gen_map_val) { \ + yajl_buf_append(g->buf, ":", 1); \ + if (g->pretty) yajl_buf_append(g->buf, " ", 1); \ + } + +#define INSERT_WHITESPACE \ + if (g->pretty) { \ + if (g->state[g->depth] != yajl_gen_map_val) { \ + unsigned int _i; \ + for (_i=0;_idepth;_i++) \ + yajl_buf_append(g->buf, g->indentString, \ + strlen(g->indentString)); \ + } \ + } + +#define ENSURE_NOT_KEY \ + if (g->state[g->depth] == yajl_gen_map_key) { \ + return yajl_gen_keys_must_be_strings; \ + } \ + +/* check that we're not complete, or in error state. in a valid state + * to be generating */ +#define ENSURE_VALID_STATE \ + if (g->state[g->depth] == yajl_gen_error) { \ + return yajl_gen_in_error_state;\ + } else if (g->state[g->depth] == yajl_gen_complete) { \ + return yajl_gen_generation_complete; \ + } + +#define INCREMENT_DEPTH \ + if (++(g->depth) >= YAJL_MAX_DEPTH) return yajl_max_depth_exceeded; + +#define APPENDED_ATOM \ + switch (g->state[g->depth]) { \ + case yajl_gen_start: \ + g->state[g->depth] = yajl_gen_complete; \ + break; \ + case yajl_gen_map_start: \ + case yajl_gen_map_key: \ + g->state[g->depth] = yajl_gen_map_val; \ + break; \ + case yajl_gen_array_start: \ + g->state[g->depth] = yajl_gen_in_array; \ + break; \ + case yajl_gen_map_val: \ + g->state[g->depth] = yajl_gen_map_key; \ + break; \ + default: \ + break; \ + } \ + +#define FINAL_NEWLINE \ + if (g->pretty && g->state[g->depth] == yajl_gen_complete) \ + yajl_buf_append(g->buf, "\n", 1); + +yajl_gen_status +yajl_gen_integer(yajl_gen g, long int number) +{ + char i[32]; + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + sprintf(i, "%ld", number); + yajl_buf_append(g->buf, i, strlen(i)); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_double(yajl_gen g, double number) +{ + char i[32]; + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + sprintf(i, "%g", number); + yajl_buf_append(g->buf, i, strlen(i)); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_number(yajl_gen g, const char * s, unsigned int l) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + yajl_buf_append(g->buf, s, l); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_string(yajl_gen g, const unsigned char * str, + unsigned int len) +{ + ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE; + yajl_buf_append(g->buf, "\"", 1); + yajl_string_encode(g->buf, str, len); + yajl_buf_append(g->buf, "\"", 1); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_null(yajl_gen g) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + yajl_buf_append(g->buf, "null", strlen("null")); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_bool(yajl_gen g, int boolean) +{ + const char * val = boolean ? "true" : "false"; + + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + yajl_buf_append(g->buf, val, strlen(val)); + APPENDED_ATOM; + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_map_open(yajl_gen g) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + INCREMENT_DEPTH; + + g->state[g->depth] = yajl_gen_map_start; + yajl_buf_append(g->buf, "{", 1); + if (g->pretty) yajl_buf_append(g->buf, "\n", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_map_close(yajl_gen g) +{ + ENSURE_VALID_STATE; + (g->depth)--; + if (g->pretty) yajl_buf_append(g->buf, "\n", 1); + APPENDED_ATOM; + INSERT_WHITESPACE; + yajl_buf_append(g->buf, "}", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_array_open(yajl_gen g) +{ + ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + INCREMENT_DEPTH; + g->state[g->depth] = yajl_gen_array_start; + yajl_buf_append(g->buf, "[", 1); + if (g->pretty) yajl_buf_append(g->buf, "\n", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_array_close(yajl_gen g) +{ + ENSURE_VALID_STATE; + if (g->pretty) yajl_buf_append(g->buf, "\n", 1); + (g->depth)--; + APPENDED_ATOM; + INSERT_WHITESPACE; + yajl_buf_append(g->buf, "]", 1); + FINAL_NEWLINE; + return yajl_gen_status_ok; +} + +yajl_gen_status +yajl_gen_get_buf(yajl_gen g, const unsigned char ** buf, + unsigned int * len) +{ + *buf = yajl_buf_data(g->buf); + *len = yajl_buf_len(g->buf); + return yajl_gen_status_ok; +} + +void +yajl_gen_clear(yajl_gen g) +{ + yajl_buf_clear(g->buf); +} diff --git a/src/libCom/yajl/yajl_gen.h b/src/libCom/yajl/yajl_gen.h new file mode 100644 index 000000000..8b814241c --- /dev/null +++ b/src/libCom/yajl/yajl_gen.h @@ -0,0 +1,123 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file yajl_gen.h + * Interface to YAJL's JSON generation facilities. + */ + +#ifndef __YAJL_GEN_H__ +#define __YAJL_GEN_H__ + +#include "yajl_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + /** generator status codes */ + typedef enum { + /** no error */ + yajl_gen_status_ok = 0, + /** at a point where a map key is generated, a function other than + * yajl_gen_string was called */ + yajl_gen_keys_must_be_strings, + /** YAJL's maximum generation depth was exceeded. see + * YAJL_MAX_DEPTH */ + yajl_max_depth_exceeded, + /** A generator function (yajl_gen_XXX) was called while in an error + * state */ + yajl_gen_in_error_state, + /** A complete JSON document has been generated */ + yajl_gen_generation_complete + } yajl_gen_status; + + /** an opaque handle to a generator */ + typedef struct yajl_gen_t * yajl_gen; + + /** configuration structure for the generator */ + typedef struct { + /** generate indented (beautiful) output */ + unsigned int beautify; + /** an opportunity to define an indent string. such as \\t or + * some number of spaces. default is four spaces ' '. This + * member is only relevant when beautify is true */ + const char * indentString; + } yajl_gen_config; + + /** allocate a generator handle + * \param config a pointer to a structure containing parameters which + * configure the behavior of the json generator + * \param allocFuncs an optional pointer to a structure which allows + * the client to overide the memory allocation + * used by yajl. May be NULL, in which case + * malloc/free/realloc will be used. + * + * \returns an allocated handle on success, NULL on failure (bad params) + */ + yajl_gen YAJL_API yajl_gen_alloc(const yajl_gen_config * config, + const yajl_alloc_funcs * allocFuncs); + + /** free a generator handle */ + void YAJL_API yajl_gen_free(yajl_gen handle); + + yajl_gen_status YAJL_API yajl_gen_integer(yajl_gen hand, long int number); + yajl_gen_status YAJL_API yajl_gen_double(yajl_gen hand, double number); + yajl_gen_status YAJL_API yajl_gen_number(yajl_gen hand, + const char * num, + unsigned int len); + yajl_gen_status YAJL_API yajl_gen_string(yajl_gen hand, + const unsigned char * str, + unsigned int len); + yajl_gen_status YAJL_API yajl_gen_null(yajl_gen hand); + yajl_gen_status YAJL_API yajl_gen_bool(yajl_gen hand, int boolean); + yajl_gen_status YAJL_API yajl_gen_map_open(yajl_gen hand); + yajl_gen_status YAJL_API yajl_gen_map_close(yajl_gen hand); + yajl_gen_status YAJL_API yajl_gen_array_open(yajl_gen hand); + yajl_gen_status YAJL_API yajl_gen_array_close(yajl_gen hand); + + /** access the null terminated generator buffer. If incrementally + * outputing JSON, one should call yajl_gen_clear to clear the + * buffer. This allows stream generation. */ + yajl_gen_status YAJL_API yajl_gen_get_buf(yajl_gen hand, + const unsigned char ** buf, + unsigned int * len); + + /** clear yajl's output buffer, but maintain all internal generation + * state. This function will not "reset" the generator state, and is + * intended to enable incremental JSON outputing. */ + void YAJL_API yajl_gen_clear(yajl_gen hand); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libCom/yajl/yajl_lex.c b/src/libCom/yajl/yajl_lex.c new file mode 100644 index 000000000..be83887ff --- /dev/null +++ b/src/libCom/yajl/yajl_lex.c @@ -0,0 +1,738 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "yajl_lex.h" +#include "yajl_buf.h" + +#ifdef YAJL_LEXER_DEBUG +static const char * +tokToStr(yajl_tok tok) +{ + switch (tok) { + case yajl_tok_bool: return "bool"; + case yajl_tok_colon: return "colon"; + case yajl_tok_comma: return "comma"; + case yajl_tok_eof: return "eof"; + case yajl_tok_error: return "error"; + case yajl_tok_left_brace: return "brace"; + case yajl_tok_left_bracket: return "bracket"; + case yajl_tok_null: return "null"; + case yajl_tok_integer: return "integer"; + case yajl_tok_double: return "double"; + case yajl_tok_right_brace: return "brace"; + case yajl_tok_right_bracket: return "bracket"; + case yajl_tok_string: return "string"; + case yajl_tok_string_with_escapes: return "string_with_escapes"; + } + return "unknown"; +} +#endif + +/* Impact of the stream parsing feature on the lexer: + * + * YAJL support stream parsing. That is, the ability to parse the first + * bits of a chunk of JSON before the last bits are available (still on + * the network or disk). This makes the lexer more complex. The + * responsibility of the lexer is to handle transparently the case where + * a chunk boundary falls in the middle of a token. This is + * accomplished is via a buffer and a character reading abstraction. + * + * Overview of implementation + * + * When we lex to end of input string before end of token is hit, we + * copy all of the input text composing the token into our lexBuf. + * + * Every time we read a character, we do so through the readChar function. + * readChar's responsibility is to handle pulling all chars from the buffer + * before pulling chars from input text + */ + +struct yajl_lexer_t { + /* the overal line and char offset into the data */ + unsigned int lineOff; + unsigned int charOff; + + /* error */ + yajl_lex_error error; + + /* a input buffer to handle the case where a token is spread over + * multiple chunks */ + yajl_buf buf; + + /* in the case where we have data in the lexBuf, bufOff holds + * the current offset into the lexBuf. */ + unsigned int bufOff; + + /* are we using the lex buf? */ + unsigned int bufInUse; + + /* shall we allow comments? */ + unsigned int allowComments; + + /* shall we validate utf8 inside strings? */ + unsigned int validateUTF8; + + yajl_alloc_funcs * alloc; +}; + +#define readChar(lxr, txt, off) \ + (((lxr)->bufInUse && yajl_buf_len((lxr)->buf) && lxr->bufOff < yajl_buf_len((lxr)->buf)) ? \ + (*((const unsigned char *) yajl_buf_data((lxr)->buf) + ((lxr)->bufOff)++)) : \ + ((txt)[(*(off))++])) + +#define unreadChar(lxr, off) ((*(off) > 0) ? (*(off))-- : ((lxr)->bufOff--)) + +yajl_lexer +yajl_lex_alloc(yajl_alloc_funcs * alloc, + unsigned int allowComments, unsigned int validateUTF8) +{ + yajl_lexer lxr = (yajl_lexer) YA_MALLOC(alloc, sizeof(struct yajl_lexer_t)); + memset((void *) lxr, 0, sizeof(struct yajl_lexer_t)); + lxr->buf = yajl_buf_alloc(alloc); + lxr->allowComments = allowComments; + lxr->validateUTF8 = validateUTF8; + lxr->alloc = alloc; + return lxr; +} + +void +yajl_lex_free(yajl_lexer lxr) +{ + yajl_buf_free(lxr->buf); + YA_FREE(lxr->alloc, lxr); + return; +} + +/* a lookup table which lets us quickly determine three things: + * VEC - valid escaped conrol char + * IJC - invalid json char + * VHC - valid hex char + * note. the solidus '/' may be escaped or not. + * note. the + */ +#define VEC 1 +#define IJC 2 +#define VHC 4 +static const char charLookupTable[256] = +{ +/*00*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , +/*08*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , +/*10*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , +/*18*/ IJC , IJC , IJC , IJC , IJC , IJC , IJC , IJC , + +/*20*/ 0 , 0 , VEC|IJC, 0 , 0 , 0 , 0 , 0 , +/*28*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , VEC , +/*30*/ VHC , VHC , VHC , VHC , VHC , VHC , VHC , VHC , +/*38*/ VHC , VHC , 0 , 0 , 0 , 0 , 0 , 0 , + +/*40*/ 0 , VHC , VHC , VHC , VHC , VHC , VHC , 0 , +/*48*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , +/*50*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , +/*58*/ 0 , 0 , 0 , 0 , VEC|IJC, 0 , 0 , 0 , + +/*60*/ 0 , VHC , VEC|VHC, VHC , VHC , VHC , VEC|VHC, 0 , +/*68*/ 0 , 0 , 0 , 0 , 0 , 0 , VEC , 0 , +/*70*/ 0 , 0 , VEC , 0 , VEC , 0 , 0 , 0 , +/*78*/ 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + +/* include these so we don't have to always check the range of the char */ + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 +}; + +/** process a variable length utf8 encoded codepoint. + * + * returns: + * yajl_tok_string - if valid utf8 char was parsed and offset was + * advanced + * yajl_tok_eof - if end of input was hit before validation could + * complete + * yajl_tok_error - if invalid utf8 was encountered + * + * NOTE: on error the offset will point to the first char of the + * invalid utf8 */ +#define UTF8_CHECK_EOF if (*offset >= jsonTextLen) { return yajl_tok_eof; } + +static yajl_tok +yajl_lex_utf8_char(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * offset, + unsigned char curChar) +{ + if (curChar <= 0x7f) { + /* single byte */ + return yajl_tok_string; + } else if ((curChar >> 5) == 0x6) { + /* two byte */ + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) return yajl_tok_string; + } else if ((curChar >> 4) == 0x0e) { + /* three byte */ + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) { + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) return yajl_tok_string; + } + } else if ((curChar >> 3) == 0x1e) { + /* four byte */ + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) { + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) { + UTF8_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if ((curChar >> 6) == 0x2) return yajl_tok_string; + } + } + } + + return yajl_tok_error; +} + +/* lex a string. input is the lexer, pointer to beginning of + * json text, and start of string (offset). + * a token is returned which has the following meanings: + * yajl_tok_string: lex of string was successful. offset points to + * terminating '"'. + * yajl_tok_eof: end of text was encountered before we could complete + * the lex. + * yajl_tok_error: embedded in the string were unallowable chars. offset + * points to the offending char + */ +#define STR_CHECK_EOF \ +if (*offset >= jsonTextLen) { \ + tok = yajl_tok_eof; \ + goto finish_string_lex; \ +} + +static yajl_tok +yajl_lex_string(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * offset) +{ + yajl_tok tok = yajl_tok_error; + int hasEscapes = 0; + + for (;;) { + unsigned char curChar; + + STR_CHECK_EOF; + + curChar = readChar(lexer, jsonText, offset); + + /* quote terminates */ + if (curChar == '"') { + tok = yajl_tok_string; + break; + } + /* backslash escapes a set of control chars, */ + else if (curChar == '\\') { + hasEscapes = 1; + STR_CHECK_EOF; + + /* special case \u */ + curChar = readChar(lexer, jsonText, offset); + if (curChar == 'u') { + unsigned int i = 0; + + for (i=0;i<4;i++) { + STR_CHECK_EOF; + curChar = readChar(lexer, jsonText, offset); + if (!(charLookupTable[curChar] & VHC)) { + /* back up to offending char */ + unreadChar(lexer, offset); + lexer->error = yajl_lex_string_invalid_hex_char; + goto finish_string_lex; + } + } + } else if (!(charLookupTable[curChar] & VEC)) { + /* back up to offending char */ + unreadChar(lexer, offset); + lexer->error = yajl_lex_string_invalid_escaped_char; + goto finish_string_lex; + } + } + /* when not validating UTF8 it's a simple table lookup to determine + * if the present character is invalid */ + else if(charLookupTable[curChar] & IJC) { + /* back up to offending char */ + unreadChar(lexer, offset); + lexer->error = yajl_lex_string_invalid_json_char; + goto finish_string_lex; + } + /* when in validate UTF8 mode we need to do some extra work */ + else if (lexer->validateUTF8) { + yajl_tok t = yajl_lex_utf8_char(lexer, jsonText, jsonTextLen, + offset, curChar); + + if (t == yajl_tok_eof) { + tok = yajl_tok_eof; + goto finish_string_lex; + } else if (t == yajl_tok_error) { + lexer->error = yajl_lex_string_invalid_utf8; + goto finish_string_lex; + } + } + /* accept it, and move on */ + } + finish_string_lex: + /* tell our buddy, the parser, wether he needs to process this string + * again */ + if (hasEscapes && tok == yajl_tok_string) { + tok = yajl_tok_string_with_escapes; + } + + return tok; +} + +#define RETURN_IF_EOF if (*offset >= jsonTextLen) return yajl_tok_eof; + +static yajl_tok +yajl_lex_number(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * offset) +{ + /** XXX: numbers are the only entities in json that we must lex + * _beyond_ in order to know that they are complete. There + * is an ambiguous case for integers at EOF. */ + + unsigned char c; + + yajl_tok tok = yajl_tok_integer; + + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + /* optional leading minus */ + if (c == '-') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } + + /* a single zero, or a series of integers */ + if (c == '0') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } else if (c >= '1' && c <= '9') { + do { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } while (c >= '0' && c <= '9'); + } else { + unreadChar(lexer, offset); + lexer->error = yajl_lex_missing_integer_after_minus; + return yajl_tok_error; + } + + /* optional fraction (indicates this is floating point) */ + if (c == '.') { + int numRd = 0; + + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + while (c >= '0' && c <= '9') { + numRd++; + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } + + if (!numRd) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_missing_integer_after_decimal; + return yajl_tok_error; + } + tok = yajl_tok_double; + } + + /* optional exponent (indicates this is floating point) */ + if (c == 'e' || c == 'E') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + /* optional sign */ + if (c == '+' || c == '-') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } + + if (c >= '0' && c <= '9') { + do { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } while (c >= '0' && c <= '9'); + } else { + unreadChar(lexer, offset); + lexer->error = yajl_lex_missing_integer_after_exponent; + return yajl_tok_error; + } + tok = yajl_tok_double; + } + + /* we always go "one too far" */ + unreadChar(lexer, offset); + + return tok; +} + +static yajl_tok +yajl_lex_comment(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * offset) +{ + unsigned char c; + + yajl_tok tok = yajl_tok_comment; + + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + + /* either slash or star expected */ + if (c == '/') { + /* now we throw away until end of line */ + do { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + } while (c != '\n'); + } else if (c == '*') { + /* now we throw away until end of comment */ + for (;;) { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + if (c == '*') { + RETURN_IF_EOF; + c = readChar(lexer, jsonText, offset); + if (c == '/') { + break; + } else { + unreadChar(lexer, offset); + } + } + } + } else { + lexer->error = yajl_lex_invalid_char; + tok = yajl_tok_error; + } + + return tok; +} + +yajl_tok +yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * offset, + const unsigned char ** outBuf, unsigned int * outLen) +{ + yajl_tok tok = yajl_tok_error; + unsigned char c; + unsigned int startOffset = *offset; + + *outBuf = NULL; + *outLen = 0; + + for (;;) { + assert(*offset <= jsonTextLen); + + if (*offset >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + + c = readChar(lexer, jsonText, offset); + + switch (c) { + case '{': + tok = yajl_tok_left_bracket; + goto lexed; + case '}': + tok = yajl_tok_right_bracket; + goto lexed; + case '[': + tok = yajl_tok_left_brace; + goto lexed; + case ']': + tok = yajl_tok_right_brace; + goto lexed; + case ',': + tok = yajl_tok_comma; + goto lexed; + case ':': + tok = yajl_tok_colon; + goto lexed; + case '\t': case '\n': case '\v': case '\f': case '\r': case ' ': + startOffset++; + break; + case 't': { + const char * want = "rue"; + do { + if (*offset >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + c = readChar(lexer, jsonText, offset); + if (c != *want) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_invalid_string; + tok = yajl_tok_error; + goto lexed; + } + } while (*(++want)); + tok = yajl_tok_bool; + goto lexed; + } + case 'f': { + const char * want = "alse"; + do { + if (*offset >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + c = readChar(lexer, jsonText, offset); + if (c != *want) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_invalid_string; + tok = yajl_tok_error; + goto lexed; + } + } while (*(++want)); + tok = yajl_tok_bool; + goto lexed; + } + case 'n': { + const char * want = "ull"; + do { + if (*offset >= jsonTextLen) { + tok = yajl_tok_eof; + goto lexed; + } + c = readChar(lexer, jsonText, offset); + if (c != *want) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_invalid_string; + tok = yajl_tok_error; + goto lexed; + } + } while (*(++want)); + tok = yajl_tok_null; + goto lexed; + } + case '"': { + tok = yajl_lex_string(lexer, (const unsigned char *) jsonText, + jsonTextLen, offset); + goto lexed; + } + case '-': + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + /* integer parsing wants to start from the beginning */ + unreadChar(lexer, offset); + tok = yajl_lex_number(lexer, (const unsigned char *) jsonText, + jsonTextLen, offset); + goto lexed; + } + case '/': + /* hey, look, a probable comment! If comments are disabled + * it's an error. */ + if (!lexer->allowComments) { + unreadChar(lexer, offset); + lexer->error = yajl_lex_unallowed_comment; + tok = yajl_tok_error; + goto lexed; + } + /* if comments are enabled, then we should try to lex + * the thing. possible outcomes are + * - successful lex (tok_comment, which means continue), + * - malformed comment opening (slash not followed by + * '*' or '/') (tok_error) + * - eof hit. (tok_eof) */ + tok = yajl_lex_comment(lexer, (const unsigned char *) jsonText, + jsonTextLen, offset); + if (tok == yajl_tok_comment) { + /* "error" is silly, but that's the initial + * state of tok. guilty until proven innocent. */ + tok = yajl_tok_error; + yajl_buf_clear(lexer->buf); + lexer->bufInUse = 0; + startOffset = *offset; + break; + } + /* hit error or eof, bail */ + goto lexed; + default: + lexer->error = yajl_lex_invalid_char; + tok = yajl_tok_error; + goto lexed; + } + } + + + lexed: + /* need to append to buffer if the buffer is in use or + * if it's an EOF token */ + if (tok == yajl_tok_eof || lexer->bufInUse) { + if (!lexer->bufInUse) yajl_buf_clear(lexer->buf); + lexer->bufInUse = 1; + yajl_buf_append(lexer->buf, jsonText + startOffset, *offset - startOffset); + lexer->bufOff = 0; + + if (tok != yajl_tok_eof) { + *outBuf = yajl_buf_data(lexer->buf); + *outLen = yajl_buf_len(lexer->buf); + lexer->bufInUse = 0; + } + } else if (tok != yajl_tok_error) { + *outBuf = jsonText + startOffset; + *outLen = *offset - startOffset; + } + + /* special case for strings. skip the quotes. */ + if (tok == yajl_tok_string || tok == yajl_tok_string_with_escapes) + { + assert(*outLen >= 2); + (*outBuf)++; + *outLen -= 2; + } + + +#ifdef YAJL_LEXER_DEBUG + if (tok == yajl_tok_error) { + printf("lexical error: %s\n", + yajl_lex_error_to_string(yajl_lex_get_error(lexer))); + } else if (tok == yajl_tok_eof) { + printf("EOF hit\n"); + } else { + printf("lexed %s: '", tokToStr(tok)); + fwrite(*outBuf, 1, *outLen, stdout); + printf("'\n"); + } +#endif + + return tok; +} + +const char * +yajl_lex_error_to_string(yajl_lex_error error) +{ + switch (error) { + case yajl_lex_e_ok: + return "ok, no error"; + case yajl_lex_string_invalid_utf8: + return "invalid bytes in UTF8 string."; + case yajl_lex_string_invalid_escaped_char: + return "inside a string, '\\' occurs before a character " + "which it may not."; + case yajl_lex_string_invalid_json_char: + return "invalid character inside string."; + case yajl_lex_string_invalid_hex_char: + return "invalid (non-hex) character occurs after '\\u' inside " + "string."; + case yajl_lex_invalid_char: + return "invalid char in json text."; + case yajl_lex_invalid_string: + return "invalid string in json text."; + case yajl_lex_missing_integer_after_exponent: + return "malformed number, a digit is required after the exponent."; + case yajl_lex_missing_integer_after_decimal: + return "malformed number, a digit is required after the " + "decimal point."; + case yajl_lex_missing_integer_after_minus: + return "malformed number, a digit is required after the " + "minus sign."; + case yajl_lex_unallowed_comment: + return "probable comment found in input text, comments are " + "not enabled."; + } + return "unknown error code"; +} + + +/** allows access to more specific information about the lexical + * error when yajl_lex_lex returns yajl_tok_error. */ +yajl_lex_error +yajl_lex_get_error(yajl_lexer lexer) +{ + if (lexer == NULL) return (yajl_lex_error) -1; + return lexer->error; +} + +unsigned int yajl_lex_current_line(yajl_lexer lexer) +{ + return lexer->lineOff; +} + +unsigned int yajl_lex_current_char(yajl_lexer lexer) +{ + return lexer->charOff; +} + +yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int offset) +{ + const unsigned char * outBuf; + unsigned int outLen; + unsigned int bufLen = yajl_buf_len(lexer->buf); + unsigned int bufOff = lexer->bufOff; + unsigned int bufInUse = lexer->bufInUse; + yajl_tok tok; + + tok = yajl_lex_lex(lexer, jsonText, jsonTextLen, &offset, + &outBuf, &outLen); + + lexer->bufOff = bufOff; + lexer->bufInUse = bufInUse; + yajl_buf_truncate(lexer->buf, bufLen); + + return tok; +} diff --git a/src/libCom/yajl/yajl_lex.h b/src/libCom/yajl/yajl_lex.h new file mode 100644 index 000000000..658885241 --- /dev/null +++ b/src/libCom/yajl/yajl_lex.h @@ -0,0 +1,133 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __YAJL_LEX_H__ +#define __YAJL_LEX_H__ + +#include "yajl_common.h" + +typedef enum { + yajl_tok_bool, + yajl_tok_colon, + yajl_tok_comma, + yajl_tok_eof, + yajl_tok_error, + yajl_tok_left_brace, + yajl_tok_left_bracket, + yajl_tok_null, + yajl_tok_right_brace, + yajl_tok_right_bracket, + + /* we differentiate between integers and doubles to allow the + * parser to interpret the number without re-scanning */ + yajl_tok_integer, + yajl_tok_double, + + /* we differentiate between strings which require further processing, + * and strings that do not */ + yajl_tok_string, + yajl_tok_string_with_escapes, + + /* comment tokens are not currently returned to the parser, ever */ + yajl_tok_comment +} yajl_tok; + +typedef struct yajl_lexer_t * yajl_lexer; + +yajl_lexer yajl_lex_alloc(yajl_alloc_funcs * alloc, + unsigned int allowComments, + unsigned int validateUTF8); + +void yajl_lex_free(yajl_lexer lexer); + +/** + * run/continue a lex. "offset" is an input/output parameter. + * It should be initialized to zero for a + * new chunk of target text, and upon subsetquent calls with the same + * target text should passed with the value of the previous invocation. + * + * the client may be interested in the value of offset when an error is + * returned from the lexer. This allows the client to render useful +n * error messages. + * + * When you pass the next chunk of data, context should be reinitialized + * to zero. + * + * Finally, the output buffer is usually just a pointer into the jsonText, + * however in cases where the entity being lexed spans multiple chunks, + * the lexer will buffer the entity and the data returned will be + * a pointer into that buffer. + * + * This behavior is abstracted from client code except for the performance + * implications which require that the client choose a reasonable chunk + * size to get adequate performance. + */ +yajl_tok yajl_lex_lex(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int * offset, + const unsigned char ** outBuf, unsigned int * outLen); + +/** have a peek at the next token, but don't move the lexer forward */ +yajl_tok yajl_lex_peek(yajl_lexer lexer, const unsigned char * jsonText, + unsigned int jsonTextLen, unsigned int offset); + + +typedef enum { + yajl_lex_e_ok = 0, + yajl_lex_string_invalid_utf8, + yajl_lex_string_invalid_escaped_char, + yajl_lex_string_invalid_json_char, + yajl_lex_string_invalid_hex_char, + yajl_lex_invalid_char, + yajl_lex_invalid_string, + yajl_lex_missing_integer_after_decimal, + yajl_lex_missing_integer_after_exponent, + yajl_lex_missing_integer_after_minus, + yajl_lex_unallowed_comment +} yajl_lex_error; + +const char * yajl_lex_error_to_string(yajl_lex_error error); + +/** allows access to more specific information about the lexical + * error when yajl_lex_lex returns yajl_tok_error. */ +yajl_lex_error yajl_lex_get_error(yajl_lexer lexer); + +/** get the current offset into the most recently lexed json string. */ +unsigned int yajl_lex_current_offset(yajl_lexer lexer); + +/** get the number of lines lexed by this lexer instance */ +unsigned int yajl_lex_current_line(yajl_lexer lexer); + +/** get the number of chars lexed by this lexer instance since the last + * \n or \r */ +unsigned int yajl_lex_current_char(yajl_lexer lexer); + +#endif diff --git a/src/libCom/yajl/yajl_parse.h b/src/libCom/yajl/yajl_parse.h new file mode 100644 index 000000000..64d484013 --- /dev/null +++ b/src/libCom/yajl/yajl_parse.h @@ -0,0 +1,179 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file yajl_parse.h + * Interface to YAJL's JSON parsing facilities. + */ + +#ifndef __YAJL_PARSE_H__ +#define __YAJL_PARSE_H__ + +#include "yajl_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + /** error codes returned from this interface */ + typedef enum { + /** no error was encountered */ + yajl_status_ok, + /** a client callback returned zero, stopping the parse */ + yajl_status_client_canceled, + /** The parse cannot yet complete because more json input text + * is required, call yajl_parse with the next buffer of input text. + * (pertinent only when stream parsing) */ + yajl_status_insufficient_data, + /** An error occured during the parse. Call yajl_get_error for + * more information about the encountered error */ + yajl_status_error + } yajl_status; + + /** attain a human readable, english, string for an error */ + YAJL_API const char * yajl_status_to_string(yajl_status code); + + /** an opaque handle to a parser */ + typedef struct yajl_handle_t * yajl_handle; + + /** yajl is an event driven parser. this means as json elements are + * parsed, you are called back to do something with the data. The + * functions in this table indicate the various events for which + * you will be called back. Each callback accepts a "context" + * pointer, this is a void * that is passed into the yajl_parse + * function which the client code may use to pass around context. + * + * All callbacks return an integer. If non-zero, the parse will + * continue. If zero, the parse will be canceled and + * yajl_status_client_canceled will be returned from the parse. + * + * Note about handling of numbers: + * yajl will only convert numbers that can be represented in a double + * or a long int. All other numbers will be passed to the client + * in string form using the yajl_number callback. Furthermore, if + * yajl_number is not NULL, it will always be used to return numbers, + * that is yajl_integer and yajl_double will be ignored. If + * yajl_number is NULL but one of yajl_integer or yajl_double are + * defined, parsing of a number larger than is representable + * in a double or long int will result in a parse error. + */ + typedef struct { + int (* yajl_null)(void * ctx); + int (* yajl_boolean)(void * ctx, int boolVal); + int (* yajl_integer)(void * ctx, long integerVal); + int (* yajl_double)(void * ctx, double doubleVal); + /** A callback which passes the string representation of the number + * back to the client. Will be used for all numbers when present */ + int (* yajl_number)(void * ctx, const char * numberVal, + unsigned int numberLen); + + /** strings are returned as pointers into the JSON text when, + * possible, as a result, they are _not_ null padded */ + int (* yajl_string)(void * ctx, const unsigned char * stringVal, + unsigned int stringLen); + + int (* yajl_start_map)(void * ctx); + int (* yajl_map_key)(void * ctx, const unsigned char * key, + unsigned int stringLen); + int (* yajl_end_map)(void * ctx); + + int (* yajl_start_array)(void * ctx); + int (* yajl_end_array)(void * ctx); + } yajl_callbacks; + + /** configuration structure for the generator */ + typedef struct { + /** if nonzero, javascript style comments will be allowed in + * the json input, both slash star and slash slash */ + unsigned int allowComments; + /** if nonzero, invalid UTF8 strings will cause a parse + * error */ + unsigned int checkUTF8; + } yajl_parser_config; + + /** allocate a parser handle + * \param callbacks a yajl callbacks structure specifying the + * functions to call when different JSON entities + * are encountered in the input text. May be NULL, + * which is only useful for validation. + * \param config configuration parameters for the parse. + * \param ctx a context pointer that will be passed to callbacks. + */ + yajl_handle YAJL_API yajl_alloc(const yajl_callbacks * callbacks, + const yajl_parser_config * config, + const yajl_alloc_funcs * allocFuncs, + void * ctx); + + /** free a parser handle */ + void YAJL_API yajl_free(yajl_handle handle); + + /** Parse some json! + * \param hand - a handle to the json parser allocated with yajl_alloc + * \param jsonText - a pointer to the UTF8 json text to be parsed + * \param jsonTextLength - the length, in bytes, of input text + */ + yajl_status YAJL_API yajl_parse(yajl_handle hand, + const unsigned char * jsonText, + unsigned int jsonTextLength); + + /** Parse any remaining buffered json. + * Since yajl is a stream-based parser, without an explicit end of + * input, yajl sometimes can't decide if content at the end of the + * stream is valid or not. For example, if "1" has been fed in, + * yajl can't know whether another digit is next or some character + * that would terminate the integer token. + * + * \param hand - a handle to the json parser allocated with yajl_alloc + */ + yajl_status yajl_parse_complete(yajl_handle hand); + + /** get an error string describing the state of the + * parse. + * + * If verbose is non-zero, the message will include the JSON + * text where the error occured, along with an arrow pointing to + * the specific char. + * + * A dynamically allocated string will be returned which should + * be freed with yajl_free_error + */ + YAJL_API unsigned char * yajl_get_error(yajl_handle hand, int verbose, + const unsigned char * jsonText, + unsigned int jsonTextLength); + + /** free an error returned from yajl_get_error */ + void YAJL_API yajl_free_error(yajl_handle hand, unsigned char * str); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/libCom/yajl/yajl_parser.c b/src/libCom/yajl/yajl_parser.c new file mode 100644 index 000000000..e1d5f2bf1 --- /dev/null +++ b/src/libCom/yajl/yajl_parser.c @@ -0,0 +1,446 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "yajl_lex.h" +#include "yajl_parser.h" +#include "yajl_encode.h" +#include "yajl_bytestack.h" + +unsigned char * +yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText, + unsigned int jsonTextLen, int verbose) +{ + unsigned int offset = hand->errorOffset; + unsigned char * str; + const char * errorType = NULL; + const char * errorText = NULL; + char text[72]; + const char * arrow = " (right here) ------^\n"; + + if (yajl_bs_current(hand->stateStack) == yajl_state_parse_error) { + errorType = "parse"; + errorText = hand->parseError; + } else if (yajl_bs_current(hand->stateStack) == yajl_state_lexical_error) { + errorType = "lexical"; + errorText = yajl_lex_error_to_string(yajl_lex_get_error(hand->lexer)); + } else { + errorType = "unknown"; + } + + { + unsigned int memneeded = 0; + memneeded += strlen(errorType); + memneeded += strlen(" error"); + if (errorText != NULL) { + memneeded += strlen(": "); + memneeded += strlen(errorText); + } + str = (unsigned char *) YA_MALLOC(&(hand->alloc), memneeded + 2); + str[0] = 0; + strcat((char *) str, errorType); + strcat((char *) str, " error"); + if (errorText != NULL) { + strcat((char *) str, ": "); + strcat((char *) str, errorText); + } + strcat((char *) str, "\n"); + } + + /* now we append as many spaces as needed to make sure the error + * falls at char 41, if verbose was specified */ + if (verbose) { + unsigned int start, end, i; + unsigned int spacesNeeded; + + spacesNeeded = (offset < 30 ? 40 - offset : 10); + start = (offset >= 30 ? offset - 30 : 0); + end = (offset + 30 > jsonTextLen ? jsonTextLen : offset + 30); + + for (i=0;ialloc), (strlen((char *) str) + + strlen((char *) text) + + strlen(arrow) + 1)); + newStr[0] = 0; + strcat((char *) newStr, (char *) str); + strcat((char *) newStr, text); + strcat((char *) newStr, arrow); + YA_FREE(&(hand->alloc), str); + str = (unsigned char *) newStr; + } + } + return str; +} + +/* check for client cancelation */ +#define _CC_CHK(x) \ + if (!(x)) { \ + yajl_bs_set(hand->stateStack, yajl_state_parse_error); \ + hand->parseError = \ + "client cancelled parse via callback return value"; \ + return yajl_status_client_canceled; \ + } + + +yajl_status +yajl_do_parse(yajl_handle hand, unsigned int * offset, + const unsigned char * jsonText, unsigned int jsonTextLen) +{ + yajl_tok tok; + const unsigned char * buf; + unsigned int bufLen; + + around_again: + switch (yajl_bs_current(hand->stateStack)) { + case yajl_state_parse_complete: + return yajl_status_ok; + case yajl_state_lexical_error: + case yajl_state_parse_error: + hand->errorOffset = *offset; + return yajl_status_error; + case yajl_state_start: + case yajl_state_map_need_val: + case yajl_state_array_need_val: + case yajl_state_array_start: { + /* for arrays and maps, we advance the state for this + * depth, then push the state of the next depth. + * If an error occurs during the parsing of the nesting + * enitity, the state at this level will not matter. + * a state that needs pushing will be anything other + * than state_start */ + yajl_state stateToPush = yajl_state_start; + + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + + switch (tok) { + case yajl_tok_eof: + return yajl_status_insufficient_data; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + case yajl_tok_string: + if (hand->callbacks && hand->callbacks->yajl_string) { + _CC_CHK(hand->callbacks->yajl_string(hand->ctx, + buf, bufLen)); + } + break; + case yajl_tok_string_with_escapes: + if (hand->callbacks && hand->callbacks->yajl_string) { + yajl_buf_clear(hand->decodeBuf); + yajl_string_decode(hand->decodeBuf, buf, bufLen); + _CC_CHK(hand->callbacks->yajl_string( + hand->ctx, yajl_buf_data(hand->decodeBuf), + yajl_buf_len(hand->decodeBuf))); + } + break; + case yajl_tok_bool: + if (hand->callbacks && hand->callbacks->yajl_boolean) { + _CC_CHK(hand->callbacks->yajl_boolean(hand->ctx, + *buf == 't')); + } + break; + case yajl_tok_null: + if (hand->callbacks && hand->callbacks->yajl_null) { + _CC_CHK(hand->callbacks->yajl_null(hand->ctx)); + } + break; + case yajl_tok_left_bracket: + if (hand->callbacks && hand->callbacks->yajl_start_map) { + _CC_CHK(hand->callbacks->yajl_start_map(hand->ctx)); + } + stateToPush = yajl_state_map_start; + break; + case yajl_tok_left_brace: + if (hand->callbacks && hand->callbacks->yajl_start_array) { + _CC_CHK(hand->callbacks->yajl_start_array(hand->ctx)); + } + stateToPush = yajl_state_array_start; + break; + case yajl_tok_integer: + /* + * note. strtol does not respect the length of + * the lexical token. in a corner case where the + * lexed number is a integer with a trailing zero, + * immediately followed by the end of buffer, + * sscanf could run off into oblivion and cause a + * crash. for this reason we copy the integer + * (and doubles), into our parse buffer (the same + * one used for unescaping strings), before + * calling strtol. yajl_buf ensures null padding, + * so we're safe. + */ + if (hand->callbacks) { + if (hand->callbacks->yajl_number) { + _CC_CHK(hand->callbacks->yajl_number( + hand->ctx,(const char *) buf, bufLen)); + } else if (hand->callbacks->yajl_integer) { + long int i = 0; + yajl_buf_clear(hand->decodeBuf); + yajl_buf_append(hand->decodeBuf, buf, bufLen); + buf = yajl_buf_data(hand->decodeBuf); + i = strtol((const char *) buf, NULL, 10); + if ((i == LONG_MIN || i == LONG_MAX) && + errno == ERANGE) + { + yajl_bs_set(hand->stateStack, + yajl_state_parse_error); + hand->parseError = "integer overflow" ; + /* try to restore error offset */ + if (*offset >= bufLen) *offset -= bufLen; + else *offset = 0; + goto around_again; + } + _CC_CHK(hand->callbacks->yajl_integer(hand->ctx, + i)); + } + } + break; + case yajl_tok_double: + if (hand->callbacks) { + if (hand->callbacks->yajl_number) { + _CC_CHK(hand->callbacks->yajl_number( + hand->ctx, (const char *) buf, bufLen)); + } else if (hand->callbacks->yajl_double) { + double d = 0.0; + yajl_buf_clear(hand->decodeBuf); + yajl_buf_append(hand->decodeBuf, buf, bufLen); + buf = yajl_buf_data(hand->decodeBuf); + d = strtod((char *) buf, NULL); + if ((d == HUGE_VAL || d == -HUGE_VAL) && + errno == ERANGE) + { + yajl_bs_set(hand->stateStack, + yajl_state_parse_error); + hand->parseError = "numeric (floating point) " + "overflow"; + /* try to restore error offset */ + if (*offset >= bufLen) *offset -= bufLen; + else *offset = 0; + goto around_again; + } + _CC_CHK(hand->callbacks->yajl_double(hand->ctx, + d)); + } + } + break; + case yajl_tok_right_brace: { + if (yajl_bs_current(hand->stateStack) == + yajl_state_array_start) + { + if (hand->callbacks && + hand->callbacks->yajl_end_array) + { + _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx)); + } + yajl_bs_pop(hand->stateStack); + goto around_again; + } + /* intentional fall-through */ + } + case yajl_tok_colon: + case yajl_tok_comma: + case yajl_tok_right_bracket: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = + "unallowed token at this point in JSON text"; + goto around_again; + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = "invalid token, internal error"; + goto around_again; + } + /* got a value. transition depends on the state we're in. */ + { + yajl_state s = yajl_bs_current(hand->stateStack); + if (s == yajl_state_start) { + yajl_bs_set(hand->stateStack, yajl_state_parse_complete); + } else if (s == yajl_state_map_need_val) { + yajl_bs_set(hand->stateStack, yajl_state_map_got_val); + } else { + yajl_bs_set(hand->stateStack, yajl_state_array_got_val); + } + } + if (stateToPush != yajl_state_start) { + yajl_bs_push(hand->stateStack, stateToPush); + } + + goto around_again; + } + case yajl_state_map_start: + case yajl_state_map_need_key: { + /* only difference between these two states is that in + * start '}' is valid, whereas in need_key, we've parsed + * a comma, and a string key _must_ follow */ + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_eof: + return yajl_status_insufficient_data; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + case yajl_tok_string_with_escapes: + if (hand->callbacks && hand->callbacks->yajl_map_key) { + yajl_buf_clear(hand->decodeBuf); + yajl_string_decode(hand->decodeBuf, buf, bufLen); + buf = yajl_buf_data(hand->decodeBuf); + bufLen = yajl_buf_len(hand->decodeBuf); + } + /* intentional fall-through */ + case yajl_tok_string: + if (hand->callbacks && hand->callbacks->yajl_map_key) { + _CC_CHK(hand->callbacks->yajl_map_key(hand->ctx, buf, + bufLen)); + } + yajl_bs_set(hand->stateStack, yajl_state_map_sep); + goto around_again; + case yajl_tok_right_bracket: + if (yajl_bs_current(hand->stateStack) == + yajl_state_map_start) + { + if (hand->callbacks && hand->callbacks->yajl_end_map) { + _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx)); + } + yajl_bs_pop(hand->stateStack); + goto around_again; + } + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = + "invalid object key (must be a string)"; + goto around_again; + } + } + case yajl_state_map_sep: { + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_colon: + yajl_bs_set(hand->stateStack, yajl_state_map_need_val); + goto around_again; + case yajl_tok_eof: + return yajl_status_insufficient_data; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = "object key and value must " + "be separated by a colon (':')"; + goto around_again; + } + } + case yajl_state_map_got_val: { + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_right_bracket: + if (hand->callbacks && hand->callbacks->yajl_end_map) { + _CC_CHK(hand->callbacks->yajl_end_map(hand->ctx)); + } + yajl_bs_pop(hand->stateStack); + goto around_again; + case yajl_tok_comma: + yajl_bs_set(hand->stateStack, yajl_state_map_need_key); + goto around_again; + case yajl_tok_eof: + return yajl_status_insufficient_data; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = "after key and value, inside map, " + "I expect ',' or '}'"; + /* try to restore error offset */ + if (*offset >= bufLen) *offset -= bufLen; + else *offset = 0; + goto around_again; + } + } + case yajl_state_array_got_val: { + tok = yajl_lex_lex(hand->lexer, jsonText, jsonTextLen, + offset, &buf, &bufLen); + switch (tok) { + case yajl_tok_right_brace: + if (hand->callbacks && hand->callbacks->yajl_end_array) { + _CC_CHK(hand->callbacks->yajl_end_array(hand->ctx)); + } + yajl_bs_pop(hand->stateStack); + goto around_again; + case yajl_tok_comma: + yajl_bs_set(hand->stateStack, yajl_state_array_need_val); + goto around_again; + case yajl_tok_eof: + return yajl_status_insufficient_data; + case yajl_tok_error: + yajl_bs_set(hand->stateStack, yajl_state_lexical_error); + goto around_again; + default: + yajl_bs_set(hand->stateStack, yajl_state_parse_error); + hand->parseError = + "after array element, I expect ',' or ']'"; + goto around_again; + } + } + } + + abort(); + return yajl_status_error; +} + diff --git a/src/libCom/yajl/yajl_parser.h b/src/libCom/yajl/yajl_parser.h new file mode 100644 index 000000000..f8bc8ee9a --- /dev/null +++ b/src/libCom/yajl/yajl_parser.h @@ -0,0 +1,79 @@ +/* + * Copyright 2007-2009, Lloyd Hilaiel. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. Neither the name of Lloyd Hilaiel nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __YAJL_PARSER_H__ +#define __YAJL_PARSER_H__ + +#include "yajl_parse.h" +#include "yajl_bytestack.h" +#include "yajl_buf.h" + + +typedef enum { + yajl_state_start = 0, + yajl_state_parse_complete, + yajl_state_parse_error, + yajl_state_lexical_error, + yajl_state_map_start, + yajl_state_map_sep, + yajl_state_map_need_val, + yajl_state_map_got_val, + yajl_state_map_need_key, + yajl_state_array_start, + yajl_state_array_got_val, + yajl_state_array_need_val +} yajl_state; + +struct yajl_handle_t { + const yajl_callbacks * callbacks; + void * ctx; + yajl_lexer lexer; + const char * parseError; + unsigned int errorOffset; + /* temporary storage for decoded strings */ + yajl_buf decodeBuf; + /* a stack of states. access with yajl_state_XXX routines */ + yajl_bytestack stateStack; + /* memory allocation routines */ + yajl_alloc_funcs alloc; +}; + +yajl_status +yajl_do_parse(yajl_handle handle, unsigned int * offset, + const unsigned char * jsonText, unsigned int jsonTextLen); + +unsigned char * +yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText, + unsigned int jsonTextLen, int verbose); + + +#endif From 359fc1b69e393ed472d8771c08d43e770f10d39d Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:31 -0400 Subject: [PATCH 02/81] Move YAJL_API to front of declarations. --- src/libCom/yajl/yajl_gen.h | 28 ++++++++++++++-------------- src/libCom/yajl/yajl_parse.h | 8 ++++---- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/libCom/yajl/yajl_gen.h b/src/libCom/yajl/yajl_gen.h index 8b814241c..1ae320eae 100644 --- a/src/libCom/yajl/yajl_gen.h +++ b/src/libCom/yajl/yajl_gen.h @@ -83,38 +83,38 @@ extern "C" { * * \returns an allocated handle on success, NULL on failure (bad params) */ - yajl_gen YAJL_API yajl_gen_alloc(const yajl_gen_config * config, + YAJL_API yajl_gen yajl_gen_alloc(const yajl_gen_config * config, const yajl_alloc_funcs * allocFuncs); /** free a generator handle */ - void YAJL_API yajl_gen_free(yajl_gen handle); + YAJL_API void yajl_gen_free(yajl_gen handle); - yajl_gen_status YAJL_API yajl_gen_integer(yajl_gen hand, long int number); - yajl_gen_status YAJL_API yajl_gen_double(yajl_gen hand, double number); - yajl_gen_status YAJL_API yajl_gen_number(yajl_gen hand, + YAJL_API yajl_gen_status yajl_gen_integer(yajl_gen hand, long int number); + YAJL_API yajl_gen_status yajl_gen_double(yajl_gen hand, double number); + YAJL_API yajl_gen_status yajl_gen_number(yajl_gen hand, const char * num, unsigned int len); - yajl_gen_status YAJL_API yajl_gen_string(yajl_gen hand, + YAJL_API yajl_gen_status yajl_gen_string(yajl_gen hand, const unsigned char * str, unsigned int len); - yajl_gen_status YAJL_API yajl_gen_null(yajl_gen hand); - yajl_gen_status YAJL_API yajl_gen_bool(yajl_gen hand, int boolean); - yajl_gen_status YAJL_API yajl_gen_map_open(yajl_gen hand); - yajl_gen_status YAJL_API yajl_gen_map_close(yajl_gen hand); - yajl_gen_status YAJL_API yajl_gen_array_open(yajl_gen hand); - yajl_gen_status YAJL_API yajl_gen_array_close(yajl_gen hand); + YAJL_API yajl_gen_status yajl_gen_null(yajl_gen hand); + YAJL_API yajl_gen_status yajl_gen_bool(yajl_gen hand, int boolean); + YAJL_API yajl_gen_status yajl_gen_map_open(yajl_gen hand); + YAJL_API yajl_gen_status yajl_gen_map_close(yajl_gen hand); + YAJL_API yajl_gen_status yajl_gen_array_open(yajl_gen hand); + YAJL_API yajl_gen_status yajl_gen_array_close(yajl_gen hand); /** access the null terminated generator buffer. If incrementally * outputing JSON, one should call yajl_gen_clear to clear the * buffer. This allows stream generation. */ - yajl_gen_status YAJL_API yajl_gen_get_buf(yajl_gen hand, + YAJL_API yajl_gen_status yajl_gen_get_buf(yajl_gen hand, const unsigned char ** buf, unsigned int * len); /** clear yajl's output buffer, but maintain all internal generation * state. This function will not "reset" the generator state, and is * intended to enable incremental JSON outputing. */ - void YAJL_API yajl_gen_clear(yajl_gen hand); + YAJL_API void yajl_gen_clear(yajl_gen hand); #ifdef __cplusplus } diff --git a/src/libCom/yajl/yajl_parse.h b/src/libCom/yajl/yajl_parse.h index 64d484013..d6b26baa7 100644 --- a/src/libCom/yajl/yajl_parse.h +++ b/src/libCom/yajl/yajl_parse.h @@ -127,20 +127,20 @@ extern "C" { * \param config configuration parameters for the parse. * \param ctx a context pointer that will be passed to callbacks. */ - yajl_handle YAJL_API yajl_alloc(const yajl_callbacks * callbacks, + YAJL_API yajl_handle yajl_alloc(const yajl_callbacks * callbacks, const yajl_parser_config * config, const yajl_alloc_funcs * allocFuncs, void * ctx); /** free a parser handle */ - void YAJL_API yajl_free(yajl_handle handle); + YAJL_API void yajl_free(yajl_handle handle); /** Parse some json! * \param hand - a handle to the json parser allocated with yajl_alloc * \param jsonText - a pointer to the UTF8 json text to be parsed * \param jsonTextLength - the length, in bytes, of input text */ - yajl_status YAJL_API yajl_parse(yajl_handle hand, + YAJL_API yajl_status yajl_parse(yajl_handle hand, const unsigned char * jsonText, unsigned int jsonTextLength); @@ -170,7 +170,7 @@ extern "C" { unsigned int jsonTextLength); /** free an error returned from yajl_get_error */ - void YAJL_API yajl_free_error(yajl_handle hand, unsigned char * str); + YAJL_API void yajl_free_error(yajl_handle hand, unsigned char * str); #ifdef __cplusplus } From cf623e290a143965727e748e8d668fdf74a654a0 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:31 -0400 Subject: [PATCH 03/81] Upgraded to yajl version 1.0.9. --- src/libCom/yajl/yajl.c | 13 ++++-- src/libCom/yajl/yajl_encode.c | 15 +++++-- src/libCom/yajl/yajl_encode.h | 6 +++ src/libCom/yajl/yajl_gen.c | 81 ++++++++++++++++++++++------------- src/libCom/yajl/yajl_gen.h | 38 +++++++++++++++- src/libCom/yajl/yajl_parse.h | 18 +++++++- src/libCom/yajl/yajl_parser.c | 11 +++-- src/libCom/yajl/yajl_parser.h | 9 ++-- 8 files changed, 145 insertions(+), 46 deletions(-) diff --git a/src/libCom/yajl/yajl.c b/src/libCom/yajl/yajl.c index 0f32eea05..f353598d8 100644 --- a/src/libCom/yajl/yajl.c +++ b/src/libCom/yajl/yajl.c @@ -96,7 +96,7 @@ yajl_alloc(const yajl_callbacks * callbacks, hand->callbacks = callbacks; hand->ctx = ctx; hand->lexer = yajl_lex_alloc(&(hand->alloc), allowComments, validateUTF8); - hand->errorOffset = 0; + hand->bytesConsumed = 0; hand->decodeBuf = yajl_buf_alloc(&(hand->alloc)); yajl_bs_init(hand->stateStack, &(hand->alloc)); @@ -118,9 +118,8 @@ yajl_status yajl_parse(yajl_handle hand, const unsigned char * jsonText, unsigned int jsonTextLen) { - unsigned int offset = 0; yajl_status status; - status = yajl_do_parse(hand, &offset, jsonText, jsonTextLen); + status = yajl_do_parse(hand, jsonText, jsonTextLen); return status; } @@ -143,6 +142,14 @@ yajl_get_error(yajl_handle hand, int verbose, return yajl_render_error_string(hand, jsonText, jsonTextLen, verbose); } +unsigned int +yajl_get_bytes_consumed(yajl_handle hand) +{ + if (!hand) return 0; + else return hand->bytesConsumed; +} + + void yajl_free_error(yajl_handle hand, unsigned char * str) { diff --git a/src/libCom/yajl/yajl_encode.c b/src/libCom/yajl/yajl_encode.c index fb85d73d3..0d7007da0 100644 --- a/src/libCom/yajl/yajl_encode.c +++ b/src/libCom/yajl/yajl_encode.c @@ -48,6 +48,15 @@ static void CharToHex(unsigned char c, char * hexBuf) void yajl_string_encode(yajl_buf buf, const unsigned char * str, unsigned int len) +{ + yajl_string_encode2((const yajl_print_t) &yajl_buf_append, buf, str, len); +} + +void +yajl_string_encode2(const yajl_print_t print, + void * ctx, + const unsigned char * str, + unsigned int len) { unsigned int beg = 0; unsigned int end = 0; @@ -74,14 +83,14 @@ yajl_string_encode(yajl_buf buf, const unsigned char * str, break; } if (escaped != NULL) { - yajl_buf_append(buf, str + beg, end - beg); - yajl_buf_append(buf, escaped, strlen(escaped)); + print(ctx, (const char *) (str + beg), end - beg); + print(ctx, escaped, strlen(escaped)); beg = ++end; } else { ++end; } } - yajl_buf_append(buf, str + beg, end - beg); + print(ctx, (const char *) (str + beg), end - beg); } static void hexToDigit(unsigned int * val, const unsigned char * hex) diff --git a/src/libCom/yajl/yajl_encode.h b/src/libCom/yajl/yajl_encode.h index 8bd01af5a..d478baa19 100644 --- a/src/libCom/yajl/yajl_encode.h +++ b/src/libCom/yajl/yajl_encode.h @@ -34,6 +34,12 @@ #define __YAJL_ENCODE_H__ #include "yajl_buf.h" +#include "yajl_gen.h" + +void yajl_string_encode2(const yajl_print_t printer, + void * ctx, + const unsigned char * str, + unsigned int length); void yajl_string_encode(yajl_buf buf, const unsigned char * str, unsigned int length); diff --git a/src/libCom/yajl/yajl_gen.c b/src/libCom/yajl/yajl_gen.c index 7e353db42..a10d08297 100644 --- a/src/libCom/yajl/yajl_gen.c +++ b/src/libCom/yajl/yajl_gen.c @@ -35,6 +35,7 @@ #include #define epicsExportSharedSymbols +#include "epicsMath.h" #include "yajl_gen.h" #include "yajl_buf.h" #include "yajl_encode.h" @@ -56,7 +57,8 @@ struct yajl_gen_t unsigned int pretty; const char * indentString; yajl_gen_state state[YAJL_MAX_DEPTH]; - yajl_buf buf; + yajl_print_t print; + void * ctx; /* yajl_buf */ /* memory allocation routines */ yajl_alloc_funcs alloc; }; @@ -64,6 +66,15 @@ struct yajl_gen_t yajl_gen yajl_gen_alloc(const yajl_gen_config * config, const yajl_alloc_funcs * afs) +{ + return yajl_gen_alloc2(NULL, config, afs, NULL); +} + +yajl_gen +yajl_gen_alloc2(const yajl_print_t callback, + const yajl_gen_config * config, + const yajl_alloc_funcs * afs, + void * ctx) { yajl_gen g = NULL; yajl_alloc_funcs afsBuffer; @@ -88,7 +99,14 @@ yajl_gen_alloc(const yajl_gen_config * config, g->pretty = config->beautify; g->indentString = config->indentString ? config->indentString : " "; } - g->buf = yajl_buf_alloc(&(g->alloc)); + + if (callback) { + g->print = callback; + g->ctx = ctx; + } else { + g->print = (yajl_print_t)&yajl_buf_append; + g->ctx = yajl_buf_alloc(&(g->alloc)); + } return g; } @@ -96,18 +114,18 @@ yajl_gen_alloc(const yajl_gen_config * config, void yajl_gen_free(yajl_gen g) { - yajl_buf_free(g->buf); + if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_free((yajl_buf)g->ctx); YA_FREE(&(g->alloc), g); } #define INSERT_SEP \ if (g->state[g->depth] == yajl_gen_map_key || \ g->state[g->depth] == yajl_gen_in_array) { \ - yajl_buf_append(g->buf, ",", 1); \ - if (g->pretty) yajl_buf_append(g->buf, "\n", 1); \ + g->print(g->ctx, ",", 1); \ + if (g->pretty) g->print(g->ctx, "\n", 1); \ } else if (g->state[g->depth] == yajl_gen_map_val) { \ - yajl_buf_append(g->buf, ":", 1); \ - if (g->pretty) yajl_buf_append(g->buf, " ", 1); \ + g->print(g->ctx, ":", 1); \ + if (g->pretty) g->print(g->ctx, " ", 1); \ } #define INSERT_WHITESPACE \ @@ -115,8 +133,8 @@ yajl_gen_free(yajl_gen g) if (g->state[g->depth] != yajl_gen_map_val) { \ unsigned int _i; \ for (_i=0;_idepth;_i++) \ - yajl_buf_append(g->buf, g->indentString, \ - strlen(g->indentString)); \ + g->print(g->ctx, g->indentString, \ + strlen(g->indentString)); \ } \ } @@ -158,7 +176,7 @@ yajl_gen_free(yajl_gen g) #define FINAL_NEWLINE \ if (g->pretty && g->state[g->depth] == yajl_gen_complete) \ - yajl_buf_append(g->buf, "\n", 1); + g->print(g->ctx, "\n", 1); yajl_gen_status yajl_gen_integer(yajl_gen g, long int number) @@ -166,7 +184,7 @@ yajl_gen_integer(yajl_gen g, long int number) char i[32]; ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; sprintf(i, "%ld", number); - yajl_buf_append(g->buf, i, strlen(i)); + g->print(g->ctx, i, strlen(i)); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; @@ -176,9 +194,11 @@ yajl_gen_status yajl_gen_double(yajl_gen g, double number) { char i[32]; - ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; + ENSURE_VALID_STATE; ENSURE_NOT_KEY; + if (isnan(number) || isinf(number)) return yajl_gen_invalid_number; + INSERT_SEP; INSERT_WHITESPACE; sprintf(i, "%g", number); - yajl_buf_append(g->buf, i, strlen(i)); + g->print(g->ctx, i, strlen(i)); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; @@ -188,7 +208,7 @@ yajl_gen_status yajl_gen_number(yajl_gen g, const char * s, unsigned int l) { ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; - yajl_buf_append(g->buf, s, l); + g->print(g->ctx, s, l); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; @@ -199,9 +219,9 @@ yajl_gen_string(yajl_gen g, const unsigned char * str, unsigned int len) { ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE; - yajl_buf_append(g->buf, "\"", 1); - yajl_string_encode(g->buf, str, len); - yajl_buf_append(g->buf, "\"", 1); + g->print(g->ctx, "\"", 1); + yajl_string_encode2(g->print, g->ctx, str, len); + g->print(g->ctx, "\"", 1); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; @@ -211,7 +231,7 @@ yajl_gen_status yajl_gen_null(yajl_gen g) { ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; - yajl_buf_append(g->buf, "null", strlen("null")); + g->print(g->ctx, "null", strlen("null")); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; @@ -223,7 +243,7 @@ yajl_gen_bool(yajl_gen g, int boolean) const char * val = boolean ? "true" : "false"; ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; - yajl_buf_append(g->buf, val, strlen(val)); + g->print(g->ctx, val, strlen(val)); APPENDED_ATOM; FINAL_NEWLINE; return yajl_gen_status_ok; @@ -236,8 +256,8 @@ yajl_gen_map_open(yajl_gen g) INCREMENT_DEPTH; g->state[g->depth] = yajl_gen_map_start; - yajl_buf_append(g->buf, "{", 1); - if (g->pretty) yajl_buf_append(g->buf, "\n", 1); + g->print(g->ctx, "{", 1); + if (g->pretty) g->print(g->ctx, "\n", 1); FINAL_NEWLINE; return yajl_gen_status_ok; } @@ -247,10 +267,10 @@ yajl_gen_map_close(yajl_gen g) { ENSURE_VALID_STATE; (g->depth)--; - if (g->pretty) yajl_buf_append(g->buf, "\n", 1); + if (g->pretty) g->print(g->ctx, "\n", 1); APPENDED_ATOM; INSERT_WHITESPACE; - yajl_buf_append(g->buf, "}", 1); + g->print(g->ctx, "}", 1); FINAL_NEWLINE; return yajl_gen_status_ok; } @@ -261,8 +281,8 @@ yajl_gen_array_open(yajl_gen g) ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE; INCREMENT_DEPTH; g->state[g->depth] = yajl_gen_array_start; - yajl_buf_append(g->buf, "[", 1); - if (g->pretty) yajl_buf_append(g->buf, "\n", 1); + g->print(g->ctx, "[", 1); + if (g->pretty) g->print(g->ctx, "\n", 1); FINAL_NEWLINE; return yajl_gen_status_ok; } @@ -271,11 +291,11 @@ yajl_gen_status yajl_gen_array_close(yajl_gen g) { ENSURE_VALID_STATE; - if (g->pretty) yajl_buf_append(g->buf, "\n", 1); + if (g->pretty) g->print(g->ctx, "\n", 1); (g->depth)--; APPENDED_ATOM; INSERT_WHITESPACE; - yajl_buf_append(g->buf, "]", 1); + g->print(g->ctx, "]", 1); FINAL_NEWLINE; return yajl_gen_status_ok; } @@ -284,13 +304,14 @@ yajl_gen_status yajl_gen_get_buf(yajl_gen g, const unsigned char ** buf, unsigned int * len) { - *buf = yajl_buf_data(g->buf); - *len = yajl_buf_len(g->buf); + if (g->print != (yajl_print_t)&yajl_buf_append) return yajl_gen_no_buf; + *buf = yajl_buf_data((yajl_buf)g->ctx); + *len = yajl_buf_len((yajl_buf)g->ctx); return yajl_gen_status_ok; } void yajl_gen_clear(yajl_gen g) { - yajl_buf_clear(g->buf); + if (g->print == (yajl_print_t)&yajl_buf_append) yajl_buf_clear((yajl_buf)g->ctx); } diff --git a/src/libCom/yajl/yajl_gen.h b/src/libCom/yajl/yajl_gen.h index 1ae320eae..8efabd073 100644 --- a/src/libCom/yajl/yajl_gen.h +++ b/src/libCom/yajl/yajl_gen.h @@ -57,12 +57,23 @@ extern "C" { * state */ yajl_gen_in_error_state, /** A complete JSON document has been generated */ - yajl_gen_generation_complete + yajl_gen_generation_complete, + /** yajl_gen_double was passed an invalid floating point value + * (infinity or NaN). */ + yajl_gen_invalid_number, + /** A print callback was passed in, so there is no internal + * buffer to get from */ + yajl_gen_no_buf } yajl_gen_status; /** an opaque handle to a generator */ typedef struct yajl_gen_t * yajl_gen; + /** a callback used for "printing" the results. */ + typedef void (*yajl_print_t)(void * ctx, + const char * str, + unsigned int len); + /** configuration structure for the generator */ typedef struct { /** generate indented (beautiful) output */ @@ -86,10 +97,35 @@ extern "C" { YAJL_API yajl_gen yajl_gen_alloc(const yajl_gen_config * config, const yajl_alloc_funcs * allocFuncs); + /** allocate a generator handle that will print to the specified + * callback rather than storing the results in an internal buffer. + * \param callback a pointer to a printer function. May be NULL + * in which case, the results will be store in an + * internal buffer. + * \param config a pointer to a structure containing parameters + * which configure the behavior of the json + * generator. + * \param allocFuncs an optional pointer to a structure which allows + * the client to overide the memory allocation + * used by yajl. May be NULL, in which case + * malloc/free/realloc will be used. + * \param ctx a context pointer that will be passed to the + * printer callback. + * + * \returns an allocated handle on success, NULL on failure (bad params) + */ + YAJL_API yajl_gen yajl_gen_alloc2(const yajl_print_t callback, + const yajl_gen_config * config, + const yajl_alloc_funcs * allocFuncs, + void * ctx); + /** free a generator handle */ YAJL_API void yajl_gen_free(yajl_gen handle); YAJL_API yajl_gen_status yajl_gen_integer(yajl_gen hand, long int number); + /** generate a floating point number. number may not be infinity or + * NaN, as these have no representation in JSON. In these cases the + * generator will return 'yajl_gen_invalid_number' */ YAJL_API yajl_gen_status yajl_gen_double(yajl_gen hand, double number); YAJL_API yajl_gen_status yajl_gen_number(yajl_gen hand, const char * num, diff --git a/src/libCom/yajl/yajl_parse.h b/src/libCom/yajl/yajl_parse.h index d6b26baa7..b4de64bbd 100644 --- a/src/libCom/yajl/yajl_parse.h +++ b/src/libCom/yajl/yajl_parse.h @@ -153,7 +153,7 @@ extern "C" { * * \param hand - a handle to the json parser allocated with yajl_alloc */ - yajl_status yajl_parse_complete(yajl_handle hand); + YAJL_API yajl_status yajl_parse_complete(yajl_handle hand); /** get an error string describing the state of the * parse. @@ -162,13 +162,27 @@ extern "C" { * text where the error occured, along with an arrow pointing to * the specific char. * - * A dynamically allocated string will be returned which should + * \returns A dynamically allocated string will be returned which should * be freed with yajl_free_error */ YAJL_API unsigned char * yajl_get_error(yajl_handle hand, int verbose, const unsigned char * jsonText, unsigned int jsonTextLength); + /** + * get the amount of data consumed from the last chunk passed to YAJL. + * + * In the case of a successful parse this can help you understand if + * the entire buffer was consumed (which will allow you to handle + * "junk at end of input". + * + * In the event an error is encountered during parsing, this function + * affords the client a way to get the offset into the most recent + * chunk where the error occured. 0 will be returned if no error + * was encountered. + */ + YAJL_API unsigned int yajl_get_bytes_consumed(yajl_handle hand); + /** free an error returned from yajl_get_error */ YAJL_API void yajl_free_error(yajl_handle hand, unsigned char * str); diff --git a/src/libCom/yajl/yajl_parser.c b/src/libCom/yajl/yajl_parser.c index e1d5f2bf1..1c15f8e1e 100644 --- a/src/libCom/yajl/yajl_parser.c +++ b/src/libCom/yajl/yajl_parser.c @@ -49,7 +49,7 @@ unsigned char * yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText, unsigned int jsonTextLen, int verbose) { - unsigned int offset = hand->errorOffset; + unsigned int offset = hand->bytesConsumed; unsigned char * str; const char * errorType = NULL; const char * errorText = NULL; @@ -137,12 +137,16 @@ yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText, yajl_status -yajl_do_parse(yajl_handle hand, unsigned int * offset, - const unsigned char * jsonText, unsigned int jsonTextLen) +yajl_do_parse(yajl_handle hand, const unsigned char * jsonText, + unsigned int jsonTextLen) { yajl_tok tok; const unsigned char * buf; unsigned int bufLen; + unsigned int * offset = &(hand->bytesConsumed); + + *offset = 0; + around_again: switch (yajl_bs_current(hand->stateStack)) { @@ -150,7 +154,6 @@ yajl_do_parse(yajl_handle hand, unsigned int * offset, return yajl_status_ok; case yajl_state_lexical_error: case yajl_state_parse_error: - hand->errorOffset = *offset; return yajl_status_error; case yajl_state_start: case yajl_state_map_need_val: diff --git a/src/libCom/yajl/yajl_parser.h b/src/libCom/yajl/yajl_parser.h index f8bc8ee9a..c9d29d436 100644 --- a/src/libCom/yajl/yajl_parser.h +++ b/src/libCom/yajl/yajl_parser.h @@ -58,7 +58,10 @@ struct yajl_handle_t { void * ctx; yajl_lexer lexer; const char * parseError; - unsigned int errorOffset; + /* the number of bytes consumed from the last client buffer, + * in the case of an error this will be an error offset, in the + * case of an error this can be used as the error offset */ + unsigned int bytesConsumed; /* temporary storage for decoded strings */ yajl_buf decodeBuf; /* a stack of states. access with yajl_state_XXX routines */ @@ -68,8 +71,8 @@ struct yajl_handle_t { }; yajl_status -yajl_do_parse(yajl_handle handle, unsigned int * offset, - const unsigned char * jsonText, unsigned int jsonTextLen); +yajl_do_parse(yajl_handle handle, const unsigned char * jsonText, + unsigned int jsonTextLen); unsigned char * yajl_render_error_string(yajl_handle hand, const unsigned char * jsonText, From 68dfeb76c0c59b21e45f2f57819d412e8c6c9507 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:21:32 -0400 Subject: [PATCH 04/81] Adding dbChannel JSON parser and test files. Passes 33 tests. --- src/ioc/db/Makefile | 2 + src/ioc/db/dbAccessDefs.h | 12 +- src/ioc/db/dbAddr.h | 2 +- src/ioc/db/dbChannel.c | 246 ++++++++++++++++++++++++++++++++ src/ioc/db/dbChannel.h | 83 +++++++++++ src/ioc/db/test/Makefile | 5 + src/ioc/db/test/dbChannelTest.c | 170 ++++++++++++++++++++++ 7 files changed, 513 insertions(+), 7 deletions(-) create mode 100644 src/ioc/db/dbChannel.c create mode 100644 src/ioc/db/dbChannel.h create mode 100644 src/ioc/db/test/dbChannelTest.c diff --git a/src/ioc/db/Makefile b/src/ioc/db/Makefile index 8bfec9434..324edecb5 100644 --- a/src/ioc/db/Makefile +++ b/src/ioc/db/Makefile @@ -17,6 +17,7 @@ INC += dbAccessDefs.h INC += dbCa.h INC += dbAddr.h INC += dbBkpt.h +INC += dbChannel.h INC += dbConvert.h INC += dbConvertFast.h INC += dbEvent.h @@ -53,6 +54,7 @@ DBDINC += dbCommon dbCore_SRCS += dbLock.c dbCore_SRCS += dbAccess.c dbCore_SRCS += dbBkpt.c +dbCore_SRCS += dbChannel.c dbCore_SRCS += dbConvert.c dbCore_SRCS += dbFastLinkConv.c dbCore_SRCS += dbNotify.c diff --git a/src/ioc/db/dbAccessDefs.h b/src/ioc/db/dbAccessDefs.h index 73d68c4ef..6c12d3df5 100644 --- a/src/ioc/db/dbAccessDefs.h +++ b/src/ioc/db/dbAccessDefs.h @@ -5,7 +5,7 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbAccessDefs.h */ /* $Revision-Id$ */ @@ -47,7 +47,7 @@ epicsShareExtern volatile int interruptAccept; #define DBR_CTRL_DOUBLE 0x00000100 #define DBR_AL_LONG 0x00000200 #define DBR_AL_DOUBLE 0x00000400 - + /********************************************************************** * The next page contains macros for defining requests. * As an example the following defines a buffer to accept an array @@ -69,7 +69,7 @@ epicsShareExtern volatile int interruptAccept; * options = DBR_STATUS|DBR_TIME; * number_elements = 10; * rtnval=dbGetField(paddr,DBR_FLOAT,&buffer,&options,&number_elements); - * + * * When dbGetField returns: * rtnval is error status (0 means success) * options has a bit set for each option that was accepted @@ -97,7 +97,7 @@ epicsShareExtern volatile int interruptAccept; * MYBUFFER *pbuf1; * MYBUFFER buf; *************************************************************************/ - + /* Macros for defining each option */ #define DBRstatus \ epicsUInt16 status; /* alarm status */\ @@ -143,7 +143,7 @@ epicsShareExtern volatile int interruptAccept; epicsFloat64 upper_warning_limit;\ epicsFloat64 lower_warning_limit;\ epicsFloat64 lower_alarm_limit; - + /* structures for each option type */ struct dbr_status {DBRstatus}; struct dbr_units {DBRunits}; @@ -198,7 +198,7 @@ struct dbr_alDouble {DBRalDouble}; #define S_db_cntSpwn (M_dbAccess|63) /*Cannot spawn dbContTask*/ #define S_db_cntCont (M_dbAccess|65) /*Cannot resume dbContTask*/ #define S_db_noMemory (M_dbAccess|66) /*unable to allocate data structure from pool*/ - + /* Global Database Access Routines*/ #define dbGetLink(PLNK, DBRTYPE, PBUFFER, OPTIONS, NREQUEST) \ ( ( ( (PLNK)->type == CONSTANT ) && \ diff --git a/src/ioc/db/dbAddr.h b/src/ioc/db/dbAddr.h index d7fdd94e9..a91bcf76d 100644 --- a/src/ioc/db/dbAddr.h +++ b/src/ioc/db/dbAddr.h @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ #ifndef dbAddrh diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c new file mode 100644 index 000000000..d58df92a0 --- /dev/null +++ b/src/ioc/db/dbChannel.c @@ -0,0 +1,246 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne + * National Laboratory. + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + \*************************************************************************/ + +#include +#include + +#include "cantProceed.h" +#include "dbChannel.h" +#include "dbBase.h" +#include "link.h" +#include "dbAccessDefs.h" +#include "epicsAssert.h" +#include "errlog.h" +#include "yajl_parse.h" + + +typedef struct parseContext { + dbChannel *chan; + chFilter *filter; + int depth; +} parseContext; + + +#define CALLIF(rtn) rtn && rtn + +static void chp_value(parseContext *parser, int result) +{ + chFilter *filter = parser->filter; + + if (!result || parser->depth > 0) return; + + parser->filter = NULL; + if (filter->fif->parse_end(filter)) { + ellAdd(&parser->chan->filters, &filter->node); + } +} + + +static int chp_null(void * ctx) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + int result; + + assert(filter); + result = CALLIF(filter->fif->parse_null) (filter ); + chp_value(parser, result); + return result; +} + +static int chp_boolean(void * ctx, int boolVal) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + int result; + + assert(filter); + result = CALLIF(filter->fif->parse_boolean) (filter , boolVal); + chp_value(parser, result); + return result; +} + +static int chp_integer(void * ctx, long integerVal) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + int result; + + assert(filter); + result = CALLIF(filter->fif->parse_integer) (filter , integerVal); + chp_value(parser, result); + return result; +} + +static int chp_double(void * ctx, double doubleVal) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + int result; + + assert(filter); + result = CALLIF(filter->fif->parse_double) (filter , doubleVal); + chp_value(parser, result); + return result; +} + +static int chp_string(void * ctx, const unsigned char * stringVal, + unsigned int stringLen) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + int result; + + assert(filter); + result = CALLIF(filter->fif->parse_string) (filter , (const char *) stringVal, stringLen); + chp_value(parser, result); + return result; +} + +static int chp_start_map(void * ctx) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + + if (!filter) { + assert(parser->depth == 0); + return 1; /* Opening '{' */ + } + + ++parser->depth; + return CALLIF(filter->fif->parse_start_map) (filter ); +} + +static int chp_map_key(void * ctx, const unsigned char * key, + unsigned int stringLen) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + const chFilterIf *fif; + int result; + + if (filter) { + return CALLIF(filter->fif->parse_map_key) (filter , (const char *) key, stringLen); + } + + assert(parser->depth == 0); + fif = dbFindFilter((const char *) key, stringLen); + if (!fif) { + printf("Channel filter '%.*s' not found\n", stringLen, key); + return parse_stop; + } + + filter = (chFilter *) callocMustSucceed(1, sizeof(*filter), "Creating dbChannel filter"); + filter->chan = parser->chan; + filter->fif = fif; + filter->puser = NULL; + + result = fif->parse_start(filter); + if (result == parse_continue) { + parser->filter = filter; + } else { + free(filter); + } + return result; +} + +static int chp_end_map(void * ctx) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + + if (filter) { + int result = CALLIF(filter->fif->parse_end_map) (filter ); + + if (--parser->depth == 0) chp_value(parser, result); + return result; + } + assert(parser->depth == 0); + return parse_continue; /* Final closing '}' */ +} + +static int chp_start_array(void * ctx) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + + assert(filter); + ++parser->depth; + return CALLIF(filter->fif->parse_start_array) (filter ); +} + +static int chp_end_array(void * ctx) +{ + parseContext *parser = (parseContext *) ctx; + chFilter *filter = parser->filter; + int result; + + assert(filter); + result = CALLIF(filter->fif->parse_end_array) (filter ); + if (--parser->depth == 0) chp_value(parser, result); + return result; +} + +static const yajl_callbacks chp_callbacks = + { chp_null, chp_boolean, chp_integer, chp_double, NULL, chp_string, + chp_start_map, chp_map_key, chp_end_map, chp_start_array, chp_end_array }; + +static const yajl_parser_config chp_config = + { 0, 1 }; /* allowComments = NO , checkUTF8 = YES */ + + +void dbChannelInit(dbChannel *chan) +{ + ellInit(&chan->filters); +} +// Move ellInit into ChannelFind, rename it again? +long dbChannelFind(dbChannel *chan, const char *name) +{ + parseContext parser = + { chan, NULL, 0}; + yajl_handle yh = yajl_alloc(&chp_callbacks, &chp_config, NULL, &parser); + size_t len = strlen(name); + yajl_status status; + + status = yajl_parse(yh, (const unsigned char *) name, len); + if (status != yajl_status_ok && parser.filter) { + parser.filter->fif->parse_abort(parser.filter); + free(parser.filter); + } + yajl_free(yh); + return status == yajl_status_ok; +} + +long dbChannelOpen(dbChannel *chan) +{ + return S_db_notFound; +} + +long dbChannelClose(dbChannel *chan) +{ + return 0; +} + + +const chFilterIf * filt = NULL; +const char *fname = NULL; + +void dbRegisterFilter(const char *key, const chFilterIf *fif) +{ + /* FIXME: Add filter to a list and hash in pdbbase */ + filt = fif; + fname = key; + fif->plugin_init(); +} + +const chFilterIf * dbFindFilter(const char *key, size_t len) +{ + /* FIXME: gpHash lookup */ + if (strncmp(fname, key, len) == 0) + return filt; + return NULL; +} diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h new file mode 100644 index 000000000..1508d5c1a --- /dev/null +++ b/src/ioc/db/dbChannel.h @@ -0,0 +1,83 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne + * National Laboratory. + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + \*************************************************************************/ + +#ifndef INC_dbChannel_H +#define INC_dbChannel_H + +#include "dbDefs.h" +#include "dbAddr.h" +#include "ellLib.h" +#include "shareLib.h" + +enum {parse_stop, parse_continue}; + +#ifdef __cplusplus +// extern "C" { +#endif + +/* A dbChannel points to a record field, and can have multiple filters */ +typedef struct dbChannel { + dbAddr addr; + ELLLIST filters; +} dbChannel; + +typedef struct chFilter chFilter; + +/* These routines must be implemented by each filter plug-in */ +typedef struct chFilterIf { + /* Plug-in management */ + void (* plugin_init)(void); + void (* plugin_exit)(void); + + /* Parsing event handlers */ + int (* parse_start)(chFilter *filter); + void (* parse_abort)(chFilter *filter); + int (* parse_end)(chFilter *filter); + + int (* parse_null)(chFilter *filter); + int (* parse_boolean)(chFilter *filter, int boolVal); + int (* parse_integer)(chFilter *filter, long integerVal); + int (* parse_double)(chFilter *filter, double doubleVal); + int (* parse_string)(chFilter *filter, const char *stringVal, + size_t stringLen); /* NB: stringVal is not zero-terminated: */ + + int (* parse_start_map)(chFilter *filter); + int (* parse_map_key)(chFilter *filter, const char *key, + size_t stringLen); + int (* parse_end_map)(chFilter *filter); + + int (* parse_start_array)(chFilter *filter); + int (* parse_end_array)(chFilter *filter); + + /* Channel operations */ + long (* channel_open)(chFilter *filter); + void (* channel_report)(chFilter *filter, int level); + /* FIXME: More routines here ... */ + void (* channel_close)(chFilter *filter); +} chFilterIf; + +/* A chFilter holds instance data for a single filter */ +struct chFilter { + ELLNODE node; + dbChannel *chan; + const chFilterIf *fif; + void *puser; +}; + +epicsShareFunc void dbChannelInit(dbChannel *chan); +epicsShareFunc long dbChannelFind(dbChannel *chan, const char *name); +epicsShareFunc long dbChannelOpen(dbChannel *chan); +epicsShareFunc long dbChannelClose(dbChannel *chan); + +epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif); +epicsShareFunc const chFilterIf * dbFindFilter(const char *key, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif /* INC_dbChannel_H */ diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 3ee77ca07..a1bc70d5e 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -20,6 +20,11 @@ TESTS += callbackTest # When we add more test programs here, this must become a vxTestHarness TESTSPEC_vxWorks = callbackTest.munch; callbackTest +TESTPROD_HOST += dbChannelTest +dbChannelTest_SRCS += dbChannelTest.c +OBJS_IOC_vxWorks += dbChannelTest +TESTS += dbChannelTest + TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c new file mode 100644 index 000000000..31ca3180d --- /dev/null +++ b/src/ioc/db/test/dbChannelTest.c @@ -0,0 +1,170 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne + * National Laboratory. + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + \*************************************************************************/ + +#include "dbChannel.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +/* Expected call bit definitions */ +#define e_init 0x00000001 +#define e_exit 0x00000002 +#define e_start 0x00000004 +#define e_abort 0x00000008 +#define e_end 0x00000010 +#define e_null 0x00000020 +#define e_boolean 0x00000040 +#define e_integer 0x00000080 +#define e_double 0x00000100 +#define e_string 0x00000200 +#define e_start_map 0x00000400 +#define e_map_key 0x00000800 +#define e_end_map 0x00001000 +#define e_start_array 0x00002000 +#define e_end_array 0x00004000 +#define e_open 0x00008000 +#define e_report 0x00010000 +#define e_close 0x00020000 + +unsigned int e; + + +void f_init(void) +{ + testOk(e & e_init, "plugin_init expected"); +} +void f_exit(void) +{ + testOk(e & e_exit, "plugin_exit expected"); +} + +int p_start(chFilter *filter) +{ + testOk(e & e_start, "parse_start expected"); + return parse_continue; +} + +void p_abort(chFilter *filter) +{ + testOk(e & e_abort, "parse_abort expected"); +} +int p_end(chFilter *filter) +{ + testOk(e & e_end, "parse_end expected"); + return parse_continue; +} + +int p_null(chFilter *filter) +{ + testOk(e & e_null, "parse_null expected"); + return parse_continue; +} +int p_boolean(chFilter *filter, int boolVal) +{ + testOk(e & e_boolean, "parse_boolean expected, val = %d", boolVal); + return parse_continue; +} +int p_integer(chFilter *filter, long integerVal) +{ + testOk(e & e_integer, "parse_integer expected, val = %ld", integerVal); + return parse_continue; +} +int p_double(chFilter *filter, double doubleVal) +{ + testOk(e & e_double, "parse_double expected, val = %g", doubleVal); + return parse_continue; +} +int p_string(chFilter *filter, const char *stringVal, size_t stringLen) +{ + testOk(e & e_string, "parse_string expected, val = '%.*s'", stringLen, stringVal); + return parse_continue; +} + +int p_start_map(chFilter *filter) +{ + testOk(e & e_start_map, "parse_start_map expected"); + return parse_continue; +} +int p_map_key(chFilter *filter, const char *key, size_t stringLen) +{ + testOk(e & e_map_key, "parse_map_key expected, key = '%.*s'", stringLen, key); + return parse_continue; +} +int p_end_map(chFilter *filter) +{ + testOk(e & e_end_map, "parse_end_map expected"); + return parse_continue; +} + +int p_start_array(chFilter *filter) +{ + testOk(e & e_start_array, "parse_start_array expected"); + return parse_continue; +} +int p_end_array(chFilter *filter) +{ + testOk(e & e_end_array, "parse_end_array expected"); + return parse_continue; +} + +long c_open(chFilter *filter) +{ + testOk(e & e_open, "channel_open expected"); + return 0; +} +void c_report(chFilter *filter, int level) +{ + testOk(e & e_report, "channel_report expected, level = %d", level); +} +void c_close(chFilter *filter) +{ + testOk(e & e_close, "channel_close expected"); +} + +chFilterIf testIf = + { f_init, f_exit, p_start, p_abort, p_end, p_null, p_boolean, + p_integer, p_double, p_string, p_start_map, p_map_key, p_end_map, + p_start_array, p_end_array, c_open, c_report, c_close }; + +chFilterIf scalarIf = + { f_init, f_exit, p_start, p_abort, p_end, p_null, p_boolean, + p_integer, p_double, p_string, NULL, NULL, NULL, NULL, NULL, c_open, + c_report, c_close }; + +MAIN(dbChannelTest) +{ + dbChannel ch; + + testPlan(33); + dbChannelInit(&ch); + + e = 0; + testOk1(dbChannelFind(&ch, "{}")); + + e = e_init; + dbRegisterFilter("test", &testIf); + + e = e_start | e_null | e_end; + testOk1(dbChannelFind(&ch, "{\"test\":null}")); + + e = e_start | e_start_array | e_boolean | e_integer | e_end_array | e_end; + testOk1(dbChannelFind(&ch, "{\"test\":[true,1]}")); + + e = e_start | e_start_map | e_map_key | e_double | e_string | e_end_map | e_end; + testOk1(dbChannelFind(&ch, "{\"test\":{\"a\":2.7183,\"b\":\"c\"}}")); + + e = e_init; + dbRegisterFilter("scalar", &scalarIf); + + e = e_start | e_null | e_end; + testOk1(dbChannelFind(&ch, "{\"scalar\":null}")); + + e = e_start | e_abort; + testOk1(!dbChannelFind(&ch, "{\"scalar\":[null]}")); + testOk1(!dbChannelFind(&ch, "{\"scalar\":{}}")); + + return testDone(); +} From 0ab891fca4a6764ff1eacdbe87e40d820a6f0866 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:33 -0400 Subject: [PATCH 05/81] Added gphFindParse(), which takes a string length parameter. This is useful for hash lookups while parsing a const string, to avoid having to copy and nil-terminate it. --- src/libCom/gpHash/gpHash.h | 4 +++- src/libCom/gpHash/gpHashLib.c | 24 +++++++++++++++--------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/libCom/gpHash/gpHash.h b/src/libCom/gpHash/gpHash.h index 1a2783b64..ce6ec5f4a 100644 --- a/src/libCom/gpHash/gpHash.h +++ b/src/libCom/gpHash/gpHash.h @@ -5,7 +5,7 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* $Revision-Id$ */ /* Author: Marty Kraimer Date: 04-07-94 */ @@ -37,6 +37,8 @@ epicsShareFunc void epicsShareAPI gphInitPvt(struct gphPvt **ppvt, int tableSize); epicsShareFunc GPHENTRY * epicsShareAPI gphFind(struct gphPvt *pvt, const char *name, void *pvtid); +epicsShareFunc GPHENTRY * epicsShareAPI + gphFindParse(struct gphPvt *pvt, const char *name, size_t len, void *pvtid); epicsShareFunc GPHENTRY * epicsShareAPI gphAdd(struct gphPvt *pvt, const char *name, void *pvtid); epicsShareFunc void epicsShareAPI diff --git a/src/libCom/gpHash/gpHashLib.c b/src/libCom/gpHash/gpHashLib.c index 3cea5cb3a..c37371fae 100644 --- a/src/libCom/gpHash/gpHashLib.c +++ b/src/libCom/gpHash/gpHashLib.c @@ -4,12 +4,12 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* $Revision-Id$ */ /* Author: Marty Kraimer Date: 04-07-94 */ - + #include #include #include @@ -34,7 +34,7 @@ typedef struct gphPvt { #define MIN_SIZE 256 #define DEFAULT_SIZE 512 #define MAX_SIZE 65536 - + void epicsShareAPI gphInitPvt(gphPvt **ppvt, int size) { gphPvt *pgphPvt; @@ -59,7 +59,7 @@ void epicsShareAPI gphInitPvt(gphPvt **ppvt, int size) return; } -GPHENTRY * epicsShareAPI gphFind(gphPvt *pgphPvt, const char *name, void *pvtid) +GPHENTRY * epicsShareAPI gphFindParse(gphPvt *pgphPvt, const char *name, size_t len, void *pvtid) { ELLLIST **paplist; ELLLIST *gphlist; @@ -69,7 +69,7 @@ GPHENTRY * epicsShareAPI gphFind(gphPvt *pgphPvt, const char *name, void *pvtid) if (pgphPvt == NULL) return NULL; paplist = pgphPvt->paplist; hash = epicsMemHash((char *)&pvtid, sizeof(void *), 0); - hash = epicsStrHash(name, hash) & pgphPvt->mask; + hash = epicsMemHash(name, len, hash) & pgphPvt->mask; epicsMutexMustLock(pgphPvt->lock); gphlist = paplist[hash]; @@ -81,14 +81,20 @@ GPHENTRY * epicsShareAPI gphFind(gphPvt *pgphPvt, const char *name, void *pvtid) while (pgphNode) { if (pvtid == pgphNode->pvtid && - strcmp(name, pgphNode->name) == 0) break; + strlen(pgphNode->name) == len && + strncmp(name, pgphNode->name, len) == 0) break; pgphNode = (GPHENTRY *) ellNext((ELLNODE *)pgphNode); } epicsMutexUnlock(pgphPvt->lock); return pgphNode; } - + +GPHENTRY * epicsShareAPI gphFind(gphPvt *pgphPvt, const char *name, void *pvtid) +{ + return gphFindParse(pgphPvt, name, strlen(name), pvtid); +} + GPHENTRY * epicsShareAPI gphAdd(gphPvt *pgphPvt, const char *name, void *pvtid) { ELLLIST **paplist; @@ -133,7 +139,7 @@ GPHENTRY * epicsShareAPI gphAdd(gphPvt *pgphPvt, const char *name, void *pvtid) epicsMutexUnlock(pgphPvt->lock); return (pgphNode); } - + void epicsShareAPI gphDelete(gphPvt *pgphPvt, const char *name, void *pvtid) { ELLLIST **paplist; @@ -167,7 +173,7 @@ void epicsShareAPI gphDelete(gphPvt *pgphPvt, const char *name, void *pvtid) epicsMutexUnlock(pgphPvt->lock); return; } - + void epicsShareAPI gphFreeMem(gphPvt *pgphPvt) { ELLLIST **paplist; From 8baf3b136222bdb5ea99a9c5396bd80a62682b7f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:39 -0400 Subject: [PATCH 06/81] Ignore eclipse project configuration files. --- .bzrignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.bzrignore b/.bzrignore index a73784625..0e2652f17 100644 --- a/.bzrignore +++ b/.bzrignore @@ -6,3 +6,6 @@ ./include ./templates **/O.* +./.cproject +./.project +./.settings From eee5a2898368005d9ce5c12a343cff5a0355c188 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:21:39 -0400 Subject: [PATCH 07/81] Integrated dbChannel more and extended its functionality: * Register filters in pdbbase, using a list and gphash * Do record and field search first * Test loads a DBD/DB file to provide a record to find --- src/ioc/db/dbChannel.c | 290 +++++++++++++++++++++++++----- src/ioc/db/dbChannel.h | 21 ++- src/ioc/db/test/dbChannelTest.c | 147 ++++++++------- src/ioc/db/test/dbChannelTest.dbx | 13 ++ src/ioc/dbStatic/dbBase.h | 9 +- src/ioc/dbStatic/dbStaticLib.c | 159 ++++++++-------- 6 files changed, 433 insertions(+), 206 deletions(-) create mode 100644 src/ioc/db/test/dbChannelTest.dbx diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index d58df92a0..313ecea72 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -13,8 +13,12 @@ #include "dbBase.h" #include "link.h" #include "dbAccessDefs.h" +#include "dbStaticLib.h" #include "epicsAssert.h" #include "errlog.h" +#include "gpHash.h" +#include "recSup.h" +#include "special.h" #include "yajl_parse.h" @@ -24,10 +28,16 @@ typedef struct parseContext { int depth; } parseContext; +typedef struct filterPlugin { + ELLNODE node; + const char *name; + const chFilterIf *fif; +} filterPlugin; + #define CALLIF(rtn) rtn && rtn -static void chp_value(parseContext *parser, int result) +static void chf_value(parseContext *parser, int result) { chFilter *filter = parser->filter; @@ -40,7 +50,7 @@ static void chp_value(parseContext *parser, int result) } -static int chp_null(void * ctx) +static int chf_null(void * ctx) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; @@ -48,11 +58,11 @@ static int chp_null(void * ctx) assert(filter); result = CALLIF(filter->fif->parse_null) (filter ); - chp_value(parser, result); + chf_value(parser, result); return result; } -static int chp_boolean(void * ctx, int boolVal) +static int chf_boolean(void * ctx, int boolVal) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; @@ -60,11 +70,11 @@ static int chp_boolean(void * ctx, int boolVal) assert(filter); result = CALLIF(filter->fif->parse_boolean) (filter , boolVal); - chp_value(parser, result); + chf_value(parser, result); return result; } -static int chp_integer(void * ctx, long integerVal) +static int chf_integer(void * ctx, long integerVal) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; @@ -72,11 +82,11 @@ static int chp_integer(void * ctx, long integerVal) assert(filter); result = CALLIF(filter->fif->parse_integer) (filter , integerVal); - chp_value(parser, result); + chf_value(parser, result); return result; } -static int chp_double(void * ctx, double doubleVal) +static int chf_double(void * ctx, double doubleVal) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; @@ -84,11 +94,11 @@ static int chp_double(void * ctx, double doubleVal) assert(filter); result = CALLIF(filter->fif->parse_double) (filter , doubleVal); - chp_value(parser, result); + chf_value(parser, result); return result; } -static int chp_string(void * ctx, const unsigned char * stringVal, +static int chf_string(void * ctx, const unsigned char * stringVal, unsigned int stringLen) { parseContext *parser = (parseContext *) ctx; @@ -97,11 +107,11 @@ static int chp_string(void * ctx, const unsigned char * stringVal, assert(filter); result = CALLIF(filter->fif->parse_string) (filter , (const char *) stringVal, stringLen); - chp_value(parser, result); + chf_value(parser, result); return result; } -static int chp_start_map(void * ctx) +static int chf_start_map(void * ctx) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; @@ -115,7 +125,7 @@ static int chp_start_map(void * ctx) return CALLIF(filter->fif->parse_start_map) (filter ); } -static int chp_map_key(void * ctx, const unsigned char * key, +static int chf_map_key(void * ctx, const unsigned char * key, unsigned int stringLen) { parseContext *parser = (parseContext *) ctx; @@ -148,7 +158,7 @@ static int chp_map_key(void * ctx, const unsigned char * key, return result; } -static int chp_end_map(void * ctx) +static int chf_end_map(void * ctx) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; @@ -156,14 +166,14 @@ static int chp_end_map(void * ctx) if (filter) { int result = CALLIF(filter->fif->parse_end_map) (filter ); - if (--parser->depth == 0) chp_value(parser, result); + if (--parser->depth == 0) chf_value(parser, result); return result; } assert(parser->depth == 0); return parse_continue; /* Final closing '}' */ } -static int chp_start_array(void * ctx) +static int chf_start_array(void * ctx) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; @@ -173,7 +183,7 @@ static int chp_start_array(void * ctx) return CALLIF(filter->fif->parse_start_array) (filter ); } -static int chp_end_array(void * ctx) +static int chf_end_array(void * ctx) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; @@ -181,66 +191,250 @@ static int chp_end_array(void * ctx) assert(filter); result = CALLIF(filter->fif->parse_end_array) (filter ); - if (--parser->depth == 0) chp_value(parser, result); + if (--parser->depth == 0) chf_value(parser, result); return result; } -static const yajl_callbacks chp_callbacks = - { chp_null, chp_boolean, chp_integer, chp_double, NULL, chp_string, - chp_start_map, chp_map_key, chp_end_map, chp_start_array, chp_end_array }; +static const yajl_callbacks chf_callbacks = + { chf_null, chf_boolean, chf_integer, chf_double, NULL, chf_string, + chf_start_map, chf_map_key, chf_end_map, chf_start_array, chf_end_array }; -static const yajl_parser_config chp_config = +static const yajl_parser_config chf_config = { 0, 1 }; /* allowComments = NO , checkUTF8 = YES */ -void dbChannelInit(dbChannel *chan) -{ - ellInit(&chan->filters); -} -// Move ellInit into ChannelFind, rename it again? -long dbChannelFind(dbChannel *chan, const char *name) +static long chf_parse(dbChannel *chan, const char *json) { parseContext parser = { chan, NULL, 0}; - yajl_handle yh = yajl_alloc(&chp_callbacks, &chp_config, NULL, &parser); - size_t len = strlen(name); - yajl_status status; + yajl_handle yh = yajl_alloc(&chf_callbacks, &chf_config, NULL, &parser); + size_t jlen = strlen(json); + yajl_status ys; + long status; - status = yajl_parse(yh, (const unsigned char *) name, len); - if (status != yajl_status_ok && parser.filter) { + if (!yh) return S_db_noMemory; + + ys = yajl_parse(yh, (const unsigned char *) json, jlen); + if (ys == yajl_status_insufficient_data) + ys = yajl_parse_complete(yh); + + switch(ys) { + case yajl_status_ok: + status = 0; + break; + + case yajl_status_error: { + unsigned char *err; + + err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen); + printf("dbChannelFind: %s\n", err); + yajl_free_error(yh, err); + } /* fall through */ + default: + status = S_db_notFound; + } + + if (parser.filter) { + assert(status); parser.filter->fif->parse_abort(parser.filter); free(parser.filter); } yajl_free(yh); - return status == yajl_status_ok; + 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_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 +}; + +long dbChannelFind(dbChannel *chan, const char *pname) +{ + DBADDR *paddr; + DBENTRY dbEntry; + dbFldDes *pflddes; + long status; + short dbfType; + + if (!chan || !pname || !*pname || !pdbbase) + return S_db_notFound; + + if (chan->magic == DBCHANNEL_MAGIC) { + chFilter *filter; + + while ((filter = (chFilter *) ellGet(&chan->filters))) { + filter->fif->channel_close(filter); + free(filter); + } + } else { + ellInit(&chan->filters); + chan->magic = DBCHANNEL_MAGIC; + } + + dbInitEntry(pdbbase, &dbEntry); + status = dbFindRecordPart(&dbEntry, &pname); + if (status) goto finish; + + if (*pname == '.') ++pname; + status = dbFindFieldPart(&dbEntry, &pname); + if (status == S_dbLib_fieldNotFound) + status = dbGetAttributePart(&dbEntry, &pname); + if (status) goto finish; + + paddr = &chan->addr; + pflddes = dbEntry.pflddes; + dbfType = pflddes->field_type; + + paddr->precord = dbEntry.precnode->precord; + paddr->pfield = dbEntry.pfield; + paddr->pfldDes = pflddes; + paddr->no_elements = 1; + paddr->field_type = dbfType; + paddr->field_size = pflddes->size; + paddr->special = pflddes->special; + paddr->dbr_field_type = mapDBFToDBR[dbfType]; + + /* Handle field modifiers */ + while (*pname) { + switch (*pname) { + case '$': + /* Some field types can be accessed as char arrays */ + if (dbfType == DBF_STRING) { + paddr->no_elements = pflddes->size; + paddr->field_type = DBF_CHAR; + paddr->field_size = 1; + paddr->dbr_field_type = DBR_CHAR; + } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) { + /* Clients see a char array, but keep original dbfType */ + paddr->no_elements = PVNAME_STRINGSZ + 12; + paddr->field_size = 1; + paddr->dbr_field_type = DBR_CHAR; + } else { + status = S_dbLib_fieldNotFound; + goto finish; + } + break; + case '{': + status = chf_parse(chan, pname); + goto finish; + default: + status = S_dbLib_fieldNotFound; + goto finish; + } + pname++; + } + +finish: + dbFinishEntry(&dbEntry); + return status; } long dbChannelOpen(dbChannel *chan) { - return S_db_notFound; + DBADDR *paddr; + chFilter *filter; + long status = 0; + + if (chan->magic != DBCHANNEL_MAGIC) return S_db_notInit; + + paddr = &chan->addr; + if (paddr->special == SPC_DBADDR) { + struct rset *prset = dbGetRset(paddr); + + /* Let record type modify the dbAddr */ + if (prset && prset->cvt_dbaddr) { + status = prset->cvt_dbaddr(paddr); + if (status) return status; + } + } + + filter = (chFilter *) ellFirst(&chan->filters); + while (filter) { + status = filter->fif->channel_open(filter); + if (status) break; + filter = (chFilter *) ellNext(&filter->node); + } + return status; +} + +void dbChannelReport(dbChannel *chan, int level) +{ + chFilter *filter; + + if (chan->magic != DBCHANNEL_MAGIC) return; + + filter = (chFilter *) ellFirst(&chan->filters); + while (filter) { + filter->fif->channel_report(filter, level); + filter = (chFilter *) ellNext(&filter->node); + } } long dbChannelClose(dbChannel *chan) { + chFilter *filter; + + if (chan->magic != DBCHANNEL_MAGIC) return S_db_notInit; + + while ((filter = (chFilter *) ellGet(&chan->filters))) { + filter->fif->channel_close(filter); + free(filter); + } + chan->magic = 0; return 0; } +/* FIXME: Do these belong in a different file? */ -const chFilterIf * filt = NULL; -const char *fname = NULL; - -void dbRegisterFilter(const char *key, const chFilterIf *fif) +void dbRegisterFilter(const char *name, const chFilterIf *fif) { - /* FIXME: Add filter to a list and hash in pdbbase */ - filt = fif; - fname = key; - fif->plugin_init(); + GPHENTRY *pgph; + filterPlugin *pfilt; + + if (!pdbbase) { + printf("dbRegisterFilter: pdbbase not set!\n"); + return; + } + + pgph = gphFind(pdbbase->pgpHash, name, &pdbbase->filterList); + if (pgph) return; + + pfilt = dbCalloc(1, sizeof(filterPlugin)); + pfilt->name = strdup(name); + pfilt->fif = fif; + + ellAdd(&pdbbase->filterList, &pfilt->node); + pgph = gphAdd(pdbbase->pgpHash, pfilt->name, &pdbbase->filterList); + if (!pgph) { + free((void *)pfilt->name); + free(pfilt); + printf("dbRegisterFilter: gphAdd failed\n"); + return; + } + pgph->userPvt = pfilt; } -const chFilterIf * dbFindFilter(const char *key, size_t len) +const chFilterIf * dbFindFilter(const char *name, size_t len) { - /* FIXME: gpHash lookup */ - if (strncmp(fname, key, len) == 0) - return filt; - return NULL; + GPHENTRY *pgph = gphFindParse(pdbbase->pgpHash, name, len, &pdbbase->filterList); + filterPlugin *pfilt; + + if (!pgph) return NULL; + pfilt = (filterPlugin *)pgph->userPvt; + return pfilt->fif; } diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index 1508d5c1a..aa43325b0 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -11,28 +11,33 @@ #include "dbDefs.h" #include "dbAddr.h" #include "ellLib.h" +#include "epicsTypes.h" +#include "errMdef.h" #include "shareLib.h" -enum {parse_stop, parse_continue}; - #ifdef __cplusplus // extern "C" { #endif +#define DBCHANNEL_MAGIC 0xdbc4a9e1 + +#define S_db_notInit (M_dbAccess|21) /*dbChannel not initialized*/ + + /* A dbChannel points to a record field, and can have multiple filters */ typedef struct dbChannel { + epicsUInt32 magic; dbAddr addr; ELLLIST filters; } dbChannel; typedef struct chFilter chFilter; +/* Return values from chFilterIf->parse_* routines: */ +enum {parse_stop, parse_continue}; + /* These routines must be implemented by each filter plug-in */ typedef struct chFilterIf { - /* Plug-in management */ - void (* plugin_init)(void); - void (* plugin_exit)(void); - /* Parsing event handlers */ int (* parse_start)(chFilter *filter); void (* parse_abort)(chFilter *filter); @@ -68,9 +73,9 @@ struct chFilter { void *puser; }; -epicsShareFunc void dbChannelInit(dbChannel *chan); -epicsShareFunc long dbChannelFind(dbChannel *chan, const char *name); +epicsShareFunc long dbChannelFind(dbChannel *chan, const char *pname); epicsShareFunc long dbChannelOpen(dbChannel *chan); +epicsShareFunc void dbChannelReport(dbChannel *chan, int level); epicsShareFunc long dbChannelClose(dbChannel *chan); epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif); diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index 31ca3180d..221a6b3d5 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -6,165 +6,178 @@ \*************************************************************************/ #include "dbChannel.h" +#include "dbStaticLib.h" +#include "dbAccessDefs.h" #include "epicsUnitTest.h" #include "testMain.h" /* Expected call bit definitions */ -#define e_init 0x00000001 -#define e_exit 0x00000002 -#define e_start 0x00000004 -#define e_abort 0x00000008 -#define e_end 0x00000010 -#define e_null 0x00000020 -#define e_boolean 0x00000040 -#define e_integer 0x00000080 -#define e_double 0x00000100 -#define e_string 0x00000200 -#define e_start_map 0x00000400 -#define e_map_key 0x00000800 -#define e_end_map 0x00001000 -#define e_start_array 0x00002000 -#define e_end_array 0x00004000 -#define e_open 0x00008000 -#define e_report 0x00010000 -#define e_close 0x00020000 +#define e_start 0x00000001 +#define e_abort 0x00000002 +#define e_end 0x00000004 +#define e_null 0x00000008 +#define e_boolean 0x00000010 +#define e_integer 0x00000020 +#define e_double 0x00000040 +#define e_string 0x00000080 +#define e_start_map 0x00000100 +#define e_map_key 0x00000200 +#define e_end_map 0x00000400 +#define e_start_array 0x00000800 +#define e_end_array 0x00001000 +#define e_open 0x00002000 +#define e_report 0x00004000 +#define e_close 0x00008000 unsigned int e; - -void f_init(void) -{ - testOk(e & e_init, "plugin_init expected"); -} -void f_exit(void) -{ - testOk(e & e_exit, "plugin_exit expected"); -} - int p_start(chFilter *filter) { - testOk(e & e_start, "parse_start expected"); + testOk(e & e_start, "parse_start called"); return parse_continue; } void p_abort(chFilter *filter) { - testOk(e & e_abort, "parse_abort expected"); + testOk(e & e_abort, "parse_abort called"); } int p_end(chFilter *filter) { - testOk(e & e_end, "parse_end expected"); + testOk(e & e_end, "parse_end called"); return parse_continue; } int p_null(chFilter *filter) { - testOk(e & e_null, "parse_null expected"); + testOk(e & e_null, "parse_null called"); return parse_continue; } int p_boolean(chFilter *filter, int boolVal) { - testOk(e & e_boolean, "parse_boolean expected, val = %d", boolVal); + testOk(e & e_boolean, "parse_boolean called, val = %d", boolVal); return parse_continue; } int p_integer(chFilter *filter, long integerVal) { - testOk(e & e_integer, "parse_integer expected, val = %ld", integerVal); + testOk(e & e_integer, "parse_integer called, val = %ld", integerVal); return parse_continue; } int p_double(chFilter *filter, double doubleVal) { - testOk(e & e_double, "parse_double expected, val = %g", doubleVal); + testOk(e & e_double, "parse_double called, val = %g", doubleVal); return parse_continue; } int p_string(chFilter *filter, const char *stringVal, size_t stringLen) { - testOk(e & e_string, "parse_string expected, val = '%.*s'", stringLen, stringVal); + testOk(e & e_string, "parse_string called, val = '%.*s'", stringLen, + stringVal); return parse_continue; } int p_start_map(chFilter *filter) { - testOk(e & e_start_map, "parse_start_map expected"); + testOk(e & e_start_map, "parse_start_map called"); return parse_continue; } int p_map_key(chFilter *filter, const char *key, size_t stringLen) { - testOk(e & e_map_key, "parse_map_key expected, key = '%.*s'", stringLen, key); + testOk(e & e_map_key, "parse_map_key called, key = '%.*s'", stringLen, key); return parse_continue; } int p_end_map(chFilter *filter) { - testOk(e & e_end_map, "parse_end_map expected"); + testOk(e & e_end_map, "parse_end_map called"); return parse_continue; } int p_start_array(chFilter *filter) { - testOk(e & e_start_array, "parse_start_array expected"); + testOk(e & e_start_array, "parse_start_array called"); return parse_continue; } int p_end_array(chFilter *filter) { - testOk(e & e_end_array, "parse_end_array expected"); + testOk(e & e_end_array, "parse_end_array called"); return parse_continue; } long c_open(chFilter *filter) { - testOk(e & e_open, "channel_open expected"); + testOk(e & e_open, "channel_open called"); return 0; } void c_report(chFilter *filter, int level) { - testOk(e & e_report, "channel_report expected, level = %d", level); + testOk(e & e_report, "channel_report called, level = %d", level); } void c_close(chFilter *filter) { - testOk(e & e_close, "channel_close expected"); + testOk(e & e_close, "channel_close called"); } -chFilterIf testIf = - { f_init, f_exit, p_start, p_abort, p_end, p_null, p_boolean, - p_integer, p_double, p_string, p_start_map, p_map_key, p_end_map, - p_start_array, p_end_array, c_open, c_report, c_close }; +chFilterIf anyIf = + { p_start, p_abort, p_end, p_null, p_boolean, p_integer, p_double, + p_string, p_start_map, p_map_key, p_end_map, p_start_array, p_end_array, + c_open, c_report, c_close }; chFilterIf scalarIf = - { f_init, f_exit, p_start, p_abort, p_end, p_null, p_boolean, - p_integer, p_double, p_string, NULL, NULL, NULL, NULL, NULL, c_open, - c_report, c_close }; + { p_start, p_abort, p_end, p_null, p_boolean, p_integer, p_double, + p_string, NULL, NULL, NULL, NULL, NULL, c_open, + c_report, c_close}; MAIN(dbChannelTest) { dbChannel ch; - testPlan(33); - dbChannelInit(&ch); + testPlan(45); + + testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.dbx", ".:..", NULL)); + testOk(!!pdbbase, "pdbbase was set"); e = 0; - testOk1(dbChannelFind(&ch, "{}")); + testOk1(!dbChannelFind(&ch, "x.NAME$")); + testOk1(ch.addr.no_elements > 1); - e = e_init; - dbRegisterFilter("test", &testIf); + testOk1(!dbChannelFind(&ch, "x.{}")); + + dbRegisterFilter("any", &anyIf); e = e_start | e_null | e_end; - testOk1(dbChannelFind(&ch, "{\"test\":null}")); + testOk1(!dbChannelFind(&ch, "x.{\"any\":null}")); + e = e_close; + testOk1(!dbChannelClose(&ch)); - e = e_start | e_start_array | e_boolean | e_integer | e_end_array | e_end; - testOk1(dbChannelFind(&ch, "{\"test\":[true,1]}")); - - e = e_start | e_start_map | e_map_key | e_double | e_string | e_end_map | e_end; - testOk1(dbChannelFind(&ch, "{\"test\":{\"a\":2.7183,\"b\":\"c\"}}")); - - e = e_init; dbRegisterFilter("scalar", &scalarIf); e = e_start | e_null | e_end; - testOk1(dbChannelFind(&ch, "{\"scalar\":null}")); + testOk1(!dbChannelFind(&ch, "x.{\"scalar\":null}")); + + e = e_report; + dbChannelReport(&ch, 0); + + e = e_close; + testOk1(!dbChannelClose(&ch)); + + e = e_start | e_start_array | e_boolean | e_integer | e_end_array | e_end; + testOk1(!dbChannelFind(&ch, "x.{\"any\":[true,1]}")); + e = e_close; + testOk1(!dbChannelClose(&ch)); + + e = e_start | e_start_map | e_map_key | e_double | e_string | e_end_map + | e_end; + testOk1(!dbChannelFind(&ch, "x.{\"any\":{\"a\":2.7183,\"b\":\"c\"}}")); + + e = e_close | e_start | e_abort; + testOk1(dbChannelFind(&ch, "x.{\"scalar\":[null]}")); + e = 0; + testOk1(!dbChannelClose(&ch)); e = e_start | e_abort; - testOk1(!dbChannelFind(&ch, "{\"scalar\":[null]}")); - testOk1(!dbChannelFind(&ch, "{\"scalar\":{}}")); + testOk1(dbChannelFind(&ch, "x.{\"scalar\":{}}")); + e = 0; + testOk1(!dbChannelClose(&ch)); + + dbFreeBase(pdbbase); return testDone(); } diff --git a/src/ioc/db/test/dbChannelTest.dbx b/src/ioc/db/test/dbChannelTest.dbx new file mode 100644 index 000000000..b743359c1 --- /dev/null +++ b/src/ioc/db/test/dbChannelTest.dbx @@ -0,0 +1,13 @@ +# This is a combined minimal DBD and DB file + +recordtype(x) { + field(NAME, DBF_STRING) { + prompt("Record Name") + special(SPC_NOMOD) + size(61) + } + field(VAL, DBF_LONG) { + prompt("Value") + } +} +record(x, x) {} diff --git a/src/ioc/dbStatic/dbBase.h b/src/ioc/dbStatic/dbBase.h index 55268ea3a..6be2465d5 100644 --- a/src/ioc/dbStatic/dbBase.h +++ b/src/ioc/dbStatic/dbBase.h @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* $Revision-Id$ * @@ -19,7 +19,7 @@ #include "dbFldTypes.h" #include "ellLib.h" #include "dbDefs.h" - + typedef struct dbMenu { ELLNODE node; char *name; @@ -67,7 +67,7 @@ typedef struct brkTable { /* breakpoint table */ long number; /*number of brkInt in this table*/ struct brkInt *paBrkInt; /* ptr to array of brkInts */ }brkTable; - + typedef struct dbFldDes{ /* field description */ char *prompt; /*Prompt string for DCT*/ char *name; /*Field name*/ @@ -128,7 +128,7 @@ typedef struct dbVariableDef { ELLNODE node; char *name; char *type; - + }dbVariableDef; typedef struct dbRecordType { @@ -164,6 +164,7 @@ typedef struct dbBase { ELLLIST functionList; ELLLIST variableList; ELLLIST bptList; + ELLLIST filterList; void *pathPvt; struct dbPvd *ppvd; struct gphPvt *pgpHash; diff --git a/src/ioc/dbStatic/dbStaticLib.c b/src/ioc/dbStatic/dbStaticLib.c index 7b42704f5..bcd630c6c 100644 --- a/src/ioc/dbStatic/dbStaticLib.c +++ b/src/ioc/dbStatic/dbStaticLib.c @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* $Revision-Id$ */ @@ -41,7 +41,7 @@ #include "guigroup.h" #include "dbStaticLib.h" #include "dbStaticPvt.h" - + int dbStaticDebug = 0; static char *pNullString = ""; #define messagesize 100 @@ -191,7 +191,7 @@ sizeof(promptINST_IO)/sizeof(char *), sizeof(promptBBGPIB_IO)/sizeof(char *), sizeof(promptRF_IO)/sizeof(char *), sizeof(promptVXI_IO)/sizeof(char *)}; - + /*forward references for private routines*/ static FILE *openOutstream(const char *filename); static void finishOutstream(FILE *stream); @@ -203,7 +203,7 @@ static void zeroDbentry(DBENTRY *pdbentry); static char *getpMessage(DBENTRY *pdbentry); static long putPvLink(DBENTRY *pdbentry,short pvlMask,const char *pvname); static long epicsShareAPI dbAddOnePath (DBBASE *pdbbase, const char *path, unsigned length); - + /* internal routines*/ static FILE *openOutstream(const char *filename) { @@ -347,7 +347,7 @@ void dbFreeLinkContents(struct link *plink) if(plink->text) free(plink->text); memset((char *)plink,0,sizeof(struct link)); } - + void dbFreePath(DBBASE *pdbbase) { ELLLIST *ppathList; @@ -366,7 +366,7 @@ void dbFreePath(DBBASE *pdbbase) return; } - + static long mapLINKTtoFORMT(DBLINK *plink,dbFldDes *pflddes,int *ind) { switch(plink->type) { @@ -407,7 +407,7 @@ static long mapLINKTtoFORMT(DBLINK *plink,dbFldDes *pflddes,int *ind) } return(S_dbLib_badLink); } - + static void entryErrMessage(DBENTRY *pdbentry,long status,char *mess) { char message[200]; @@ -439,7 +439,7 @@ static void entryErrMessage(DBENTRY *pdbentry,long status,char *mess) strcat(pmessage,mess); errMessage(status,pmessage); } - + static void zeroDbentry(DBENTRY *pdbentry) { /*NOTE that pdbbase, message, and formpvt MUST NOT be set to NULL*/ @@ -466,7 +466,7 @@ static long putPvLink(DBENTRY *pdbentry,short pvlMask,const char *pvname) dbFldDes *pflddes; DBLINK *plink; char *pname; - + dbGetFieldAddress(pdbentry); pflddes = pdbentry->pflddes; if(!pflddes) return(-1); @@ -489,7 +489,7 @@ static long putPvLink(DBENTRY *pdbentry,short pvlMask,const char *pvname) } return(S_dbLib_badLink); } - + /*Public only for dbStaticNoRun*/ dbDeviceMenu *dbGetDeviceMenu(DBENTRY *pdbentry) { @@ -526,7 +526,7 @@ dbDeviceMenu *dbGetDeviceMenu(DBENTRY *pdbentry) pflddes->ftPvt = pdbDeviceMenu; return(pdbDeviceMenu); } - + /* Beginning of Public Routines */ #define INC_SIZE 256 @@ -555,7 +555,7 @@ void epicsShareAPI dbCatString(char **string,int *stringLength,char *src,char *s strcat(*string,src); *stringLength += strlen(src); } - + dbBase * epicsShareAPI dbAllocBase(void) { dbBase *pdbbase; @@ -568,6 +568,7 @@ dbBase * epicsShareAPI dbAllocBase(void) ellInit(&pdbbase->functionList); ellInit(&pdbbase->variableList); ellInit(&pdbbase->bptList); + ellInit(&pdbbase->filterList); gphInitPvt(&pdbbase->pgpHash,256); dbPvdInitPvt(pdbbase); return (pdbbase); @@ -595,7 +596,7 @@ void epicsShareAPI dbFreeBase(dbBase *pdbbase) brkTable *pbrkTableNext; int i; DBENTRY dbentry; - + dbInitEntry(pdbbase,&dbentry); pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); @@ -620,7 +621,7 @@ void epicsShareAPI dbFreeBase(dbBase *pdbbase) free((void *)pdbFldDes->initial); if(pdbFldDes->field_type==DBF_DEVICE && pdbFldDes->ftPvt) { dbDeviceMenu *pdbDeviceMenu; - + pdbDeviceMenu = (dbDeviceMenu *)pdbFldDes->ftPvt; free((void *)pdbDeviceMenu->papChoice); free((void *)pdbDeviceMenu); @@ -729,12 +730,12 @@ void epicsShareAPI dbFreeBase(dbBase *pdbbase) free((void *)pdbbase); return; } - + DBENTRY * epicsShareAPI dbAllocEntry(dbBase *pdbbase) { DBENTRY *pdbentry; - pdbentry = dbmfMalloc(sizeof(DBENTRY)); + pdbentry = dbmfMalloc(sizeof(DBENTRY)); memset(pdbentry,'\0',sizeof(DBENTRY)); pdbentry->pdbbase = pdbbase; return(pdbentry); @@ -780,7 +781,7 @@ void epicsShareAPI dbCopyEntryContents(DBENTRY *pfrom,DBENTRY *pto) pto->formpvt = NULL; } - + long epicsShareAPI dbPath(DBBASE *pdbbase,const char *path) { if(!pdbbase) return(-1); @@ -796,7 +797,7 @@ long epicsShareAPI dbAddPath(DBBASE *pdbbase,const char *path) const char *plast; unsigned expectingPath; unsigned sawMissingPath; - + if(!pdbbase) return(-1); ppathList = (ELLLIST *)pdbbase->pathPvt; if(!ppathList) { @@ -865,7 +866,7 @@ static long epicsShareAPI dbAddOnePath (DBBASE *pdbbase, const char *path, unsig { ELLLIST *ppathList; dbPathNode *pdbPathNode; - + if(!pdbbase) return(-1); ppathList = (ELLLIST *)pdbbase->pathPvt; @@ -877,7 +878,7 @@ static long epicsShareAPI dbAddOnePath (DBBASE *pdbbase, const char *path, unsig return 0; } - + long epicsShareAPI dbWriteRecord(DBBASE *ppdbbase,const char *filename, const char *precordTypename,int level) { @@ -974,7 +975,7 @@ long epicsShareAPI dbWriteRecordFP( dbFinishEntry(pdbentry); return(0); } - + long epicsShareAPI dbWriteMenu( DBBASE *ppdbbase,const char *filename,const char *menuName) { @@ -1017,7 +1018,7 @@ long epicsShareAPI dbWriteMenuFP(DBBASE *pdbbase,FILE *fp,const char *menuName) } return(0); } - + long epicsShareAPI dbWriteRecordType( DBBASE *pdbbase,const char *filename,const char *recordTypeName) { @@ -1114,7 +1115,7 @@ long epicsShareAPI dbWriteRecordTypeFP( } return(0); } - + long epicsShareAPI dbWriteDevice(DBBASE *pdbbase,const char *filename) { FILE *stream; @@ -1156,7 +1157,7 @@ long epicsShareAPI dbWriteDeviceFP(DBBASE *pdbbase,FILE *fp) } return(0); } - + long epicsShareAPI dbWriteDriver(DBBASE *pdbbase,const char *filename) { FILE *stream; @@ -1227,7 +1228,7 @@ long epicsShareAPI dbWriteVariableFP(DBBASE *pdbbase,FILE *fp) } return(0); } - + long epicsShareAPI dbWriteBreaktable(DBBASE *pdbbase,const char *filename) { FILE *stream; @@ -1262,7 +1263,7 @@ long epicsShareAPI dbWriteBreaktableFP(DBBASE *pdbbase,FILE *fp) } return(0); } - + long epicsShareAPI dbFindRecordType(DBENTRY *pdbentry,const char *recordType) { dbBase *pdbbase = pdbentry->pdbbase; @@ -1306,7 +1307,7 @@ int epicsShareAPI dbGetNRecordTypes(DBENTRY *pdbentry) { return(ellCount(&pdbentry->pdbbase->recordTypeList)); } - + long epicsShareAPI dbPutRecordAttribute( DBENTRY *pdbentry, const char *name, const char*value) { @@ -1382,7 +1383,7 @@ long epicsShareAPI dbGetRecordAttribute(DBENTRY *pdbentry, const char *pname) { return dbGetAttributePart(pdbentry, &pname); } - + long epicsShareAPI dbFirstField(DBENTRY *pdbentry,int dctonly) { @@ -1426,7 +1427,7 @@ long epicsShareAPI dbNextField(DBENTRY *pdbentry,int dctonly) indfield++; } } - + int epicsShareAPI dbGetFieldType(DBENTRY *pdbentry) { dbFldDes *pflddes = pdbentry->pflddes; @@ -1450,7 +1451,7 @@ int epicsShareAPI dbGetNFields(DBENTRY *pdbentry,int dctonly) n = 0; for(indfield=0; indfieldno_fields; indfield++) { pflddes = precordType->papFldDes[indfield]; - if(dctonly && (pflddes->field_type==DBF_DEVICE) + if(dctonly && (pflddes->field_type==DBF_DEVICE) && (ellCount(&precordType->devList)==0) ) continue; if(!dctonly || pflddes->promptgroup) n++; } @@ -1488,7 +1489,7 @@ int epicsShareAPI dbGetPromptGroup(DBENTRY *pdbentry) if(!pflddes) return(0); return(pflddes->promptgroup); } - + long epicsShareAPI dbCreateRecord(DBENTRY *pdbentry,const char *precordName) { dbRecordType *precordType = pdbentry->precordType; @@ -1535,7 +1536,7 @@ long epicsShareAPI dbCreateRecord(DBENTRY *pdbentry,const char *precordName) if(!ppvd) {errMessage(-1,"Logic Err: Could not add to PVD");return(-1);} return(0); } - + long epicsShareAPI dbDeleteAliases(DBENTRY *pdbentry) { dbBase *pdbbase = pdbentry->pdbbase; @@ -1653,7 +1654,7 @@ long epicsShareAPI dbFindRecord(DBENTRY *pdbentry, const char *pname) return dbFindField(pdbentry, ++pname); return 0; } - + long epicsShareAPI dbFirstRecord(DBENTRY *pdbentry) { dbRecordType *precordType = pdbentry->precordType; @@ -1706,7 +1707,7 @@ char * epicsShareAPI dbGetRecordName(DBENTRY *pdbentry) if(!precnode) return NULL; return precnode->recordname; } - + long epicsShareAPI dbRenameRecord(DBENTRY *pdbentry,const char *newName) { dbBase *pdbbase = pdbentry->pdbbase; @@ -1752,7 +1753,7 @@ long epicsShareAPI dbRenameRecord(DBENTRY *pdbentry,const char *newName) /*Leave pdbentry pointing to newly renamed record*/ return(dbFindRecord(pdbentry,newName)); } - + long epicsShareAPI dbVisibleRecord(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; @@ -1778,7 +1779,7 @@ int epicsShareAPI dbIsVisibleRecord(DBENTRY *pdbentry) if(!precnode) return 0; return precnode->flags & DBRN_FLAGS_VISIBLE ? 1 : 0; } - + long epicsShareAPI dbCreateAlias(DBENTRY *pdbentry, const char *alias) { dbRecordType *precordType = pdbentry->precordType; @@ -1828,7 +1829,7 @@ int epicsShareAPI dbIsAlias(DBENTRY *pdbentry) if(!precnode) return 0; return precnode->flags & DBRN_FLAGS_ISALIAS ? 1 : 0; } - + long epicsShareAPI dbCopyRecord(DBENTRY *pdbentry,const char *newRecordName,int overWriteOK) { dbRecordType *precordType = pdbentry->precordType; @@ -1882,7 +1883,7 @@ long epicsShareAPI dbCopyRecord(DBENTRY *pdbentry,const char *newRecordName,int /*Leave pdbentry pointing to newRecordName*/ return(dbFindRecord(pdbentry,newRecordName)); } - + long epicsShareAPI dbFindFieldPart(DBENTRY *pdbentry,const char **ppname) { dbRecordType *precordType = pdbentry->precordType; @@ -1967,7 +1968,7 @@ long epicsShareAPI dbFindField(DBENTRY *pdbentry,const char *pname) int epicsShareAPI dbFoundField(DBENTRY *pdbentry) { return((pdbentry->pfield) ? TRUE : FALSE); } - + char * epicsShareAPI dbGetString(DBENTRY *pdbentry) { dbFldDes *pflddes = pdbentry->pflddes; @@ -1997,7 +1998,7 @@ char * epicsShareAPI dbGetString(DBENTRY *pdbentry) case DBF_DEVICE: return(dbGetStringNum(pdbentry)); case DBF_INLINK: - case DBF_OUTLINK: + case DBF_OUTLINK: if(!pfield) {strcpy(message,"Field not found"); return(message);} plink = (DBLINK *)pfield; switch(plink->type) { @@ -2138,7 +2139,7 @@ char * epicsShareAPI dbGetString(DBENTRY *pdbentry) } return (message); } - + static void cvtDecimalOrHexToShort(char *from,short *value) { if(strspn(from,"0x")==2 || strspn(from,"0X")==2) { @@ -2264,7 +2265,7 @@ long epicsShareAPI dbPutString(DBENTRY *pdbentry,const char *pstring) goto done; } switch (plink->type) { - case CONSTANT: + case CONSTANT: case PV_LINK: { short ppOpt = 0; short msOpt = 0; @@ -2428,7 +2429,7 @@ long epicsShareAPI dbPutString(DBENTRY *pdbentry,const char *pstring) } break; case GPIB_IO: { - char *end; + char *end; if(!(end = strchr(pstr,'#'))) return (S_dbLib_badField); pstr = end + 1; @@ -2545,7 +2546,7 @@ done: } return(status); } - + char * epicsShareAPI dbVerify(DBENTRY *pdbentry,const char *pstring) { dbFldDes *pflddes = pdbentry->pflddes; @@ -2591,13 +2592,13 @@ char * epicsShareAPI dbVerify(DBENTRY *pdbentry,const char *pstring) return(message); } switch (pflddes->field_type) { - case DBF_CHAR : + case DBF_CHAR : if(value<-128 || value>127) { strcpy(message,"must have -128<=value<=127"); return(message); } return(NULL); - case DBF_SHORT : + case DBF_SHORT : if(value<-32768 || value>32767) { strcpy(message,"must have -32768<=value<=32767"); return(message); @@ -2626,14 +2627,14 @@ char * epicsShareAPI dbVerify(DBENTRY *pdbentry,const char *pstring) return(message); } switch (pflddes->field_type) { - case DBF_UCHAR : + case DBF_UCHAR : if(value>255) { strcpy(message,"must have 0<=value<=255"); return(message); } return(NULL); case DBF_ENUM: - case DBF_USHORT : + case DBF_USHORT : if(value>65535) { strcpy(message,"must have 0<=value<=65535"); return(message); @@ -2646,7 +2647,7 @@ char * epicsShareAPI dbVerify(DBENTRY *pdbentry,const char *pstring) } } case DBF_FLOAT: - case DBF_DOUBLE: { + case DBF_DOUBLE: { double value; char *endp; @@ -2661,7 +2662,7 @@ char * epicsShareAPI dbVerify(DBENTRY *pdbentry,const char *pstring) dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt; char *pchoice; int i; - + if(!pdbMenu) return(NULL); for (i = 0; i < pdbMenu->nChoice; i++) { if(!(pchoice = pdbMenu->papChoiceValue[i])) continue; @@ -2699,7 +2700,7 @@ char * epicsShareAPI dbVerify(DBENTRY *pdbentry,const char *pstring) strcpy(message,"Not a valid field type"); return (message); } - + char *epicsShareAPI dbGetRange(DBENTRY *pdbentry) { dbFldDes *pflddes = pdbentry->pflddes; @@ -2729,14 +2730,14 @@ char *epicsShareAPI dbGetRange(DBENTRY *pdbentry) strcpy(message,"Not a valid field type"); return (message); } - + long epicsShareAPI dbFirstInfo(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; - + pdbentry->pinfonode = NULL; if (!precnode) return (S_dbLib_recNotFound); - + pdbentry->pinfonode = (dbInfoNode *)ellFirst(&precnode->infoList); return (pdbentry->pinfonode ? 0 : S_dbLib_infoNotFound); } @@ -2745,11 +2746,11 @@ long epicsShareAPI dbNextInfo(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; dbInfoNode *pinfo; - + if (!precnode) return (S_dbLib_recNotFound); pinfo = pdbentry->pinfonode; if (!pinfo) return (S_dbLib_infoNotFound); - + pinfo = (dbInfoNode *)ellNext(&pinfo->node); pdbentry->pinfonode = pinfo; return (pinfo ? 0 : S_dbLib_infoNotFound); @@ -2759,10 +2760,10 @@ long epicsShareAPI dbFindInfo(DBENTRY *pdbentry,const char *name) { dbRecordNode *precnode = pdbentry->precnode; dbInfoNode *pinfo; - + pdbentry->pinfonode = NULL; if (!precnode) return(S_dbLib_recNotFound); - + pinfo = (dbInfoNode *)ellFirst(&precnode->infoList); while (pinfo) { if (!strcmp(pinfo->name, name)) { @@ -2778,7 +2779,7 @@ long epicsShareAPI dbDeleteInfo(DBENTRY *pdbentry) { dbRecordNode *precnode = pdbentry->precnode; dbInfoNode *pinfo = pdbentry->pinfonode; - + if (!precnode) return (S_dbLib_recNotFound); if (!pinfo) return (S_dbLib_infoNotFound); ellDelete(&precnode->infoList,&pinfo->node); @@ -2841,11 +2842,11 @@ long epicsShareAPI dbPutInfo(DBENTRY *pdbentry,const char *name,const char *stri dbInfoNode *pinfo; dbRecordNode *precnode = pdbentry->precnode; if (!precnode) return (S_dbLib_recNotFound); - + dbFindInfo(pdbentry, name); pinfo = pdbentry->pinfonode; if (pinfo) return (dbPutInfoString(pdbentry, string)); - + /*Create new info node*/ pinfo = calloc(1,sizeof(dbInfoNode)); if (!pinfo) return (S_dbLib_outMem); @@ -2875,7 +2876,7 @@ brkTable * epicsShareAPI dbFindBrkTable(dbBase *pdbbase,const char *name) if(!pgph) return(NULL); return((brkTable *)pgph->userPvt); } - + dbMenu * epicsShareAPI dbFindMenu(dbBase *pdbbase,const char *name) { GPHENTRY *pgph; @@ -2933,7 +2934,7 @@ int epicsShareAPI dbGetNMenuChoices(DBENTRY *pdbentry) } return (-1); } - + char * epicsShareAPI dbGetMenuStringFromIndex(DBENTRY *pdbentry, int index) { dbFldDes *pflddes = pdbentry->pflddes; @@ -2996,13 +2997,13 @@ int epicsShareAPI dbGetMenuIndexFromString(DBENTRY *pdbentry, const char *choice } return (-1); } - + drvSup * epicsShareAPI dbFindDriver(dbBase *pdbbase, const char *name) { GPHENTRY *pgph = gphFind(pdbbase->pgpHash,name,&pdbbase->drvList); if (!pgph) return NULL; return (drvSup *) pgph->userPvt; } - + int epicsShareAPI dbAllocForm(DBENTRY *psave) { DBENTRY dbEntry; @@ -3076,7 +3077,7 @@ done: dbFinishEntry(pdbentry); return(nlines); } - + long epicsShareAPI dbFreeForm(DBENTRY *pdbentry) { if(pdbentry->formpvt) { @@ -3093,7 +3094,7 @@ char ** epicsShareAPI dbGetFormPrompt(DBENTRY *pdbentry) if(!pform) return(NULL); return(pform->prompt); } - + char ** epicsShareAPI dbGetFormValue(DBENTRY *pdbentry) { struct form *pform = pdbentry->formpvt; @@ -3105,7 +3106,7 @@ char ** epicsShareAPI dbGetFormValue(DBENTRY *pdbentry) if(!plink) return(NULL); value = pform->value; switch(pform->linkType) { - case FORM_CONSTANT: + case FORM_CONSTANT: if(plink->value.constantStr) { strcpy(*value,plink->value.constantStr); } else { @@ -3263,7 +3264,7 @@ char ** epicsShareAPI dbGetFormValue(DBENTRY *pdbentry) } return(pform->value); } - + long epicsShareAPI dbPutForm(DBENTRY *pdbentry,char **value) { struct form *pform = pdbentry->formpvt; @@ -3279,7 +3280,7 @@ long epicsShareAPI dbPutForm(DBENTRY *pdbentry,char **value) if(!plink) return(S_dbLib_badLink); verify = pform->verify; switch(pform->linkType) { - case FORM_CONSTANT: + case FORM_CONSTANT: **verify = 0; /*Initialize to no error*/ if(**value == '\0') break; dvalue = epicsStrtod(*value,&endp); @@ -3580,7 +3581,7 @@ long epicsShareAPI dbPutForm(DBENTRY *pdbentry,char **value) **verify = 0; break; case FORM_VXI_IO: - plink->value.vxiio.flag = + plink->value.vxiio.flag = ((strchr(*value,'Y')||strchr(*value,'y') ? VXIDYNAMIC : VXISTATIC)); value++; verify++; lvalue = strtol(*value,&endp,0); @@ -3619,7 +3620,7 @@ long epicsShareAPI dbPutForm(DBENTRY *pdbentry,char **value) } return(status); } - + char ** epicsShareAPI dbVerifyForm(DBENTRY *pdbentry,char **value) { struct form *pform = pdbentry->formpvt; @@ -3664,7 +3665,7 @@ char * epicsShareAPI dbGetRelatedField(DBENTRY *psave) dbFinishEntry(pdbentry); return(rtnval); } - + int epicsShareAPI dbGetNLinks(DBENTRY *pdbentry) { dbRecordType *precordType = pdbentry->precordType; @@ -3715,7 +3716,7 @@ int epicsShareAPI dbGetLinkType(DBENTRY *pdbentry) } return(-1); } - + long epicsShareAPI dbCvtLinkToConstant(DBENTRY *pdbentry) { dbFldDes *pflddes; @@ -3779,7 +3780,7 @@ long epicsShareAPI dbCvtLinkToPvlink(DBENTRY *pdbentry) } return(S_dbLib_badLink); } - + void epicsShareAPI dbDumpPath(DBBASE *pdbbase) { ELLLIST *ppathList; @@ -3821,7 +3822,7 @@ void epicsShareAPI dbDumpMenu(dbBase *pdbbase,const char *menuName) } dbWriteMenuFP(pdbbase,stdout,menuName); } - + void epicsShareAPI dbDumpRecordType(DBBASE *pdbbase,const char *recordTypeName) { dbRecordType *pdbRecordType; @@ -3863,7 +3864,7 @@ void epicsShareAPI dbDumpRecordType(DBBASE *pdbbase,const char *recordTypeName) if(recordTypeName) break; } } - + void epicsShareAPI dbDumpField( DBBASE *pdbbase,const char *recordTypeName,const char *fname) { @@ -3955,7 +3956,7 @@ void epicsShareAPI dbDumpField( if(recordTypeName) break; } } - + void epicsShareAPI dbDumpDevice(DBBASE *pdbbase,const char *recordTypeName) { dbRecordType *pdbRecordType; @@ -4051,7 +4052,7 @@ void epicsShareAPI dbDumpBreaktable(DBBASE *pdbbase,const char *name) } return; } - + static char *bus[VXI_IO+1] = {"","","VME","CAMAC","AB", "GPIB","BITBUS","","","","","","INST","BBGPIB","VXI"}; void epicsShareAPI dbReportDeviceConfig(dbBase *pdbbase,FILE *report) From 4324ffd27465cb24c67cbfdf9266169e090b710d Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:40 -0400 Subject: [PATCH 08/81] Various improvements: * A filter's parse routines now return a parse_result (enum) * Return value from test filter parse routines is configurable * Either call parse_end() or parse_abort(), but never both. --- src/ioc/db/dbChannel.c | 196 +++++++++++++++++--------------- src/ioc/db/dbChannel.h | 46 +++++--- src/ioc/db/test/dbChannelTest.c | 98 +++++++++------- 3 files changed, 195 insertions(+), 145 deletions(-) diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index 313ecea72..eabf71350 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -21,7 +21,6 @@ #include "special.h" #include "yajl_parse.h" - typedef struct parseContext { dbChannel *chan; chFilter *filter; @@ -34,31 +33,33 @@ typedef struct filterPlugin { const chFilterIf *fif; } filterPlugin; +#define CALLIF(rtn) !rtn ? parse_stop : rtn -#define CALLIF(rtn) rtn && rtn - -static void chf_value(parseContext *parser, int result) +static void chf_value(parseContext *parser, parse_result *presult) { chFilter *filter = parser->filter; - if (!result || parser->depth > 0) return; + if (*presult == parse_stop || parser->depth > 0) + return; parser->filter = NULL; - if (filter->fif->parse_end(filter)) { + if (filter->fif->parse_end(filter) == parse_continue) { ellAdd(&parser->chan->filters, &filter->node); + } else { + free(filter); // FIXME: Use free-list + *presult = parse_stop; } } - static int chf_null(void * ctx) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; - int result; + parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_null) (filter ); - chf_value(parser, result); + result = CALLIF(filter->fif->parse_null)(filter ); + chf_value(parser, &result); return result; } @@ -66,11 +67,11 @@ static int chf_boolean(void * ctx, int boolVal) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; - int result; + parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_boolean) (filter , boolVal); - chf_value(parser, result); + result = CALLIF(filter->fif->parse_boolean)(filter , boolVal); + chf_value(parser, &result); return result; } @@ -78,11 +79,11 @@ static int chf_integer(void * ctx, long integerVal) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; - int result; + parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_integer) (filter , integerVal); - chf_value(parser, result); + result = CALLIF(filter->fif->parse_integer)(filter , integerVal); + chf_value(parser, &result); return result; } @@ -90,11 +91,11 @@ static int chf_double(void * ctx, double doubleVal) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; - int result; + parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_double) (filter , doubleVal); - chf_value(parser, result); + result = CALLIF(filter->fif->parse_double)(filter , doubleVal); + chf_value(parser, &result); return result; } @@ -103,11 +104,11 @@ static int chf_string(void * ctx, const unsigned char * stringVal, { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; - int result; + parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_string) (filter , (const char *) stringVal, stringLen); - chf_value(parser, result); + result = CALLIF(filter->fif->parse_string)(filter , (const char *) stringVal, stringLen); + chf_value(parser, &result); return result; } @@ -118,11 +119,11 @@ static int chf_start_map(void * ctx) if (!filter) { assert(parser->depth == 0); - return 1; /* Opening '{' */ + return parse_continue; /* Opening '{' */ } ++parser->depth; - return CALLIF(filter->fif->parse_start_map) (filter ); + return CALLIF(filter->fif->parse_start_map)(filter ); } static int chf_map_key(void * ctx, const unsigned char * key, @@ -131,10 +132,11 @@ static int chf_map_key(void * ctx, const unsigned char * key, parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; const chFilterIf *fif; - int result; + parse_result result; if (filter) { - return CALLIF(filter->fif->parse_map_key) (filter , (const char *) key, stringLen); + assert(parser->depth > 0); + return CALLIF(filter->fif->parse_map_key)(filter , (const char *) key, stringLen); } assert(parser->depth == 0); @@ -144,6 +146,7 @@ static int chf_map_key(void * ctx, const unsigned char * key, return parse_stop; } + /* FIXME: Use a free-list */ filter = (chFilter *) callocMustSucceed(1, sizeof(*filter), "Creating dbChannel filter"); filter->chan = parser->chan; filter->fif = fif; @@ -153,7 +156,7 @@ static int chf_map_key(void * ctx, const unsigned char * key, if (result == parse_continue) { parser->filter = filter; } else { - free(filter); + free(filter); // FIXME: Use free-list } return result; } @@ -162,15 +165,19 @@ static int chf_end_map(void * ctx) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; + parse_result result; - if (filter) { - int result = CALLIF(filter->fif->parse_end_map) (filter ); - - if (--parser->depth == 0) chf_value(parser, result); - return result; + if (!filter) { + assert(parser->depth == 0); + return parse_continue; /* Final closing '}' */ } - assert(parser->depth == 0); - return parse_continue; /* Final closing '}' */ + + assert(parser->depth > 0); + result = CALLIF(filter->fif->parse_end_map)(filter ); + + --parser->depth; + chf_value(parser, &result); + return result; } static int chf_start_array(void * ctx) @@ -180,18 +187,19 @@ static int chf_start_array(void * ctx) assert(filter); ++parser->depth; - return CALLIF(filter->fif->parse_start_array) (filter ); + return CALLIF(filter->fif->parse_start_array)(filter ); } static int chf_end_array(void * ctx) { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; - int result; + parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_end_array) (filter ); - if (--parser->depth == 0) chf_value(parser, result); + result = CALLIF(filter->fif->parse_end_array)(filter ); + --parser->depth; + chf_value(parser, &result); return result; } @@ -202,23 +210,23 @@ static const yajl_callbacks chf_callbacks = static const yajl_parser_config chf_config = { 0, 1 }; /* allowComments = NO , checkUTF8 = YES */ - static long chf_parse(dbChannel *chan, const char *json) { parseContext parser = - { chan, NULL, 0}; + { chan, NULL, 0 }; yajl_handle yh = yajl_alloc(&chf_callbacks, &chf_config, NULL, &parser); size_t jlen = strlen(json); yajl_status ys; long status; - if (!yh) return S_db_noMemory; + if (!yh) + return S_db_noMemory; ys = yajl_parse(yh, (const unsigned char *) json, jlen); if (ys == yajl_status_insufficient_data) ys = yajl_parse_complete(yh); - switch(ys) { + switch (ys) { case yajl_status_ok: status = 0; break; @@ -244,24 +252,24 @@ static long chf_parse(dbChannel *chan, const char *json) } /* 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_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 -}; +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_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 }; long dbChannelFind(dbChannel *chan, const char *pname) { @@ -288,25 +296,28 @@ long dbChannelFind(dbChannel *chan, const char *pname) dbInitEntry(pdbbase, &dbEntry); status = dbFindRecordPart(&dbEntry, &pname); - if (status) goto finish; + if (status) + goto finish; - if (*pname == '.') ++pname; + if (*pname == '.') + ++pname; status = dbFindFieldPart(&dbEntry, &pname); if (status == S_dbLib_fieldNotFound) status = dbGetAttributePart(&dbEntry, &pname); - if (status) goto finish; + if (status) + goto finish; - paddr = &chan->addr; + paddr = &chan->addr; pflddes = dbEntry.pflddes; dbfType = pflddes->field_type; - paddr->precord = dbEntry.precnode->precord; - paddr->pfield = dbEntry.pfield; - paddr->pfldDes = pflddes; - paddr->no_elements = 1; - paddr->field_type = dbfType; - paddr->field_size = pflddes->size; - paddr->special = pflddes->special; + paddr->precord = dbEntry.precnode->precord; + paddr->pfield = dbEntry.pfield; + paddr->pfldDes = pflddes; + paddr->no_elements = 1; + paddr->field_type = dbfType; + paddr->field_size = pflddes->size; + paddr->special = pflddes->special; paddr->dbr_field_type = mapDBFToDBR[dbfType]; /* Handle field modifiers */ @@ -315,14 +326,14 @@ long dbChannelFind(dbChannel *chan, const char *pname) case '$': /* Some field types can be accessed as char arrays */ if (dbfType == DBF_STRING) { - paddr->no_elements = pflddes->size; - paddr->field_type = DBF_CHAR; - paddr->field_size = 1; + paddr->no_elements = pflddes->size; + paddr->field_type = DBF_CHAR; + paddr->field_size = 1; paddr->dbr_field_type = DBR_CHAR; } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) { /* Clients see a char array, but keep original dbfType */ - paddr->no_elements = PVNAME_STRINGSZ + 12; - paddr->field_size = 1; + paddr->no_elements = PVNAME_STRINGSZ + 12; + paddr->field_size = 1; paddr->dbr_field_type = DBR_CHAR; } else { status = S_dbLib_fieldNotFound; @@ -339,8 +350,7 @@ long dbChannelFind(dbChannel *chan, const char *pname) pname++; } -finish: - dbFinishEntry(&dbEntry); + finish: dbFinishEntry(&dbEntry); return status; } @@ -350,7 +360,8 @@ long dbChannelOpen(dbChannel *chan) chFilter *filter; long status = 0; - if (chan->magic != DBCHANNEL_MAGIC) return S_db_notInit; + if (chan->magic != DBCHANNEL_MAGIC) + return S_db_notInit; paddr = &chan->addr; if (paddr->special == SPC_DBADDR) { @@ -359,14 +370,16 @@ long dbChannelOpen(dbChannel *chan) /* Let record type modify the dbAddr */ if (prset && prset->cvt_dbaddr) { status = prset->cvt_dbaddr(paddr); - if (status) return status; + if (status) + return status; } } filter = (chFilter *) ellFirst(&chan->filters); while (filter) { status = filter->fif->channel_open(filter); - if (status) break; + if (status) + break; filter = (chFilter *) ellNext(&filter->node); } return status; @@ -376,7 +389,8 @@ void dbChannelReport(dbChannel *chan, int level) { chFilter *filter; - if (chan->magic != DBCHANNEL_MAGIC) return; + if (chan->magic != DBCHANNEL_MAGIC) + return; filter = (chFilter *) ellFirst(&chan->filters); while (filter) { @@ -389,7 +403,8 @@ long dbChannelClose(dbChannel *chan) { chFilter *filter; - if (chan->magic != DBCHANNEL_MAGIC) return S_db_notInit; + if (chan->magic != DBCHANNEL_MAGIC) + return S_db_notInit; while ((filter = (chFilter *) ellGet(&chan->filters))) { filter->fif->channel_close(filter); @@ -412,16 +427,17 @@ void dbRegisterFilter(const char *name, const chFilterIf *fif) } pgph = gphFind(pdbbase->pgpHash, name, &pdbbase->filterList); - if (pgph) return; + if (pgph) + return; pfilt = dbCalloc(1, sizeof(filterPlugin)); pfilt->name = strdup(name); - pfilt->fif = fif; + pfilt->fif = fif; ellAdd(&pdbbase->filterList, &pfilt->node); pgph = gphAdd(pdbbase->pgpHash, pfilt->name, &pdbbase->filterList); if (!pgph) { - free((void *)pfilt->name); + free((void *) pfilt->name); free(pfilt); printf("dbRegisterFilter: gphAdd failed\n"); return; @@ -431,10 +447,12 @@ void dbRegisterFilter(const char *name, const chFilterIf *fif) const chFilterIf * dbFindFilter(const char *name, size_t len) { - GPHENTRY *pgph = gphFindParse(pdbbase->pgpHash, name, len, &pdbbase->filterList); + GPHENTRY *pgph = gphFindParse(pdbbase->pgpHash, name, len, + &pdbbase->filterList); filterPlugin *pfilt; - if (!pgph) return NULL; - pfilt = (filterPlugin *)pgph->userPvt; + if (!pgph) + return NULL; + pfilt = (filterPlugin *) pgph->userPvt; return pfilt->fif; } diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index aa43325b0..902245fe6 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -23,7 +23,6 @@ #define S_db_notInit (M_dbAccess|21) /*dbChannel not initialized*/ - /* A dbChannel points to a record field, and can have multiple filters */ typedef struct dbChannel { epicsUInt32 magic; @@ -34,31 +33,44 @@ typedef struct dbChannel { typedef struct chFilter chFilter; /* Return values from chFilterIf->parse_* routines: */ -enum {parse_stop, parse_continue}; +typedef enum { + parse_stop, parse_continue +} parse_result; /* These routines must be implemented by each filter plug-in */ typedef struct chFilterIf { - /* Parsing event handlers */ - int (* parse_start)(chFilter *filter); + /* Parsing event handlers: */ + parse_result (* parse_start)(chFilter *filter); + /* If parse_start() returns parse_continue for a filter, one of + * parse_abort() or parse_end() will later be called for that same + * filter. + */ void (* parse_abort)(chFilter *filter); - int (* parse_end)(chFilter *filter); + /* If parse_abort() is called it should release any memory allocated + * for this filter; no further parse_...() calls will be made; + */ + parse_result (* parse_end)(chFilter *filter); + /* If parse_end() returns parse_stop it should have released any + * memory allocated for this filter; no further parse_...() calls will + * be made in this case. + */ - int (* parse_null)(chFilter *filter); - int (* parse_boolean)(chFilter *filter, int boolVal); - int (* parse_integer)(chFilter *filter, long integerVal); - int (* parse_double)(chFilter *filter, double doubleVal); - int (* parse_string)(chFilter *filter, const char *stringVal, + parse_result (* parse_null)(chFilter *filter); + parse_result (* parse_boolean)(chFilter *filter, int boolVal); + parse_result (* parse_integer)(chFilter *filter, long integerVal); + parse_result (* parse_double)(chFilter *filter, double doubleVal); + parse_result (* parse_string)(chFilter *filter, const char *stringVal, size_t stringLen); /* NB: stringVal is not zero-terminated: */ - int (* parse_start_map)(chFilter *filter); - int (* parse_map_key)(chFilter *filter, const char *key, - size_t stringLen); - int (* parse_end_map)(chFilter *filter); + parse_result (* parse_start_map)(chFilter *filter); + parse_result (* parse_map_key)(chFilter *filter, const char *key, + size_t stringLen); /* NB: key is not zero-terminated: */ + parse_result (* parse_end_map)(chFilter *filter); - int (* parse_start_array)(chFilter *filter); - int (* parse_end_array)(chFilter *filter); + parse_result (* parse_start_array)(chFilter *filter); + parse_result (* parse_end_array)(chFilter *filter); - /* Channel operations */ + /* Channel operations: */ long (* channel_open)(chFilter *filter); void (* channel_report)(chFilter *filter, int level); /* FIXME: More routines here ... */ diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index 221a6b3d5..06b979ede 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -29,76 +29,84 @@ #define e_report 0x00004000 #define e_close 0x00008000 -unsigned int e; +#define r_any (e_start | e_abort | e_end | \ + e_null | e_boolean | e_integer | e_double | e_string | \ + e_start_map | e_map_key | e_end_map | e_start_array | e_end_array) +#define r_scalar (e_start | e_abort | e_end | \ + e_null | e_boolean | e_integer | e_double | e_string) -int p_start(chFilter *filter) +unsigned int e, r; +#define p_ret(x) return r & x ? parse_continue : parse_stop + +parse_result p_start(chFilter *filter) { testOk(e & e_start, "parse_start called"); - return parse_continue; + p_ret(e_start); } void p_abort(chFilter *filter) { testOk(e & e_abort, "parse_abort called"); } -int p_end(chFilter *filter) + +parse_result p_end(chFilter *filter) { testOk(e & e_end, "parse_end called"); - return parse_continue; + p_ret(e_end); } -int p_null(chFilter *filter) +parse_result p_null(chFilter *filter) { testOk(e & e_null, "parse_null called"); - return parse_continue; + p_ret(e_null); } -int p_boolean(chFilter *filter, int boolVal) +parse_result p_boolean(chFilter *filter, int boolVal) { testOk(e & e_boolean, "parse_boolean called, val = %d", boolVal); - return parse_continue; + p_ret(e_boolean); } -int p_integer(chFilter *filter, long integerVal) +parse_result p_integer(chFilter *filter, long integerVal) { testOk(e & e_integer, "parse_integer called, val = %ld", integerVal); - return parse_continue; + p_ret(e_integer); } -int p_double(chFilter *filter, double doubleVal) +parse_result p_double(chFilter *filter, double doubleVal) { testOk(e & e_double, "parse_double called, val = %g", doubleVal); - return parse_continue; + p_ret(e_double); } -int p_string(chFilter *filter, const char *stringVal, size_t stringLen) +parse_result p_string(chFilter *filter, const char *stringVal, size_t stringLen) { testOk(e & e_string, "parse_string called, val = '%.*s'", stringLen, stringVal); - return parse_continue; + p_ret(e_string); } -int p_start_map(chFilter *filter) +parse_result p_start_map(chFilter *filter) { testOk(e & e_start_map, "parse_start_map called"); - return parse_continue; + p_ret(e_start_map); } -int p_map_key(chFilter *filter, const char *key, size_t stringLen) +parse_result p_map_key(chFilter *filter, const char *key, size_t stringLen) { testOk(e & e_map_key, "parse_map_key called, key = '%.*s'", stringLen, key); - return parse_continue; + p_ret(e_map_key); } -int p_end_map(chFilter *filter) +parse_result p_end_map(chFilter *filter) { testOk(e & e_end_map, "parse_end_map called"); - return parse_continue; + p_ret(e_end_map); } -int p_start_array(chFilter *filter) +parse_result p_start_array(chFilter *filter) { testOk(e & e_start_array, "parse_start_array called"); - return parse_continue; + p_ret(e_start_array); } -int p_end_array(chFilter *filter) +parse_result p_end_array(chFilter *filter) { testOk(e & e_end_array, "parse_end_array called"); - return parse_continue; + p_ret(e_end_array); } long c_open(chFilter *filter) @@ -115,39 +123,49 @@ void c_close(chFilter *filter) testOk(e & e_close, "channel_close called"); } -chFilterIf anyIf = +chFilterIf testIf = { p_start, p_abort, p_end, p_null, p_boolean, p_integer, p_double, p_string, p_start_map, p_map_key, p_end_map, p_start_array, p_end_array, c_open, c_report, c_close }; -chFilterIf scalarIf = - { p_start, p_abort, p_end, p_null, p_boolean, p_integer, p_double, - p_string, NULL, NULL, NULL, NULL, NULL, c_open, - c_report, c_close}; - MAIN(dbChannelTest) { dbChannel ch; - testPlan(45); + testPlan(59); testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.dbx", ".:..", NULL)); testOk(!!pdbbase, "pdbbase was set"); - e = 0; + r = e = 0; testOk1(!dbChannelFind(&ch, "x.NAME$")); - testOk1(ch.addr.no_elements > 1); + testOk1(ch.addr.no_elements> 1); testOk1(!dbChannelFind(&ch, "x.{}")); - dbRegisterFilter("any", &anyIf); + testOk1(dbChannelFind(&ch, "y")); + testOk1(dbChannelFind(&ch, "x.{\"none\":null}")); + dbRegisterFilter("any", &testIf); + + e = e_start; + testOk1(dbChannelFind(&ch, "x.{\"any\":null}")); + + r = e_start; + e = e_start | e_null | e_abort; + testOk1(dbChannelFind(&ch, "x.{\"any\":null}")); + + r = e_start | e_null; + e = e_start | e_null | e_end; + testOk1(dbChannelFind(&ch, "x.{\"any\":null}")); + + r = r_any; e = e_start | e_null | e_end; testOk1(!dbChannelFind(&ch, "x.{\"any\":null}")); e = e_close; testOk1(!dbChannelClose(&ch)); - dbRegisterFilter("scalar", &scalarIf); + dbRegisterFilter("scalar", &testIf); e = e_start | e_null | e_end; testOk1(!dbChannelFind(&ch, "x.{\"scalar\":null}")); @@ -158,7 +176,8 @@ MAIN(dbChannelTest) e = e_close; testOk1(!dbChannelClose(&ch)); - e = e_start | e_start_array | e_boolean | e_integer | e_end_array | e_end; + e = e_start | e_start_array | e_boolean | e_integer | e_end_array + | e_end; testOk1(!dbChannelFind(&ch, "x.{\"any\":[true,1]}")); e = e_close; testOk1(!dbChannelClose(&ch)); @@ -167,12 +186,13 @@ MAIN(dbChannelTest) | e_end; testOk1(!dbChannelFind(&ch, "x.{\"any\":{\"a\":2.7183,\"b\":\"c\"}}")); - e = e_close | e_start | e_abort; + r = r_scalar; + e = e_close | e_start | e_start_array | e_abort; testOk1(dbChannelFind(&ch, "x.{\"scalar\":[null]}")); e = 0; testOk1(!dbChannelClose(&ch)); - e = e_start | e_abort; + e = e_start | e_start_map | e_abort; testOk1(dbChannelFind(&ch, "x.{\"scalar\":{}}")); e = 0; testOk1(!dbChannelClose(&ch)); From f4d55f12492eb6b8c2422a5a3bcf66fd59bc83ce Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:40 -0400 Subject: [PATCH 09/81] Changed dbChannel management API to Test/Create/Delete --- src/ioc/db/dbChannel.c | 138 +++++++++++++++++++++++++------- src/ioc/db/dbChannel.h | 6 +- src/ioc/db/test/dbChannelTest.c | 63 +++++++++------ 3 files changed, 152 insertions(+), 55 deletions(-) diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index eabf71350..f9a995a6a 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -210,6 +210,27 @@ static const yajl_callbacks chf_callbacks = static const yajl_parser_config chf_config = { 0, 1 }; /* allowComments = NO , checkUTF8 = YES */ +static long json_validate(const char *json) +{ + yajl_handle yh = yajl_alloc(NULL, &chf_config, NULL, NULL); + size_t jlen = strlen(json); + yajl_status ys; + + if (!yh) + return S_db_noMemory; + + ys = yajl_parse(yh, (const unsigned char *) json, jlen); + if (ys == yajl_status_insufficient_data) + ys = yajl_parse_complete(yh); + + yajl_free(yh); + + if (ys == yajl_status_ok) + return 0; + + return S_db_notFound; +} + static long chf_parse(dbChannel *chan, const char *json) { parseContext parser = @@ -251,6 +272,68 @@ static long chf_parse(dbChannel *chan, const char *json) return status; } +static long pvNameLookup(DBENTRY *pdbe, const char **ppname) +{ + long status; + + dbInitEntry(pdbbase, pdbe); + + status = dbFindRecordPart(pdbe, ppname); + if (status) + return status; + + if (**ppname == '.') + ++*ppname; + + status = dbFindFieldPart(pdbe, ppname); + if (status == S_dbLib_fieldNotFound) + status = dbGetAttributePart(pdbe, ppname); + + return status; +} + +long dbChannelTest(const char *pname) +{ + DBENTRY dbEntry; + long status; + + if (!pname || !*pname || !pdbbase) + return S_db_notFound; + + status = pvNameLookup(&dbEntry, &pname); + if (status) + goto finish; + + /* Test field modifiers */ + while (*pname) { + switch (*pname) { + case '$': + switch (dbEntry.pflddes->field_type) { + case DBF_STRING: + case DBF_INLINK: + case DBF_OUTLINK: + case DBF_FWDLINK: + break; + default: + status = S_dbLib_fieldNotFound; + goto finish; + } + break; + case '{': + status = json_validate(pname); + goto finish; + default: + status = S_dbLib_fieldNotFound; + goto finish; + } + pname++; + } + + finish: + dbFinishEntry(&dbEntry); + return status; +} + /* Stolen from dbAccess.c: */ static short mapDBFToDBR[DBF_NTYPES] = { @@ -271,41 +354,29 @@ static short mapDBFToDBR[DBF_NTYPES] = /* DBF_FWDLINK => */DBR_STRING, /* DBF_NOACCESS => */DBR_NOACCESS }; -long dbChannelFind(dbChannel *chan, const char *pname) +dbChannel * dbChannelCreate(const char *name) { - DBADDR *paddr; DBENTRY dbEntry; + dbChannel *chan = NULL; + DBADDR *paddr; dbFldDes *pflddes; + const char *pname; long status; short dbfType; - if (!chan || !pname || !*pname || !pdbbase) - return S_db_notFound; + if (!name || !*name || !pdbbase) + return NULL; + pname = name; - if (chan->magic == DBCHANNEL_MAGIC) { - chFilter *filter; - - while ((filter = (chFilter *) ellGet(&chan->filters))) { - filter->fif->channel_close(filter); - free(filter); - } - } else { - ellInit(&chan->filters); - chan->magic = DBCHANNEL_MAGIC; - } - - dbInitEntry(pdbbase, &dbEntry); - status = dbFindRecordPart(&dbEntry, &pname); + status = pvNameLookup(&dbEntry, &pname); if (status) goto finish; - if (*pname == '.') - ++pname; - status = dbFindFieldPart(&dbEntry, &pname); - if (status == S_dbLib_fieldNotFound) - status = dbGetAttributePart(&dbEntry, &pname); - if (status) - goto finish; + // FIXME: Use free-list + chan = (dbChannel *) callocMustSucceed(1, sizeof(*chan), "dbChannelCreate"); + chan->magic = DBCHANNEL_MAGIC; + chan->name = strdup(name); // FIXME? + ellInit(&chan->filters); paddr = &chan->addr; pflddes = dbEntry.pflddes; @@ -350,8 +421,13 @@ long dbChannelFind(dbChannel *chan, const char *pname) pname++; } - finish: dbFinishEntry(&dbEntry); - return status; +finish: + dbFinishEntry(&dbEntry); + if (status && chan) { + dbChannelDelete(chan); + chan = NULL; + } + return chan; } long dbChannelOpen(dbChannel *chan) @@ -392,6 +468,9 @@ void dbChannelReport(dbChannel *chan, int level) if (chan->magic != DBCHANNEL_MAGIC) return; + printf("Channel '%s': %d filter(s)\n", chan->name, + ellCount(&chan->filters)); + filter = (chFilter *) ellFirst(&chan->filters); while (filter) { filter->fif->channel_report(filter, level); @@ -399,7 +478,7 @@ void dbChannelReport(dbChannel *chan, int level) } } -long dbChannelClose(dbChannel *chan) +long dbChannelDelete(dbChannel *chan) { chFilter *filter; @@ -410,7 +489,10 @@ long dbChannelClose(dbChannel *chan) filter->fif->channel_close(filter); free(filter); } + free(chan->name); // FIXME? chan->magic = 0; + free(chan); // FIXME: Use free-list + return 0; } diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index 902245fe6..740165b17 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -26,6 +26,7 @@ /* A dbChannel points to a record field, and can have multiple filters */ typedef struct dbChannel { epicsUInt32 magic; + const char *name; dbAddr addr; ELLLIST filters; } dbChannel; @@ -85,10 +86,11 @@ struct chFilter { void *puser; }; -epicsShareFunc long dbChannelFind(dbChannel *chan, const char *pname); +epicsShareFunc long dbChannelTest(const char *pname); +epicsShareFunc dbChannel * dbChannelCreate(const char *pname); epicsShareFunc long dbChannelOpen(dbChannel *chan); epicsShareFunc void dbChannelReport(dbChannel *chan, int level); -epicsShareFunc long dbChannelClose(dbChannel *chan); +epicsShareFunc long dbChannelDelete(dbChannel *chan); epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif); epicsShareFunc const chFilterIf * dbFindFilter(const char *key, size_t len); diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index 06b979ede..9a8245f55 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -130,72 +130,85 @@ chFilterIf testIf = MAIN(dbChannelTest) { - dbChannel ch; + dbChannel *pch; - testPlan(59); + testPlan(64); testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.dbx", ".:..", NULL)); testOk(!!pdbbase, "pdbbase was set"); r = e = 0; - testOk1(!dbChannelFind(&ch, "x.NAME$")); - testOk1(ch.addr.no_elements> 1); + /* Regular record and field names */ + testOk1(!dbChannelTest("x")); + testOk1(!dbChannelTest("x.NAME")); - testOk1(!dbChannelFind(&ch, "x.{}")); + /* Long string field modifier */ + testOk1(!dbChannelTest("x.NAME$")); + testOk1(!!(pch = dbChannelCreate("x.NAME$"))); + testOk1(pch->addr.no_elements > 1); + testOk1(!dbChannelDelete(pch)); - testOk1(dbChannelFind(&ch, "y")); - testOk1(dbChannelFind(&ch, "x.{\"none\":null}")); + /* JSON field modifier validation */ + testOk1(!dbChannelTest("x.{\"json\":true}")); + + /* Ensure bad PVs get rejected */ + testOk(dbChannelTest("y"), "Test nonexistent record"); + testOk(dbChannelCreate("y") == NULL, "Create nonexistent record"); + + testOk1(dbChannelTest("x.{not-json}")); + testOk1(!dbChannelCreate("x.{\"none\":null}")); dbRegisterFilter("any", &testIf); + /* Parser event rejection by filter */ e = e_start; - testOk1(dbChannelFind(&ch, "x.{\"any\":null}")); + testOk1(!dbChannelCreate("x.{\"any\":null}")); r = e_start; e = e_start | e_null | e_abort; - testOk1(dbChannelFind(&ch, "x.{\"any\":null}")); + testOk1(!dbChannelCreate("x.{\"any\":null}")); r = e_start | e_null; e = e_start | e_null | e_end; - testOk1(dbChannelFind(&ch, "x.{\"any\":null}")); + testOk1(!dbChannelCreate("x.{\"any\":null}")); + /* Successful parsing... */ r = r_any; e = e_start | e_null | e_end; - testOk1(!dbChannelFind(&ch, "x.{\"any\":null}")); + testOk1(!!(pch = dbChannelCreate("x.{\"any\":null}"))); e = e_close; - testOk1(!dbChannelClose(&ch)); + testOk1(!dbChannelDelete(pch)); dbRegisterFilter("scalar", &testIf); e = e_start | e_null | e_end; - testOk1(!dbChannelFind(&ch, "x.{\"scalar\":null}")); + testOk1(!!(pch = dbChannelCreate("x.{\"scalar\":null}"))); e = e_report; - dbChannelReport(&ch, 0); + dbChannelReport(pch, 0); e = e_close; - testOk1(!dbChannelClose(&ch)); + testOk1(!dbChannelDelete(pch)); e = e_start | e_start_array | e_boolean | e_integer | e_end_array | e_end; - testOk1(!dbChannelFind(&ch, "x.{\"any\":[true,1]}")); + testOk1(!!(pch = dbChannelCreate("x.{\"any\":[true,1]}"))); e = e_close; - testOk1(!dbChannelClose(&ch)); + testOk1(!dbChannelDelete(pch)); e = e_start | e_start_map | e_map_key | e_double | e_string | e_end_map | e_end; - testOk1(!dbChannelFind(&ch, "x.{\"any\":{\"a\":2.7183,\"b\":\"c\"}}")); + testOk1(!!(pch = dbChannelCreate("x.{\"any\":{\"a\":2.7183,\"b\":\"c\"}}"))); + e = e_close; + testOk1(!dbChannelDelete(pch)); + /* More event rejection */ r = r_scalar; - e = e_close | e_start | e_start_array | e_abort; - testOk1(dbChannelFind(&ch, "x.{\"scalar\":[null]}")); - e = 0; - testOk1(!dbChannelClose(&ch)); + e = e_start | e_start_array | e_abort; + testOk1(!dbChannelCreate("x.{\"scalar\":[null]}")); e = e_start | e_start_map | e_abort; - testOk1(dbChannelFind(&ch, "x.{\"scalar\":{}}")); - e = 0; - testOk1(!dbChannelClose(&ch)); + testOk1(!dbChannelCreate("x.{\"scalar\":{}}")); dbFreeBase(pdbbase); From 56468b684e70ec0d6a2b895d42527a0d2551e0e2 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:41 -0400 Subject: [PATCH 10/81] Extended functionality of dbChannel code: * Deleted the chan->magic member and associated checks. * Moved rset->cvt_dbaddr() call into dbChannelCreate() * Added many accessor routines * Renamed dbChannelReport() to dbChannelShow() and added dbChannelFilterShow() --- src/ioc/db/dbChannel.c | 140 ++++++++++++++++++++++---------- src/ioc/db/dbChannel.h | 32 +++++--- src/ioc/db/test/dbChannelTest.c | 2 +- 3 files changed, 119 insertions(+), 55 deletions(-) diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index f9a995a6a..97fe9ec91 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -10,6 +10,7 @@ #include "cantProceed.h" #include "dbChannel.h" +#include "dbCommon.h" #include "dbBase.h" #include "link.h" #include "dbAccessDefs.h" @@ -292,21 +293,21 @@ static long pvNameLookup(DBENTRY *pdbe, const char **ppname) return status; } -long dbChannelTest(const char *pname) +long dbChannelTest(const char *name) { DBENTRY dbEntry; long status; - if (!pname || !*pname || !pdbbase) + if (!name || !*name || !pdbbase) return S_db_notFound; - status = pvNameLookup(&dbEntry, &pname); + status = pvNameLookup(&dbEntry, &name); if (status) goto finish; /* Test field modifiers */ - while (*pname) { - switch (*pname) { + while (*name) { + switch (*name) { case '$': switch (dbEntry.pflddes->field_type) { case DBF_STRING: @@ -320,13 +321,13 @@ long dbChannelTest(const char *pname) } break; case '{': - status = json_validate(pname); + status = json_validate(name); goto finish; default: status = S_dbLib_fieldNotFound; goto finish; } - pname++; + name++; } finish: @@ -356,17 +357,16 @@ static short mapDBFToDBR[DBF_NTYPES] = dbChannel * dbChannelCreate(const char *name) { + const char *pname = name; DBENTRY dbEntry; dbChannel *chan = NULL; DBADDR *paddr; dbFldDes *pflddes; - const char *pname; long status; short dbfType; if (!name || !*name || !pdbbase) return NULL; - pname = name; status = pvNameLookup(&dbEntry, &pname); if (status) @@ -374,7 +374,6 @@ dbChannel * dbChannelCreate(const char *name) // FIXME: Use free-list chan = (dbChannel *) callocMustSucceed(1, sizeof(*chan), "dbChannelCreate"); - chan->magic = DBCHANNEL_MAGIC; chan->name = strdup(name); // FIXME? ellInit(&chan->filters); @@ -421,36 +420,30 @@ dbChannel * dbChannelCreate(const char *name) pname++; } -finish: - dbFinishEntry(&dbEntry); - if (status && chan) { - dbChannelDelete(chan); - chan = NULL; - } - return chan; -} - -long dbChannelOpen(dbChannel *chan) -{ - DBADDR *paddr; - chFilter *filter; - long status = 0; - - if (chan->magic != DBCHANNEL_MAGIC) - return S_db_notInit; - - paddr = &chan->addr; if (paddr->special == SPC_DBADDR) { struct rset *prset = dbGetRset(paddr); /* Let record type modify the dbAddr */ if (prset && prset->cvt_dbaddr) { status = prset->cvt_dbaddr(paddr); - if (status) - return status; + if (status) goto finish; } } +finish: + if (status && chan) { + dbChannelDelete(chan); + chan = NULL; + } + dbFinishEntry(&dbEntry); + return chan; +} + +long dbChannelOpen(dbChannel *chan) +{ + chFilter *filter; + long status = 0; + filter = (chFilter *) ellFirst(&chan->filters); while (filter) { status = filter->fif->channel_open(filter); @@ -461,19 +454,81 @@ long dbChannelOpen(dbChannel *chan) return status; } -void dbChannelReport(dbChannel *chan, int level) +const char * dbChannelName(dbChannel *chan) { - chFilter *filter; + return chan->name; +} - if (chan->magic != DBCHANNEL_MAGIC) - return; +struct dbCommon * dbChannelRecord(dbChannel *chan) +{ + return chan->addr.precord; +} - printf("Channel '%s': %d filter(s)\n", chan->name, - ellCount(&chan->filters)); +struct dbFldDes * dbChannelFldDes(dbChannel *chan) +{ + return chan->addr.pfldDes; +} - filter = (chFilter *) ellFirst(&chan->filters); +long dbChannelElements(dbChannel *chan) +{ + return chan->addr.no_elements; +} + +short dbChannelFieldType(dbChannel *chan) +{ + return chan->addr.field_type; +} + +short dbChannelExportType(dbChannel *chan) +{ + return chan->addr.dbr_field_type; +} + +short dbChannelElementSize(dbChannel *chan) +{ + return chan->addr.field_size; +} + +short dbChannelSpecial(dbChannel *chan) +{ + return chan->addr.special; +} + +void * dbChannelData(dbChannel *chan) +{ + void * pdata = chan->addr.pfield; + /* FIXME: offer to filters? */ + return pdata; +} + +long dbChannelPut(dbChannel *chan, short type, const void *pbuffer, long nRequest) +{ + /* FIXME: offer to filters? */ + return dbPut(&chan->addr, type, pbuffer, nRequest); +} + +long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer, long nRequest) +{ + /* FIXME: offer to filters? */ + return dbPutField(&chan->addr, type, pbuffer, nRequest); +} + +void dbChannelShow(dbChannel *chan, int level) +{ + printf(" dbChannel name: %s\n", chan->name); + /* FIXME: show field_type name */ + printf(" field_type %d, %ld element(s), %d filter(s)", + chan->addr.field_type, + chan->addr.no_elements, ellCount(&chan->filters)); + if (level > 0) + dbChannelFilterShow(chan, level - 1); +} + +void dbChannelFilterShow(dbChannel *chan, int level) +{ + chFilter *filter = (chFilter *) ellFirst(&chan->filters); while (filter) { - filter->fif->channel_report(filter, level); + filter->fif->channel_report(filter, level - 1); filter = (chFilter *) ellNext(&filter->node); } } @@ -482,20 +537,17 @@ long dbChannelDelete(dbChannel *chan) { chFilter *filter; - if (chan->magic != DBCHANNEL_MAGIC) - return S_db_notInit; - while ((filter = (chFilter *) ellGet(&chan->filters))) { filter->fif->channel_close(filter); free(filter); } - free(chan->name); // FIXME? - chan->magic = 0; + free((char *) chan->name); // FIXME: Use free-list free(chan); // FIXME: Use free-list return 0; } + /* FIXME: Do these belong in a different file? */ void dbRegisterFilter(const char *name, const chFilterIf *fif) diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index 740165b17..3c5d2e2b3 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -16,16 +16,11 @@ #include "shareLib.h" #ifdef __cplusplus -// extern "C" { +extern "C" { #endif -#define DBCHANNEL_MAGIC 0xdbc4a9e1 - -#define S_db_notInit (M_dbAccess|21) /*dbChannel not initialized*/ - /* A dbChannel points to a record field, and can have multiple filters */ typedef struct dbChannel { - epicsUInt32 magic; const char *name; dbAddr addr; ELLLIST filters; @@ -74,7 +69,7 @@ typedef struct chFilterIf { /* Channel operations: */ long (* channel_open)(chFilter *filter); void (* channel_report)(chFilter *filter, int level); - /* FIXME: More routines here ... */ + /* FIXME: More filter routines here ... */ void (* channel_close)(chFilter *filter); } chFilterIf; @@ -86,10 +81,27 @@ struct chFilter { void *puser; }; -epicsShareFunc long dbChannelTest(const char *pname); -epicsShareFunc dbChannel * dbChannelCreate(const char *pname); +struct dbCommon; +struct dbFldDes; + +epicsShareFunc long dbChannelTest(const char *name); +epicsShareFunc dbChannel * dbChannelCreate(const char *name); epicsShareFunc long dbChannelOpen(dbChannel *chan); -epicsShareFunc void dbChannelReport(dbChannel *chan, int level); +epicsShareFunc const char * dbChannelName(dbChannel *chan); +epicsShareFunc struct dbCommon * dbChannelRecord(dbChannel *chan); +epicsShareFunc struct dbFldDes * dbChannelFldDes(dbChannel *chan); +epicsShareFunc long dbChannelElements(dbChannel *chan); +epicsShareFunc short dbChannelFieldType(dbChannel *chan); +epicsShareFunc short dbChannelExportType(dbChannel *chan); +epicsShareFunc short dbChannelElementSize(dbChannel *chan); +epicsShareFunc short dbChannelSpecial(dbChannel *chan); +epicsShareFunc void * dbChannelData(dbChannel *chan); +epicsShareFunc long dbChannelPut(dbChannel *chan, short type, + const void *pbuffer, long nRequest); +epicsShareFunc long dbChannelPutField(dbChannel *chan, short type, + const void *pbuffer, long nRequest); +epicsShareFunc void dbChannelShow(dbChannel *chan, int level); +epicsShareFunc void dbChannelFilterShow(dbChannel *chan, int level); epicsShareFunc long dbChannelDelete(dbChannel *chan); epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif); diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index 9a8245f55..58db707c5 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -185,7 +185,7 @@ MAIN(dbChannelTest) testOk1(!!(pch = dbChannelCreate("x.{\"scalar\":null}"))); e = e_report; - dbChannelReport(pch, 0); + dbChannelShow(pch, 0); e = e_close; testOk1(!dbChannelDelete(pch)); From 8bf53d3c593c29e357ba98da3046548d6f57473f Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:21:41 -0400 Subject: [PATCH 11/81] Replaced dbAddr with dbChannel in all CA-server related code * Routines with a db_access interface are named dbChannel_xxx * Using dbch for member variable names in Jeff's code It seems to work! TODO: Make dbChannel use a freelist --- src/ioc/as/asDbLib.c | 34 +- src/ioc/as/asDbLib.h | 10 +- src/ioc/db/dbAccess.c | 112 +--- src/ioc/db/dbAddr.h | 3 - src/ioc/db/dbCAC.h | 68 +- src/ioc/db/dbChannelIO.cpp | 124 ++-- src/ioc/db/dbChannelIO.h | 63 +- src/ioc/db/dbContext.cpp | 149 +++-- src/ioc/db/dbContextReadNotifyCache.cpp | 38 +- src/ioc/db/dbEvent.c | 174 ++--- src/ioc/db/dbEvent.h | 16 +- src/ioc/db/dbNotify.c | 396 ++++++------ src/ioc/db/dbNotify.h | 22 +- src/ioc/db/dbPutNotifyBlocker.cpp | 42 +- src/ioc/db/dbPutNotifyBlocker.h | 28 +- src/ioc/db/dbSubscriptionIO.cpp | 44 +- src/ioc/db/db_access.c | 68 +- src/ioc/db/db_access_routines.h | 18 +- src/ioc/db/db_test.c | 828 +++++------------------- src/ioc/rsrv/camessage.c | 559 ++++++++-------- src/ioc/rsrv/caservertask.c | 125 ++-- src/ioc/rsrv/server.h | 30 +- 22 files changed, 1206 insertions(+), 1745 deletions(-) diff --git a/src/ioc/as/asDbLib.c b/src/ioc/as/asDbLib.c index bf4a85dcc..734b5e8f8 100644 --- a/src/ioc/as/asDbLib.c +++ b/src/ioc/as/asDbLib.c @@ -6,7 +6,7 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* Author: Marty Kraimer Date: 02-11-94*/ @@ -27,7 +27,7 @@ #include "caeventmask.h" #include "callback.h" #include "dbStaticLib.h" -#include "dbAddr.h" +#include "dbChannel.h" #include "dbAccess.h" #include "db_field_log.h" #include "dbEvent.h" @@ -40,7 +40,7 @@ static char *pacf=NULL; static char *psubstitutions=NULL; static epicsThreadId asInitTheadId=0; static int firstTime = TRUE; - + static long asDbAddRecords(void) { DBENTRY dbentry; @@ -98,7 +98,7 @@ int epicsShareAPI asSetSubstitutions(const char *substitutions) } return(0); } - + static void asSpcAsCallback(struct dbCommon *precord) { asChangeGroup(&precord->asp, precord->asg); @@ -109,7 +109,7 @@ static void asInitCommonOnce(void *arg) int *firstTime = (int *)arg; *firstTime = FALSE; } - + static long asInitCommon(void) { long status; @@ -117,7 +117,7 @@ static long asInitCommon(void) int wasFirstTime = firstTime; static epicsThreadOnceId asInitCommonOnceFlag = EPICS_THREAD_ONCE_INIT; - + epicsThreadOnce(&asInitCommonOnceFlag,asInitCommonOnce,(void *)&firstTime); if(wasFirstTime) { if(!pacf) return(0); /*access security will NEVER be turned on*/ @@ -148,7 +148,7 @@ int epicsShareAPI asInit(void) { return(asInitCommon()); } - + static void wdCallback(void *arg) { ASDBCALLBACK *pcallback = (ASDBCALLBACK *)arg; @@ -195,23 +195,15 @@ int epicsShareAPI asInitAsyn(ASDBCALLBACK *pcallback) } return(0); } - -int epicsShareAPI asDbGetAsl(void *paddress) -{ - DBADDR *paddr = paddress; - dbFldDes *pflddes; - pflddes = paddr->pfldDes; - return((int)pflddes->as_level); +int epicsShareAPI asDbGetAsl(struct dbChannel *chan) +{ + return dbChannelFldDes(chan)->as_level; } -void * epicsShareAPI asDbGetMemberPvt(void *paddress) +void * epicsShareAPI asDbGetMemberPvt(struct dbChannel *chan) { - DBADDR *paddr = paddress; - dbCommon *precord; - - precord = paddr->precord; - return((void *)precord->asp); + return dbChannelRecord(chan)->asp; } static void astacCallback(ASCLIENTPVT clientPvt,asClientStatus status) @@ -259,7 +251,7 @@ int epicsShareAPI astac(const char *pname,const char *user,const char *location) } return(0); } - + static void myMemberCallback(ASMEMBERPVT memPvt,FILE *fp) { dbCommon *precord; diff --git a/src/ioc/as/asDbLib.h b/src/ioc/as/asDbLib.h index f209a9028..b40a8d9df 100644 --- a/src/ioc/as/asDbLib.h +++ b/src/ioc/as/asDbLib.h @@ -5,12 +5,12 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* share/epicsH/dbAsLib.h */ /* $Revision-Id$ */ /* Author: Marty Kraimer Date: 02-23-94*/ - + #ifndef INCdbAsLibh #define INCdbAsLibh @@ -22,6 +22,8 @@ typedef struct { long status; } ASDBCALLBACK; +struct dbChannel; + #ifdef __cplusplus extern "C" { #endif @@ -30,8 +32,8 @@ epicsShareFunc int epicsShareAPI asSetFilename(const char *acf); epicsShareFunc int epicsShareAPI asSetSubstitutions(const char *substitutions); epicsShareFunc int epicsShareAPI asInit(void); epicsShareFunc int epicsShareAPI asInitAsyn(ASDBCALLBACK *pcallback); -epicsShareFunc int epicsShareAPI asDbGetAsl( void *paddr); -epicsShareFunc void * epicsShareAPI asDbGetMemberPvt( void *paddr); +epicsShareFunc int epicsShareAPI asDbGetAsl(struct dbChannel *chan); +epicsShareFunc void * epicsShareAPI asDbGetMemberPvt(struct dbChannel *chan); epicsShareFunc int epicsShareAPI asdbdump(void); epicsShareFunc int epicsShareAPI asdbdumpFP(FILE *fp); epicsShareFunc int epicsShareAPI aspuag(const char *uagname); diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index d1ea856ec..280077149 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -5,7 +5,7 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbAccess.c */ /* $Revision-Id$ */ @@ -15,7 +15,7 @@ * Date: 11-7-90 */ - + #include #include #include @@ -89,7 +89,7 @@ static short mapDBFToDBR[DBF_NTYPES] = { /* The following is to handle SPC_AS */ static SPC_ASCALLBACK spcAsCallback = 0; - + static void inherit_severity(const struct pv_link *ppv_link, dbCommon *pdest, epicsEnum16 stat, epicsEnum16 sevr) { @@ -113,7 +113,7 @@ long epicsShareAPI dbPutSpecial(DBADDR *paddr,int pass) dbCommon *precord = paddr->precord; long status=0; long special=paddr->special; - + prset = dbGetRset(paddr); if(special<100) { /*global processing*/ if((special==SPC_NOMOD) && (pass==0)) { @@ -139,7 +139,7 @@ long epicsShareAPI dbPutSpecial(DBADDR *paddr,int pass) } return(0); } - + static void get_enum_strs(DBADDR *paddr, char **ppbuffer, struct rset *prset,long *options) { @@ -199,7 +199,7 @@ choice_common: *ppbuffer = ((char *)*ppbuffer) + dbr_enumStrs_size; return; } - + static void get_graphics(DBADDR *paddr, char **ppbuffer, struct rset *prset,long *options) { @@ -239,7 +239,7 @@ static void get_graphics(DBADDR *paddr, char **ppbuffer, } return; } - + static void get_control(DBADDR *paddr, char **ppbuffer, struct rset *prset,long *options) { @@ -279,7 +279,7 @@ static void get_control(DBADDR *paddr, char **ppbuffer, } return; } - + static void get_alarm(DBADDR *paddr, char **ppbuffer, struct rset *prset,long *options) { @@ -324,7 +324,7 @@ static void get_alarm(DBADDR *paddr, char **ppbuffer, } return; } - + static void getOptions(DBADDR *paddr,char **poriginal,long *options,void *pflin) { db_field_log *pfl= (db_field_log *)pflin; @@ -352,7 +352,7 @@ static void getOptions(DBADDR *paddr,char **poriginal,long *options,void *pflin) } if( (*options) & DBR_UNITS ) { memset(pbuffer,'\0',dbr_units_size); - if( prset && prset->get_units ){ + if( prset && prset->get_units ){ (*prset->get_units)(paddr, pbuffer); pbuffer[DB_UNITS_SIZE-1] = '\0'; } else { @@ -363,7 +363,7 @@ static void getOptions(DBADDR *paddr,char **poriginal,long *options,void *pflin) if( (*options) & DBR_PRECISION ) { memset(pbuffer, '\0', dbr_precision_size); if((field_type==DBF_FLOAT || field_type==DBF_DOUBLE) - && prset && prset->get_precision ){ + && prset && prset->get_precision ){ (*prset->get_precision)(paddr,pbuffer); } else { *options ^= DBR_PRECISION; /*Turn off DBR_PRECISION*/ @@ -392,7 +392,7 @@ static void getOptions(DBADDR *paddr,char **poriginal,long *options,void *pflin) get_alarm(paddr, &pbuffer, prset, options); *poriginal = pbuffer; } - + struct rset * epicsShareAPI dbGetRset(const struct dbAddr *paddr) { struct dbFldDes *pfldDes = paddr->pfldDes; @@ -462,7 +462,7 @@ int epicsShareAPI dbIsLinkConnected(const struct link *plink) int epicsShareAPI dbGetLinkDBFtype(const struct link *plink) { switch(plink->type) { - case DB_LINK: + case DB_LINK: { DBADDR *paddr = (DBADDR *)plink->value.pv_link.pvt; @@ -473,7 +473,7 @@ int epicsShareAPI dbGetLinkDBFtype(const struct link *plink) } return(-1); } - + /* * Process a record if its scan field is passive. * Will notify if processing is complete by callback. @@ -482,7 +482,7 @@ int epicsShareAPI dbGetLinkDBFtype(const struct link *plink) long epicsShareAPI dbScanPassive(dbCommon *pfrom, dbCommon *pto) { long status; - + /* if not passive just return success */ if(pto->scan != 0) return(0); @@ -524,7 +524,7 @@ void epicsShareAPI dbScanFwdLink(struct link *plink) dbCaPutLink(plink,DBR_SHORT,&fwdLinkValue,1); return; } - + /* * Process the record. * 1. Check for breakpoints. @@ -551,7 +551,7 @@ long epicsShareAPI dbProcess(dbCommon *precord) * to dbProcess() corresponding changes will have to * be made in the breakpoint handler. */ - + /* see if there are any stopped records or breakpoints */ if (lset_stack_count != 0) { /* @@ -566,7 +566,7 @@ long epicsShareAPI dbProcess(dbCommon *precord) if (dbBkpt(precord)) goto all_done; } - + /* check for trace processing*/ if (tpro) { if(*ptrace==0) { @@ -650,7 +650,7 @@ all_done: if(callNotifyCompletion && precord->ppn) dbNotifyCompletion(precord); return(status); } - + /* * Fill out a database structure (*paddr) for * a record given by the name "pname." @@ -726,59 +726,7 @@ finish: dbFinishEntry(&dbEntry); return status; } - -/* JOH 10-19-04 */ -static char * dbCopyInNameComponentOfPV ( - char * pBuf, unsigned bufLen, const char * pComponent ) -{ - unsigned compLen = strlen ( pComponent ); - if ( compLen < bufLen ) { - strcpy ( pBuf, pComponent ); - return pBuf + compLen; - } - else { - unsigned reducedSize = bufLen - 1u; - strncpy ( pBuf, pComponent, reducedSize ); - pBuf[reducedSize] = '\0'; - return pBuf + reducedSize; - } -} -/* - * Copies PV name into pBuf of length bufLen - * - * Returns the number of bytes written to pBuf - * not including null termination. - * JOH 10-19-04 - */ -unsigned dbNameOfPV ( - const dbAddr * paddr, char * pBuf, unsigned bufLen ) -{ - dbFldDes * pfldDes = paddr->pfldDes; - char * pBufTmp = pBuf; - if ( bufLen == 0u ) { - return 0u; - } - pBufTmp = dbCopyInNameComponentOfPV ( - pBufTmp, bufLen, paddr->precord->name ); - pBufTmp = dbCopyInNameComponentOfPV ( - pBufTmp, bufLen - ( pBufTmp - pBuf ), "." ); - pBufTmp = dbCopyInNameComponentOfPV ( - pBufTmp, bufLen - ( pBufTmp - pBuf ), pfldDes->name ); - return pBufTmp - pBuf; -} -/* - * Returns the number of bytes in the PV name - * not including null termination. - * JOH 10-19-04 - */ -unsigned dbNameSizeOfPV ( const dbAddr * paddr ) -{ - unsigned recNameLen = strlen ( paddr->precord->name ); - dbFldDes * pfldDes = paddr->pfldDes; - unsigned fieldNameLen = strlen ( pfldDes->name ); - return recNameLen + fieldNameLen + 1; -} - + long epicsShareAPI dbValueSize(short dbr_type) { /* sizes for value associated with each DBR request type */ @@ -826,7 +774,7 @@ int epicsShareAPI dbLoadRecords(const char* file, const char* subs) return dbReadDatabase(&pdbbase, file, 0, subs); } - + long epicsShareAPI dbGetLinkValue(struct link *plink, short dbrType, void *pbuffer, long *poptions, long *pnRequest) { @@ -902,7 +850,7 @@ long epicsShareAPI dbGetLinkValue(struct link *plink, short dbrType, } return status; } - + long epicsShareAPI dbPutLinkValue(struct link *plink, short dbrType, const void *pbuffer, long nRequest) { @@ -941,7 +889,7 @@ long epicsShareAPI dbPutLinkValue(struct link *plink, short dbrType, } return status; } - + long epicsShareAPI dbGetField(DBADDR *paddr,short dbrType, void *pbuffer, long *options, long *nRequest, void *pflin) { @@ -995,7 +943,7 @@ done: dbScanUnlock(precord); return status; } - + long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, void *pbuffer, long *options, long *nRequest, void *pflin) { @@ -1105,7 +1053,7 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, } return status; } - + devSup* epicsShareAPI dbDTYPtoDevSup(dbRecordType *prdes, int dtyp) { return (devSup *)ellNth(&prdes->devList, dtyp+1); } @@ -1366,7 +1314,7 @@ long epicsShareAPI dbPutField(DBADDR *paddr, short dbrType, dbScanUnlock(precord); return status; } - + static long putAckt(DBADDR *paddr, const unsigned short *pbuffer, long nRequest, long no_elements, long offset) { @@ -1395,7 +1343,7 @@ static long putAcks(DBADDR *paddr, const unsigned short *pbuffer, long nRequest, } return 0; } - + long epicsShareAPI dbPut(DBADDR *paddr, short dbrType, const void *pbuffer, long nRequest) { @@ -1471,7 +1419,7 @@ long epicsShareAPI dbPut(DBADDR *paddr, short dbrType, return status; } - + /* various utility routines */ long epicsShareAPI dbGetControlLimits( const struct link *plink,double *low, double *high) @@ -1516,7 +1464,7 @@ long epicsShareAPI dbGetGraphicLimits( *high = buffer.upper_disp_limit; return(0); } - + long epicsShareAPI dbGetAlarmLimits(const struct link *plink, double *lolo, double *low, double *high, double *hihi) { @@ -1561,7 +1509,7 @@ long epicsShareAPI dbGetPrecision(const struct link *plink,short *precision) *precision = buffer.precision.dp; return(0); } - + long epicsShareAPI dbGetUnits( const struct link *plink,char *units,int unitsSize) { diff --git a/src/ioc/db/dbAddr.h b/src/ioc/db/dbAddr.h index a91bcf76d..2a6e4c0e2 100644 --- a/src/ioc/db/dbAddr.h +++ b/src/ioc/db/dbAddr.h @@ -27,7 +27,4 @@ typedef struct dbAddr { typedef dbAddr DBADDR; -unsigned dbNameOfPV (const dbAddr * paddr, char * pBuf, unsigned bufLen); -unsigned dbNameSizeOfPV (const dbAddr * paddr); - #endif /* dbAddrh */ diff --git a/src/ioc/db/dbCAC.h b/src/ioc/db/dbCAC.h index 3032023dc..8325a7d2d 100644 --- a/src/ioc/db/dbCAC.h +++ b/src/ioc/db/dbCAC.h @@ -5,7 +5,7 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* * $Revision-Id$ @@ -49,7 +49,7 @@ #include "db_access.h" #include "dbNotify.h" #include "dbEvent.h" -#include "dbAddr.h" +#include "dbChannel.h" #include "dbLock.h" #include "dbCommon.h" #include "db_convert.h" @@ -75,25 +75,25 @@ protected: virtual ~dbBaseIO() {} }; -extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbAddr *paddr, +extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbChannel *dbch, int eventsRemaining, struct db_field_log *pfl ); -class dbSubscriptionIO : - public tsDLNode < dbSubscriptionIO >, +class dbSubscriptionIO : + public tsDLNode < dbSubscriptionIO >, public dbBaseIO { public: - dbSubscriptionIO ( + dbSubscriptionIO ( epicsGuard < epicsMutex > &, epicsMutex &, - dbContext &, dbChannelIO &, struct dbAddr &, cacStateNotify &, + dbContext &, dbChannelIO &, struct dbChannel *, cacStateNotify &, unsigned type, unsigned long count, unsigned mask, dbEventCtx ); void destructor ( epicsGuard < epicsMutex > & ); void unsubscribe ( epicsGuard < epicsMutex > & ); void channelDeleteException ( epicsGuard < epicsMutex > & ); void show ( epicsGuard < epicsMutex > &, unsigned level ) const; void show ( unsigned level ) const; - void * operator new ( size_t size, + void * operator new ( size_t size, tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & ); - epicsPlacementDeleteOperator (( void *, + epicsPlacementDeleteOperator (( void *, tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & )) private: epicsMutex & mutex; @@ -104,8 +104,8 @@ private: unsigned type; unsigned id; dbSubscriptionIO * isSubscription (); - friend void dbSubscriptionEventCallback ( - void * pPrivate, struct dbAddr * paddr, + friend void dbSubscriptionEventCallback ( + void * pPrivate, struct dbChannel * dbch, int eventsRemaining, struct db_field_log * pfl ); dbSubscriptionIO ( const dbSubscriptionIO & ); dbSubscriptionIO & operator = ( const dbSubscriptionIO & ); @@ -149,8 +149,8 @@ private: class dbContextReadNotifyCache { public: dbContextReadNotifyCache ( epicsMutex & ); - void callReadNotify ( epicsGuard < epicsMutex > &, - struct dbAddr & addr, unsigned type, unsigned long count, + void callReadNotify ( epicsGuard < epicsMutex > &, + struct dbChannel * dbch, unsigned type, unsigned long count, cacReadNotify & notify ); void show ( epicsGuard < epicsMutex > &, unsigned level ) const; private: @@ -162,29 +162,29 @@ private: class dbContext : public cacContext { public: - dbContext ( epicsMutex & cbMutex, epicsMutex & mutex, + dbContext ( epicsMutex & cbMutex, epicsMutex & mutex, cacContextNotify & notify ); virtual ~dbContext (); void destroyChannel ( epicsGuard < epicsMutex > &, dbChannelIO & ); - void callReadNotify ( epicsGuard < epicsMutex > &, - struct dbAddr & addr, unsigned type, unsigned long count, + void callReadNotify ( epicsGuard < epicsMutex > &, + struct dbChannel * dbch, unsigned type, unsigned long count, cacReadNotify & notify ); - void callStateNotify ( struct dbAddr &addr, unsigned type, unsigned long count, + void callStateNotify ( struct dbChannel * dbch, unsigned type, unsigned long count, const struct db_field_log * pfl, cacStateNotify & notify ); - void subscribe ( + void subscribe ( epicsGuard < epicsMutex > &, - struct dbAddr & addr, dbChannelIO & chan, - unsigned type, unsigned long count, unsigned mask, + struct dbChannel * dbch, dbChannelIO & chan, + unsigned type, unsigned long count, unsigned mask, cacStateNotify & notify, cacChannel::ioid * pId ); - void initiatePutNotify ( - epicsGuard < epicsMutex > &, dbChannelIO &, struct dbAddr &, - unsigned type, unsigned long count, const void * pValue, - cacWriteNotify & notify, cacChannel::ioid * pId ); + void initiatePutNotify ( + epicsGuard < epicsMutex > &, dbChannelIO &, struct dbChannel *, + unsigned type, unsigned long count, const void * pValue, + cacWriteNotify & notify, cacChannel::ioid * pId ); void show ( unsigned level ) const; void showAllIO ( const dbChannelIO & chan, unsigned level ) const; - void destroyAllIO ( + void destroyAllIO ( epicsGuard < epicsMutex > &, dbChannelIO & chan ); - void ioCancel ( epicsGuard < epicsMutex > &, + void ioCancel ( epicsGuard < epicsMutex > &, dbChannelIO & chan, const cacChannel::ioid &id ); void ioShow ( epicsGuard < epicsMutex > &, const cacChannel::ioid & id, unsigned level ) const; @@ -202,11 +202,11 @@ private: epics_auto_ptr < cacContext > pNetContext; char * pStateNotifyCache; - cacChannel & createChannel ( + cacChannel & createChannel ( epicsGuard < epicsMutex > &, - const char * pChannelName, cacChannelNotify &, + const char * pChannelName, cacChannelNotify &, cacChannel::priLev ); - void flush ( + void flush ( epicsGuard < epicsMutex > & ); unsigned circuitCount ( epicsGuard < epicsMutex > & ) const; @@ -214,7 +214,7 @@ private: epicsGuard < epicsMutex > & ) const; unsigned beaconAnomaliesSinceProgramStart ( epicsGuard < epicsMutex > & ) const; - void show ( + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; dbContext ( const dbContext & ); @@ -226,17 +226,17 @@ inline dbContextPrivateListOfIO::dbContextPrivateListOfIO () : { } -inline dbContextPrivateListOfIO::~dbContextPrivateListOfIO () +inline dbContextPrivateListOfIO::~dbContextPrivateListOfIO () { assert ( ! this->pBlocker ); } -inline void dbContext::callReadNotify ( - epicsGuard < epicsMutex > & guard, struct dbAddr &addr, +inline void dbContext::callReadNotify ( + epicsGuard < epicsMutex > & guard, struct dbChannel * dbch, unsigned type, unsigned long count, cacReadNotify & notifyIn ) { guard.assertIdenticalMutex ( this-> mutex ); - this->readNotifyCache.callReadNotify ( guard, addr, type, count, notifyIn ); + this->readNotifyCache.callReadNotify ( guard, dbch, type, count, notifyIn ); } #endif // dbCACh diff --git a/src/ioc/db/dbChannelIO.cpp b/src/ioc/db/dbChannelIO.cpp index c95778abe..9bf9b0d52 100644 --- a/src/ioc/db/dbChannelIO.cpp +++ b/src/ioc/db/dbChannelIO.cpp @@ -4,20 +4,20 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ -/* +/* * $Revision-Id$ * - * + * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 - * + * * Copyright, 1986, The Regents of the University of California. - * - * + * + * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 @@ -40,15 +40,12 @@ #include "dbChannelIO.h" #include "dbPutNotifyBlocker.h" -dbChannelIO::dbChannelIO ( - epicsMutex & mutexIn, cacChannelNotify & notify, - const dbAddr & addrIn, dbContext & serviceIO ) : - cacChannel ( notify ), mutex ( mutexIn ), serviceIO ( serviceIO ), - addr ( addrIn ) +dbChannelIO::dbChannelIO ( + epicsMutex & mutexIn, cacChannelNotify & notify, + dbChannel * dbchIn, dbContext & serviceIO ) : + cacChannel ( notify ), mutex ( mutexIn ), serviceIO ( serviceIO ), + dbch ( dbchIn ) { - unsigned bufLen = dbNameSizeOfPV ( & this->addr ) + 1; - this->pNameStr.reset ( new char [ bufLen ] ); - dbNameOfPV ( & this->addr, this->pNameStr.get (), bufLen ); } void dbChannelIO::initiateConnect ( epicsGuard < epicsMutex > & guard ) @@ -57,7 +54,7 @@ void dbChannelIO::initiateConnect ( epicsGuard < epicsMutex > & guard ) this->notify().connectNotify ( guard ); } -dbChannelIO::~dbChannelIO () +dbChannelIO::~dbChannelIO () { } @@ -65,48 +62,49 @@ void dbChannelIO::destructor ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); this->serviceIO.destroyAllIO ( guard, *this ); + dbChannelDelete ( this->dbch ); this->~dbChannelIO (); } -void dbChannelIO::destroy ( +void dbChannelIO::destroy ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); this->serviceIO.destroyChannel ( guard, *this ); - // dont access this pointer after above call because - // object nolonger exists + // don't access this pointer after above call because + // object no longer exists } -cacChannel::ioStatus dbChannelIO::read ( - epicsGuard < epicsMutex > & guard, unsigned type, - unsigned long count, cacReadNotify & notify, ioid * ) +cacChannel::ioStatus dbChannelIO::read ( + epicsGuard < epicsMutex > & guard, unsigned type, + unsigned long count, cacReadNotify & notify, ioid * ) { guard.assertIdenticalMutex ( this->mutex ); - this->serviceIO.callReadNotify ( guard, this->addr, + this->serviceIO.callReadNotify ( guard, this->dbch, type, count, notify ); return iosSynch; } -void dbChannelIO::write ( - epicsGuard < epicsMutex > & guard, unsigned type, +void dbChannelIO::write ( + epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, const void *pValue ) { epicsGuardRelease < epicsMutex > unguard ( guard ); if ( count > LONG_MAX ) { throw outOfBounds(); } - int status = db_put_field ( &this->addr, type, pValue, + int status = dbChannel_put ( this->dbch, type, pValue, static_cast (count) ); if ( status ) { - throw std::logic_error ( + throw std::logic_error ( "db_put_field() completed unsuccessfully" ); } } -cacChannel::ioStatus dbChannelIO::write ( - epicsGuard < epicsMutex > & guard, unsigned type, - unsigned long count, const void * pValue, - cacWriteNotify & notify, ioid * pId ) +cacChannel::ioStatus dbChannelIO::write ( + epicsGuard < epicsMutex > & guard, unsigned type, + unsigned long count, const void * pValue, + cacWriteNotify & notify, ioid * pId ) { guard.assertIdenticalMutex ( this->mutex ); @@ -114,24 +112,24 @@ cacChannel::ioStatus dbChannelIO::write ( throw outOfBounds(); } - this->serviceIO.initiatePutNotify ( - guard, *this, this->addr, + this->serviceIO.initiatePutNotify ( + guard, *this, this->dbch, type, count, pValue, notify, pId ); return iosAsynch; } -void dbChannelIO::subscribe ( - epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, - unsigned mask, cacStateNotify & notify, ioid * pId ) -{ +void dbChannelIO::subscribe ( + epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, + unsigned mask, cacStateNotify & notify, ioid * pId ) +{ guard.assertIdenticalMutex ( this->mutex ); - this->serviceIO.subscribe ( - guard, this->addr, *this, + this->serviceIO.subscribe ( + guard, this->dbch, *this, type, count, mask, notify, pId ); } -void dbChannelIO::ioCancel ( +void dbChannelIO::ioCancel ( epicsGuard < epicsMutex > & mutualExclusionGuard, const ioid & id ) { @@ -139,7 +137,7 @@ void dbChannelIO::ioCancel ( this->serviceIO.ioCancel ( mutualExclusionGuard, *this, id ); } -void dbChannelIO::ioShow ( +void dbChannelIO::ioShow ( epicsGuard < epicsMutex > & guard, const ioid & id, unsigned level ) const { @@ -147,31 +145,35 @@ void dbChannelIO::ioShow ( this->serviceIO.ioShow ( guard, id, level ); } -void dbChannelIO::show ( +void dbChannelIO::show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { guard.assertIdenticalMutex ( this->mutex ); - printf ("channel at %p attached to local database record %s\n", - static_cast ( this ), this->addr.precord->name ); + printf ("channel at %p attached to local database record %s\n", + static_cast ( this ), + dbChannelRecord ( this->dbch ) -> name ); if ( level > 0u ) { printf ( "\ttype %s, element count %li, field at %p\n", - dbf_type_to_text ( this->addr.dbr_field_type ), this->addr.no_elements, - this->addr.pfield ); - } - if ( level > 1u ) { - this->serviceIO.show ( level - 2u ); - this->serviceIO.showAllIO ( *this, level - 2u ); + dbf_type_to_text ( dbChannelExportType ( this->dbch ) ), + dbChannelElements ( this->dbch ), + dbChannelData ( this->dbch ) ); + if ( level > 1u ) { + dbChannelFilterShow ( this->dbch, level - 2u ); + this->serviceIO.show ( level - 2u ); + this->serviceIO.showAllIO ( *this, level - 2u ); + } } } unsigned long dbChannelIO::nativeElementCount ( - epicsGuard < epicsMutex > & guard ) const + epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->mutex ); - if ( this->addr.no_elements >= 0u ) { - return static_cast < unsigned long > ( this->addr.no_elements ); + long elements = dbChannelElements ( this->dbch ); + if ( elements >= 0u ) { + return static_cast < unsigned long > ( elements ); } return 0u; } @@ -181,24 +183,30 @@ const char * dbChannelIO::pName ( epicsGuard < epicsMutex > & guard ) const throw () { guard.assertIdenticalMutex ( this->mutex ); - return this->pNameStr.get (); + return dbChannelName ( this->dbch ); } unsigned dbChannelIO::getName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLen ) const throw () { - return dbNameOfPV ( & this->addr, pBuf, bufLen ); + const char *name = dbChannelName ( this->dbch ); + size_t len = strlen ( name ); + strncpy ( pBuf, name, bufLen ); + if (len < bufLen) + return len; + pBuf[--bufLen] = '\0'; + return bufLen; } short dbChannelIO::nativeType ( - epicsGuard < epicsMutex > & guard ) const + epicsGuard < epicsMutex > & guard ) const { guard.assertIdenticalMutex ( this->mutex ); - return this->addr.dbr_field_type; + return dbChannelExportType( this->dbch ); } -void * dbChannelIO::operator new ( size_t size, +void * dbChannelIO::operator new ( size_t size, tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); @@ -212,7 +220,7 @@ void * dbChannelIO::operator new ( size_t ) // X aCC 361 } #ifdef CXX_PLACEMENT_DELETE -void dbChannelIO::operator delete ( void *pCadaver, +void dbChannelIO::operator delete ( void *pCadaver, tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); diff --git a/src/ioc/db/dbChannelIO.h b/src/ioc/db/dbChannelIO.h index 0473962ba..56cf1f55d 100644 --- a/src/ioc/db/dbChannelIO.h +++ b/src/ioc/db/dbChannelIO.h @@ -5,7 +5,7 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* @@ -44,65 +44,64 @@ class dbChannelIO : public cacChannel, public dbContextPrivateListOfIO { public: - dbChannelIO ( - epicsMutex &, cacChannelNotify &, - const dbAddr &, dbContext & ); - void destructor ( + dbChannelIO ( + epicsMutex &, cacChannelNotify &, + dbChannel *, dbContext & ); + void destructor ( epicsGuard < epicsMutex > & ); void destroy ( epicsGuard < epicsMutex > & mutualExclusionGuard ); - void callReadNotify ( - epicsGuard < epicsMutex > &, - unsigned type, unsigned long count, + void callReadNotify ( + epicsGuard < epicsMutex > &, + unsigned type, unsigned long count, cacReadNotify & notify ); - void callStateNotify ( - unsigned type, unsigned long count, + void callStateNotify ( + unsigned type, unsigned long count, const struct db_field_log * pfl, cacStateNotify & notify ); - void show ( + void show ( epicsGuard < epicsMutex > &, unsigned level ) const; unsigned getName ( epicsGuard < epicsMutex > &, char * pBuf, unsigned bufLen ) const throw (); const char * pName ( epicsGuard < epicsMutex > & ) const throw (); - void * operator new ( size_t size, + void * operator new ( size_t size, tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & ); - epicsPlacementDeleteOperator (( void *, + epicsPlacementDeleteOperator (( void *, tsFreeList < dbChannelIO, 256, epicsMutexNOOP > & )) protected: ~dbChannelIO (); private: epicsMutex & mutex; dbContext & serviceIO; - dbAddr addr; - epics_auto_ptr < char, eapt_array > pNameStr; + dbChannel * dbch; void initiateConnect ( epicsGuard < epicsMutex > & ); - unsigned requestMessageBytesPending ( + unsigned requestMessageBytesPending ( epicsGuard < epicsMutex > & ); - void flush ( + void flush ( epicsGuard < epicsMutex > & ); - ioStatus read ( + ioStatus read ( epicsGuard < epicsMutex > &, - unsigned type, unsigned long count, + unsigned type, unsigned long count, cacReadNotify &, ioid * ); - void write ( + void write ( epicsGuard < epicsMutex > &, - unsigned type, unsigned long count, + unsigned type, unsigned long count, const void * pvalue ); - ioStatus write ( + ioStatus write ( epicsGuard < epicsMutex > &, - unsigned type, unsigned long count, + unsigned type, unsigned long count, const void * pvalue, cacWriteNotify &, ioid * ); - void subscribe ( + void subscribe ( epicsGuard < epicsMutex > &, - unsigned type, unsigned long count, + unsigned type, unsigned long count, unsigned mask, cacStateNotify ¬ify, ioid * ); - void ioCancel ( + void ioCancel ( epicsGuard < epicsMutex > & mutualExclusionGuard, const ioid & ); - void ioShow ( + void ioShow ( epicsGuard < epicsMutex > &, const ioid &, unsigned level ) const; short nativeType ( @@ -115,18 +114,18 @@ private: void operator delete ( void * ); }; -inline void dbChannelIO::callReadNotify ( - epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, +inline void dbChannelIO::callReadNotify ( + epicsGuard < epicsMutex > & guard, unsigned type, unsigned long count, cacReadNotify & notify ) { guard.assertIdenticalMutex ( this->mutex ); - this->serviceIO.callReadNotify ( guard, this->addr, type, count, notify ); + this->serviceIO.callReadNotify ( guard, this->dbch, type, count, notify ); } -inline void dbChannelIO::callStateNotify ( unsigned type, unsigned long count, +inline void dbChannelIO::callStateNotify ( unsigned type, unsigned long count, const struct db_field_log *pfl, cacStateNotify ¬ify ) { - this->serviceIO.callStateNotify ( this->addr, type, count, pfl, notify ); + this->serviceIO.callStateNotify ( this->dbch, type, count, pfl, notify ); } diff --git a/src/ioc/db/dbContext.cpp b/src/ioc/db/dbContext.cpp index 86ebf1997..99f257e7e 100644 --- a/src/ioc/db/dbContext.cpp +++ b/src/ioc/db/dbContext.cpp @@ -4,19 +4,19 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ -/* +/* * $Revision-Id$ * - * + * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 - * + * * Copyright, 1986, The Regents of the University of California. - * - * + * + * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 @@ -33,6 +33,7 @@ #include "epicsEvent.h" #include "epicsThread.h" #include "errlog.h" +#include "dbChannel.h" #define epicsExportSharedSymbols #include "db_access_routines.h" @@ -43,20 +44,20 @@ class dbService : public cacService { public: ~dbService () {} - cacContext & contextCreate ( - epicsMutex & mutualExclusion, - epicsMutex & callbackControl, + cacContext & contextCreate ( + epicsMutex & mutualExclusion, + epicsMutex & callbackControl, cacContextNotify & ); }; static dbService dbs; -cacContext & dbService::contextCreate ( - epicsMutex & mutualExclusion, - epicsMutex & callbackControl, +cacContext & dbService::contextCreate ( + epicsMutex & mutualExclusion, + epicsMutex & callbackControl, cacContextNotify & notify ) { - return * new dbContext ( callbackControl, + return * new dbContext ( callbackControl, mutualExclusion, notify ); } @@ -67,9 +68,9 @@ extern "C" void dbServiceIOInit () dbBaseIO::dbBaseIO () {} -dbContext::dbContext ( epicsMutex & cbMutexIn, +dbContext::dbContext ( epicsMutex & cbMutexIn, epicsMutex & mutexIn, cacContextNotify & notifyIn ) : - readNotifyCache ( mutexIn ), ctx ( 0 ), + readNotifyCache ( mutexIn ), ctx ( 0 ), stateNotifyCacheSize ( 0 ), mutex ( mutexIn ), cbMutex ( cbMutexIn ), notify ( notifyIn ), pNetContext ( 0 ), pStateNotifyCache ( 0 ) { @@ -84,41 +85,47 @@ dbContext::~dbContext () } cacChannel & dbContext::createChannel ( // X aCC 361 - epicsGuard < epicsMutex > & guard, const char * pName, + epicsGuard < epicsMutex > & guard, const char * pName, cacChannelNotify & notifyIn, cacChannel::priLev priority ) { guard.assertIdenticalMutex ( this->mutex ); - struct dbAddr addr; - int status; - { - // dont know if the database might call a put callback - // while holding its lock ... - epicsGuardRelease < epicsMutex > unguard ( guard ); - status = db_name_to_addr ( pName, & addr ); - } - if ( status ) { + dbChannel *dbch = dbChannel_create ( pName ); + if ( ! dbch ) { if ( ! this->pNetContext.get() ) { this->pNetContext.reset ( - & this->notify.createNetworkContext ( + & this->notify.createNetworkContext ( this->mutex, this->cbMutex ) ); } return this->pNetContext->createChannel ( guard, pName, notifyIn, priority ); } - else if ( ca_preemtive_callback_is_enabled () ) { - return * new ( this->dbChannelIOFreeList ) - dbChannelIO ( this->mutex, notifyIn, addr, *this ); - } - else { - errlogPrintf ( + + if ( ! ca_preemtive_callback_is_enabled () ) { + dbChannelDelete ( dbch ); + errlogPrintf ( "dbContext: preemptive callback required for direct in\n" "memory interfacing of CA channels to the DB.\n" ); throw cacChannel::unsupportedByService (); } + + long status = dbChannelOpen ( dbch ); + if (status) { + dbChannelDelete ( dbch ); + throw cacChannel::notConnected (); + } + + try { + return * new ( this->dbChannelIOFreeList ) + dbChannelIO ( this->mutex, notifyIn, dbch, *this ); + } + catch (...) { + dbChannelDelete ( dbch ); + throw; + } } -void dbContext::destroyChannel ( +void dbContext::destroyChannel ( epicsGuard < epicsMutex > & guard, dbChannelIO & chan ) { guard.assertIdenticalMutex ( this->mutex ); @@ -134,30 +141,30 @@ void dbContext::destroyChannel ( this->dbChannelIOFreeList.release ( & chan ); } -void dbContext::callStateNotify ( struct dbAddr & addr, - unsigned type, unsigned long count, - const struct db_field_log * pfl, +void dbContext::callStateNotify ( struct dbChannel * dbch, + unsigned type, unsigned long count, + const struct db_field_log * pfl, cacStateNotify & notifyIn ) { unsigned long size = dbr_size_n ( type, count ); if ( type > INT_MAX ) { epicsGuard < epicsMutex > guard ( this->mutex ); - notifyIn.exception ( guard, ECA_BADTYPE, - "type code out of range (high side)", + notifyIn.exception ( guard, ECA_BADTYPE, + "type code out of range (high side)", type, count ); return; } if ( count > INT_MAX ) { epicsGuard < epicsMutex > guard ( this->mutex ); - notifyIn.exception ( guard, ECA_BADCOUNT, + notifyIn.exception ( guard, ECA_BADCOUNT, "element count out of range (high side)", type, count); return; } - // no need to lock this because state notify is + // no need to lock this because state notify is // called from only one event queue consumer thread if ( this->stateNotifyCacheSize < size) { char * pTmp = new char [size]; @@ -166,14 +173,14 @@ void dbContext::callStateNotify ( struct dbAddr & addr, this->stateNotifyCacheSize = size; } void *pvfl = (void *) pfl; - int status = db_get_field ( &addr, static_cast ( type ), + int status = dbChannel_get ( dbch, static_cast ( type ), this->pStateNotifyCache, static_cast ( count ), pvfl ); if ( status ) { epicsGuard < epicsMutex > guard ( this->mutex ); - notifyIn.exception ( guard, ECA_GETFAIL, - "db_get_field() completed unsuccessfuly", type, count ); + notifyIn.exception ( guard, ECA_GETFAIL, + "dbChannel_get() completed unsuccessfully", type, count ); } - else { + else { epicsGuard < epicsMutex > guard ( this->mutex ); notifyIn.current ( guard, type, count, this->pStateNotifyCache ); } @@ -185,10 +192,10 @@ extern "C" void cacAttachClientCtx ( void * pPrivate ) assert ( status == ECA_NORMAL ); } -void dbContext::subscribe ( +void dbContext::subscribe ( epicsGuard < epicsMutex > & guard, - struct dbAddr & addr, dbChannelIO & chan, - unsigned type, unsigned long count, unsigned mask, + struct dbChannel * dbch, dbChannelIO & chan, + unsigned type, unsigned long count, unsigned mask, cacStateNotify & notifyIn, cacChannel::ioid * pId ) { guard.assertIdenticalMutex ( this->mutex ); @@ -214,12 +221,12 @@ void dbContext::subscribe ( unsigned selfPriority = epicsThreadGetPrioritySelf (); unsigned above; - epicsThreadBooleanStatus tbs = + epicsThreadBooleanStatus tbs = epicsThreadLowestPriorityLevelAbove ( selfPriority, &above ); if ( tbs != epicsThreadBooleanStatusSuccess ) { above = selfPriority; } - int status = db_start_events ( tmpctx, "CAC-event", + int status = db_start_events ( tmpctx, "CAC-event", cacAttachClientCtx, ca_current_context (), above ); if ( status ) { db_close_events ( tmpctx ); @@ -227,7 +234,7 @@ void dbContext::subscribe ( } } if ( this->ctx ) { - // another thread tried to simultaneously setup + // another thread tried to simultaneously setup // the event system db_close_events ( tmpctx ); } @@ -237,9 +244,9 @@ void dbContext::subscribe ( } dbSubscriptionIO & subscr = - * new ( this->dbSubscriptionIOFreeList ) - dbSubscriptionIO ( guard, this->mutex, *this, chan, - addr, notifyIn, type, count, mask, this->ctx ); + * new ( this->dbSubscriptionIOFreeList ) + dbSubscriptionIO ( guard, this->mutex, *this, chan, + dbch, notifyIn, type, count, mask, this->ctx ); chan.dbContextPrivateListOfIO::eventq.add ( subscr ); this->ioTable.idAssignAdd ( subscr ); @@ -248,27 +255,27 @@ void dbContext::subscribe ( } } -void dbContext::initiatePutNotify ( +void dbContext::initiatePutNotify ( epicsGuard < epicsMutex > & guard, - dbChannelIO & chan, struct dbAddr & addr, - unsigned type, unsigned long count, const void * pValue, + dbChannelIO & chan, struct dbChannel * dbch, + unsigned type, unsigned long count, const void * pValue, cacWriteNotify & notifyIn, cacChannel::ioid * pId ) { guard.assertIdenticalMutex ( this->mutex ); if ( ! chan.dbContextPrivateListOfIO::pBlocker ) { - chan.dbContextPrivateListOfIO::pBlocker = - new ( this->dbPutNotifyBlockerFreeList ) + chan.dbContextPrivateListOfIO::pBlocker = + new ( this->dbPutNotifyBlockerFreeList ) dbPutNotifyBlocker ( this->mutex ); this->ioTable.idAssignAdd ( *chan.dbContextPrivateListOfIO::pBlocker ); } - chan.dbContextPrivateListOfIO::pBlocker->initiatePutNotify ( - guard, notifyIn, addr, type, count, pValue ); + chan.dbContextPrivateListOfIO::pBlocker->initiatePutNotify ( + guard, notifyIn, dbch, type, count, pValue ); if ( pId ) { *pId = chan.dbContextPrivateListOfIO::pBlocker->getId (); } } -void dbContext::destroyAllIO ( +void dbContext::destroyAllIO ( epicsGuard < epicsMutex > & guard, dbChannelIO & chan ) { guard.assertIdenticalMutex ( this->mutex ); @@ -284,7 +291,7 @@ void dbContext::destroyAllIO ( } while ( ( pIO = tmp.get() ) ) { - // This prevents a db event callback from coming + // This prevents a db event callback from coming // through after the notify IO is deleted pIO->unsubscribe ( guard ); // If they call ioCancel() here it will be ignored @@ -301,8 +308,8 @@ void dbContext::destroyAllIO ( } } -void dbContext::ioCancel ( - epicsGuard < epicsMutex > & guard, dbChannelIO & chan, +void dbContext::ioCancel ( + epicsGuard < epicsMutex > & guard, dbChannelIO & chan, const cacChannel::ioid &id ) { guard.assertIdenticalMutex ( this->mutex ); @@ -325,8 +332,8 @@ void dbContext::ioCancel ( } } -void dbContext::ioShow ( - epicsGuard < epicsMutex > & guard, const cacChannel::ioid &id, +void dbContext::ioShow ( + epicsGuard < epicsMutex > & guard, const cacChannel::ioid &id, unsigned level ) const { guard.assertIdenticalMutex ( this->mutex ); @@ -339,7 +346,7 @@ void dbContext::ioShow ( void dbContext::showAllIO ( const dbChannelIO & chan, unsigned level ) const { epicsGuard < epicsMutex > guard ( this->mutex ); - tsDLIterConst < dbSubscriptionIO > pItem = + tsDLIterConst < dbSubscriptionIO > pItem = chan.dbContextPrivateListOfIO::eventq.firstIter (); while ( pItem.valid () ) { pItem->show ( guard, level ); @@ -356,14 +363,14 @@ void dbContext::show ( unsigned level ) const this->show ( guard, level ); } -void dbContext::show ( +void dbContext::show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { guard.assertIdenticalMutex ( this->mutex ); - printf ( "dbContext at %p\n", + printf ( "dbContext at %p\n", static_cast ( this ) ); if ( level > 0u ) { - printf ( "\tevent call back cache location %p, and its size %lu\n", + printf ( "\tevent call back cache location %p, and its size %lu\n", static_cast ( this->pStateNotifyCache ), this->stateNotifyCacheSize ); this->readNotifyCache.show ( guard, level - 1 ); } @@ -375,7 +382,7 @@ void dbContext::show ( } } -void dbContext::flush ( +void dbContext::flush ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); diff --git a/src/ioc/db/dbContextReadNotifyCache.cpp b/src/ioc/db/dbContextReadNotifyCache.cpp index 1d3b8ab1e..f298a63c8 100644 --- a/src/ioc/db/dbContextReadNotifyCache.cpp +++ b/src/ioc/db/dbContextReadNotifyCache.cpp @@ -5,7 +5,7 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* @@ -30,11 +30,11 @@ dbContextReadNotifyCache::dbContextReadNotifyCache ( epicsMutex & mutexIn ) : class privateAutoDestroyPtr { public: - privateAutoDestroyPtr ( + privateAutoDestroyPtr ( dbContextReadNotifyCacheAllocator & allocator, unsigned long size ) : _allocator ( allocator ), _p ( allocator.alloc ( size ) ) {} ~privateAutoDestroyPtr () { _allocator.free ( _p ); } - char * get () const { return _p; } + char * get () const { return _p; } private: dbContextReadNotifyCacheAllocator & _allocator; char * _p; @@ -43,28 +43,28 @@ private: }; // extra effort taken here to not hold the lock when calling the callback -void dbContextReadNotifyCache::callReadNotify ( - epicsGuard < epicsMutex > & guard, struct dbAddr & addr, +void dbContextReadNotifyCache::callReadNotify ( + epicsGuard < epicsMutex > & guard, struct dbChannel * dbch, unsigned type, unsigned long count, cacReadNotify & notify ) { guard.assertIdenticalMutex ( _mutex ); if ( type > INT_MAX ) { - notify.exception ( guard, ECA_BADTYPE, - "type code out of range (high side)", + notify.exception ( guard, ECA_BADTYPE, + "type code out of range (high side)", type, count ); return; } - if ( addr.no_elements < 0 ) { - notify.exception ( guard, ECA_BADCOUNT, + if ( dbChannelElements(dbch) < 0 ) { + notify.exception ( guard, ECA_BADCOUNT, "database has negetive element count", type, count); return; } - if ( count > static_cast < unsigned > ( addr.no_elements ) ) { - notify.exception ( guard, ECA_BADCOUNT, + if ( count > static_cast < unsigned long > ( dbChannelElements(dbch) ) ) { + notify.exception ( guard, ECA_BADCOUNT, "element count out of range (high side)", type, count); return; @@ -75,21 +75,21 @@ void dbContextReadNotifyCache::callReadNotify ( int status; { epicsGuardRelease < epicsMutex > unguard ( guard ); - status = db_get_field ( &addr, static_cast ( type ), - ptr.get (), static_cast ( count ), 0 ); + status = dbChannel_get ( dbch, static_cast ( type ), + ptr.get (), static_cast ( count ), 0 ); } if ( status ) { - notify.exception ( guard, ECA_GETFAIL, + notify.exception ( guard, ECA_GETFAIL, "db_get_field() completed unsuccessfuly", type, count ); } - else { - notify.completion ( + else { + notify.completion ( guard, type, count, ptr.get () ); } } -void dbContextReadNotifyCache::show ( +void dbContextReadNotifyCache::show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { guard.assertIdenticalMutex ( _mutex ); @@ -155,8 +155,8 @@ void dbContextReadNotifyCacheAllocator::show ( unsigned level ) const pNext = _pReadNotifyCache->pNext; count++; } - printf ( "\tcount %lu and size %lu\n", - static_cast < unsigned long > ( count ), + printf ( "\tcount %lu and size %lu\n", + static_cast < unsigned long > ( count ), _readNotifyCacheSize ); } } diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index 76f4f1a2c..d53a2e4f2 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -5,13 +5,13 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbEvent.c */ /* $Revision-Id$ */ /* routines for scheduling events to lower priority tasks via the RT kernel */ /* - * Author: Jeffrey O. Hill + * Author: Jeffrey O. Hill * Date: 4-1-89 */ @@ -39,6 +39,7 @@ #include "db_field_log.h" #define epicsExportSharedSymbols #include "dbAddr.h" +#include "dbChannel.h" #include "dbLock.h" #include "dbAccessDefs.h" #include "dbEvent.h" @@ -53,7 +54,7 @@ */ struct evSubscrip { ELLNODE node; - struct dbAddr *paddr; + struct dbChannel *chan; EVENTFUNC *user_sub; void *user_arg; struct event_que *ev_que; @@ -80,20 +81,20 @@ struct event_que { unsigned short putix; unsigned short getix; unsigned short quota; /* the number of assigned entries*/ - unsigned short nDuplicates; /* N events duplicated on this q */ + unsigned short nDuplicates; /* N events duplicated on this q */ unsigned short nCanceled; /* the number of canceled entries */ }; struct event_user { struct event_que firstque; /* the first event que */ - + epicsMutexId lock; epicsEventId ppendsem; /* Wait while empty */ epicsEventId pflush_sem; /* wait for flush */ - + EXTRALABORFUNC *extralabor_sub;/* off load to event task */ void *extralabor_arg;/* parameter to above */ - + epicsThreadId taskid; /* event handler task id */ struct evSubscrip *pSuicideEvent; /* event that is deleteing itself */ unsigned queovr; /* event que overflow count */ @@ -134,14 +135,14 @@ static char *EVENT_PEND_NAME = "eventTask"; static struct evSubscrip canceledEvent; -static unsigned short ringSpace ( const struct event_que *pevq ) +static unsigned short ringSpace ( const struct event_que *pevq ) { if ( pevq->evque[pevq->putix] == EVENTQEMPTY ) { if ( pevq->getix > pevq->putix ) { return ( unsigned short ) ( pevq->getix - pevq->putix ); } else { - return ( unsigned short ) ( ( EVENTQUESIZE + pevq->getix ) - pevq->putix ); + return ( unsigned short ) ( ( EVENTQUESIZE + pevq->getix ) - pevq->putix ); } } return 0; @@ -186,7 +187,7 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) ellCount ( &addr.precord->mlis ) ); while ( pevent ) { - pdbFldDes = pevent->paddr->pfldDes; + pdbFldDes = dbChannelFldDes(pevent->chan); if ( level > 0 ) { printf ( "%4.4s", pdbFldDes->name ); @@ -210,15 +211,15 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) taskId = ( void * ) pevent->ev_que->evUser->taskid; UNLOCKEVQUE(pevent->ev_que); if ( nEntriesFree == 0u ) { - printf ( ", thread=%p, queue full", + printf ( ", thread=%p, queue full", (void *) taskId ); } else if ( nEntriesFree == EVENTQUESIZE ) { - printf ( ", thread=%p, queue empty", + printf ( ", thread=%p, queue empty", (void *) taskId ); } else { - printf ( ", thread=%p, unused entries=%u", + printf ( ", thread=%p, unused entries=%u", (void *) taskId, nEntriesFree ); } } @@ -245,9 +246,9 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) } if ( level > 3 ) { - printf ( ", ev %p, ev que %p, ev user %p", - ( void * ) pevent, - ( void * ) pevent->ev_que, + printf ( ", ev %p, ev que %p, ev user %p", + ( void * ) pevent, + ( void * ) pevent->ev_que, ( void * ) pevent->ev_que->evUser ); } @@ -268,32 +269,32 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) * * Initialize the event facility for this task. Must be called at least once * by each task which uses the db event facility - * + * * returns: ptr to event user block or NULL if memory can't be allocated */ dbEventCtx epicsShareAPI db_init_events (void) { struct event_user * evUser; - + if (!dbevEventUserFreeList) { - freeListInitPvt(&dbevEventUserFreeList, + freeListInitPvt(&dbevEventUserFreeList, sizeof(struct event_user),8); } if (!dbevEventQueueFreeList) { - freeListInitPvt(&dbevEventQueueFreeList, + freeListInitPvt(&dbevEventQueueFreeList, sizeof(struct event_que),8); } if (!dbevEventBlockFreeList) { - freeListInitPvt(&dbevEventBlockFreeList, + freeListInitPvt(&dbevEventBlockFreeList, sizeof(struct evSubscrip),256); } - - evUser = (struct event_user *) + + evUser = (struct event_user *) freeListCalloc(dbevEventUserFreeList); if (!evUser) { return NULL; } - + evUser->firstque.evUser = evUser; evUser->firstque.writelock = epicsMutexCreate(); if (!evUser->firstque.writelock) { @@ -304,7 +305,7 @@ dbEventCtx epicsShareAPI db_init_events (void) if (!evUser->ppendsem) { epicsMutexDestroy (evUser->firstque.writelock); return NULL; - } + } evUser->pflush_sem = epicsEventCreate(epicsEventEmpty); if (!evUser->pflush_sem) { epicsMutexDestroy (evUser->firstque.writelock); @@ -327,7 +328,7 @@ dbEventCtx epicsShareAPI db_init_events (void) /* * DB_CLOSE_EVENTS() - * + * * evUser block and additional event queues * deallocated when the event thread terminates * itself @@ -341,7 +342,7 @@ void epicsShareAPI db_close_events (dbEventCtx ctx) * Exit not forced on event blocks for now - this is left to channel * access and any other tasks using this facility which can find them * more efficiently. - * + * * NOTE: not deleting events before calling this routine could be * hazardous to the system's health. */ @@ -375,7 +376,7 @@ static struct event_que * create_ev_que ( struct event_user * const evUser ) * DB_ADD_EVENT() */ dbEventSubscription epicsShareAPI db_add_event ( - dbEventCtx ctx, struct dbAddr *paddr, + dbEventCtx ctx, struct dbChannel *chan, EVENTFUNC *user_sub, void *user_arg, unsigned select) { struct event_user * const evUser = (struct event_user *) ctx; @@ -430,7 +431,7 @@ dbEventSubscription epicsShareAPI db_add_event ( pevent->nreplace = 0ul; pevent->user_sub = user_sub; pevent->user_arg = user_arg; - pevent->paddr = paddr; + pevent->chan = chan; pevent->select = (unsigned char) select; pevent->pLastLog = NULL; /* not yet in the queue */ pevent->callBackInProgress = FALSE; @@ -442,8 +443,8 @@ dbEventSubscription epicsShareAPI db_add_event ( * communication (for other types they get whatever happens to be * there upon wakeup) */ - if( paddr->no_elements == 1 && - paddr->field_size <= sizeof(union native_value)) { + if( dbChannelElements(chan) == 1 && + dbChannelElementSize(chan) <= sizeof(union native_value)) { pevent->valque = TRUE; } else { @@ -459,8 +460,7 @@ dbEventSubscription epicsShareAPI db_add_event ( void epicsShareAPI db_event_enable (dbEventSubscription es) { struct evSubscrip * const pevent = (struct evSubscrip *) es; - struct dbCommon * const precord = - (struct dbCommon *) pevent->paddr->precord; + struct dbCommon * const precord = dbChannelRecord(pevent->chan); LOCKREC(precord); if ( ! pevent->enabled ) { @@ -476,8 +476,7 @@ void epicsShareAPI db_event_enable (dbEventSubscription es) void epicsShareAPI db_event_disable (dbEventSubscription es) { struct evSubscrip * const pevent = (struct evSubscrip *) es; - struct dbCommon * const precord = - (struct dbCommon *) pevent->paddr->precord; + struct dbCommon * const precord = dbChannelRecord(pevent->chan); LOCKREC(precord); if ( pevent->enabled ) { @@ -491,7 +490,7 @@ void epicsShareAPI db_event_disable (dbEventSubscription es) * event_remove() * event queue lock _must_ be applied */ -static void event_remove ( struct event_que *ev_que, +static void event_remove ( struct event_que *ev_que, unsigned short index, struct evSubscrip *placeHolder ) { struct evSubscrip * const pEvent = ev_que->evque[index]; @@ -511,9 +510,9 @@ static void event_remove ( struct event_que *ev_que, /* * DB_CANCEL_EVENT() * - * This routine does not prevent two threads from deleting + * This routine does not prevent two threads from deleting * the same block at the same time. - * + * */ void epicsShareAPI db_cancel_event (dbEventSubscription es) { @@ -523,7 +522,7 @@ void epicsShareAPI db_cancel_event (dbEventSubscription es) db_event_disable ( es ); /* - * flag the event as canceled by NULLing out the callback handler + * flag the event as canceled by NULLing out the callback handler * * make certain that the event isnt being accessed while * its call back changes @@ -535,14 +534,14 @@ void epicsShareAPI db_cancel_event (dbEventSubscription es) /* * purge this event from the queue * - * Its better to take this approach rather than waiting + * Its better to take this approach rather than waiting * for the event thread to finish removing this event * from the queue because the event thread will not - * process if we are in flow control mode. Since blocking + * process if we are in flow control mode. Since blocking * here will block CA's TCP input queue then a dead lock * would be possible. */ - for ( getix = pevent->ev_que->getix; + for ( getix = pevent->ev_que->getix; pevent->ev_que->evque[getix] != EVENTQEMPTY; ) { if ( pevent->ev_que->evque[getix] == pevent ) { assert ( pevent->ev_que->nCanceled < USHRT_MAX ); @@ -570,7 +569,7 @@ void epicsShareAPI db_cancel_event (dbEventSubscription es) pevent->ev_que->quota -= EVENTENTRIES; UNLOCKEVQUE ( pevent->ev_que ) - + freeListFree ( dbevEventBlockFreeList, pevent ); return; @@ -645,7 +644,7 @@ int epicsShareAPI db_post_extra_labor (dbEventCtx ctx) * NOTE: This assumes that the db scan lock is already applied */ static void db_post_single_event_private (struct evSubscrip *event) -{ +{ struct event_que * const ev_que = event->ev_que; db_field_log * pLog; int firstEventFlag; @@ -661,7 +660,7 @@ static void db_post_single_event_private (struct evSubscrip *event) /* * if we have an event on the queue and we are * not saving the current value (because this is a - * string or an array) then ignore duplicate + * string or an array) then ignore duplicate * events (saving them without the current valuye * serves no purpose) */ @@ -670,8 +669,8 @@ static void db_post_single_event_private (struct evSubscrip *event) return; } - /* - * add to task local event que + /* + * add to task local event que */ /* @@ -680,7 +679,7 @@ static void db_post_single_event_private (struct evSubscrip *event) * then replace the last event on the queue (for this monitor) */ rngSpace = ringSpace ( ev_que ); - if ( event->npend>0u && + if ( event->npend>0u && (ev_que->evUser->flowCtrlMode || rngSpace<=EVENTSPERQUE) ) { /* * replace last event if no space is left @@ -688,7 +687,7 @@ static void db_post_single_event_private (struct evSubscrip *event) pLog = event->pLastLog; event->nreplace++; /* - * the event task has already been notified about + * the event task has already been notified about * this so we dont need to post the semaphore */ firstEventFlag = 0; @@ -705,9 +704,9 @@ static void db_post_single_event_private (struct evSubscrip *event) ev_que->nDuplicates++; } event->npend++; - /* - * if the ring buffer was empty before - * adding this event + /* + * if the ring buffer was empty before + * adding this event */ if (rngSpace==EVENTQUESIZE) { firstEventFlag = 1; @@ -719,38 +718,39 @@ static void db_post_single_event_private (struct evSubscrip *event) } if (pLog && event->valque) { - struct dbCommon *precord = event->paddr->precord; + struct dbChannel *chan = event->chan; + struct dbCommon *precord = dbChannelRecord(chan); pLog->stat = precord->stat; pLog->sevr = precord->sevr; pLog->time = precord->time; /* * use memcpy to avoid a bus error on - * union copy of char in the db at an odd + * union copy of char in the db at an odd * address */ - memcpy( (char *)&pLog->field, - (char *)event->paddr->pfield, - event->paddr->field_size); + memcpy(& pLog->field, + dbChannelData(chan), + dbChannelElementSize(chan)); event->pLastLog = pLog; } UNLOCKEVQUE(ev_que) - /* - * its more efficent to notify the event handler + /* + * its more efficent to notify the event handler * only after the event is ready and the lock * is off in case it runs at a higher priority * than the caller here. */ if (firstEventFlag) { - /* - * notify the event handler + /* + * notify the event handler */ epicsEventSignal(ev_que->evUser->ppendsem); } -} +} /* * DB_POST_EVENTS() @@ -763,22 +763,22 @@ void *pRecord, void *pField, unsigned int caEventMask ) -{ +{ struct dbCommon * const pdbc = (struct dbCommon *)pRecord; struct evSubscrip * event; if (pdbc->mlis.count == 0) return DB_EVENT_OK; /* no monitors set */ LOCKREC(pdbc); - + for (event = (struct evSubscrip *) pdbc->mlis.node.next; event; event = (struct evSubscrip *) event->node.next){ - + /* * Only send event msg if they are waiting on the field which * changed or pval==NULL and waiting on alarms and alarms changed */ - if ( (event->paddr->pfield == (void *)pField || pField==NULL) && + if ( (dbChannelData(event->chan) == (void *)pField || pField==NULL) && (caEventMask & event->select) ) { db_post_single_event_private (event); } @@ -793,9 +793,9 @@ unsigned int caEventMask * DB_POST_SINGLE_EVENT() */ void epicsShareAPI db_post_single_event (dbEventSubscription es) -{ +{ struct evSubscrip * const event = (struct evSubscrip *) es; - struct dbCommon * const precord = event->paddr->precord; + struct dbCommon * const precord = dbChannelRecord(event->chan); dbScanLock (precord); db_post_single_event_private (event); @@ -808,15 +808,15 @@ void epicsShareAPI db_post_single_event (dbEventSubscription es) static int event_read ( struct event_que *ev_que ) { db_field_log *pfl; - void ( *user_sub ) ( void *user_arg, struct dbAddr *paddr, + void ( *user_sub ) ( void *user_arg, struct dbChannel *chan, int eventsRemaining, db_field_log *pfl ); - + /* * evUser ring buffer must be locked for the multiple * threads writing/reading it */ LOCKEVQUE ( ev_que ) - + /* * if in flow control mode drain duplicates and then * suspend processing events until flow control @@ -826,7 +826,7 @@ static int event_read ( struct event_que *ev_que ) UNLOCKEVQUE(ev_que); return DB_EVENT_OK; } - + while ( ev_que->evque[ev_que->getix] != EVENTQEMPTY ) { db_field_log fl = ev_que->valque[ev_que->getix]; struct evSubscrip *event = ev_que->evque[ev_que->getix]; @@ -865,20 +865,20 @@ static int event_read ( struct event_que *ev_que ) * if more events are waiting in the queue * * Must remove the lock here so that we dont deadlock if - * this calls dbGetField() and blocks on the record lock, - * dbPutField() is in progress in another task, it has the - * record lock, and it is calling db_post_events() waiting + * this calls dbGetField() and blocks on the record lock, + * dbPutField() is in progress in another task, it has the + * record lock, and it is calling db_post_events() waiting * for the event queue lock (which this thread now has). */ if ( user_sub ) { /* * This provides a way to test to see if an event is in use - * despite the fact that the event queue does not point to - * it. + * despite the fact that the event queue does not point to + * it. */ event->callBackInProgress = TRUE; UNLOCKEVQUE ( ev_que ) - ( *user_sub ) ( event->user_arg, event->paddr, + ( *user_sub ) ( event->user_arg, event->chan, ev_que->evque[ev_que->getix] != EVENTQEMPTY, pfl ); LOCKEVQUE ( ev_que ) @@ -951,7 +951,7 @@ static void event_task (void *pParm) } evUser->extraLaborBusy = FALSE; - for ( ev_que = &evUser->firstque; ev_que; + for ( ev_que = &evUser->firstque; ev_que; ev_que = ev_que->nextque ) { epicsMutexUnlock ( evUser->lock ); event_read (ev_que); @@ -967,7 +967,7 @@ static void event_task (void *pParm) struct event_que *nextque; ev_que = evUser->firstque.nextque; - while(ev_que){ + while(ev_que){ nextque = ev_que->nextque; epicsMutexDestroy(ev_que->writelock); freeListFree(dbevEventQueueFreeList, ev_que); @@ -990,15 +990,15 @@ static void event_task (void *pParm) * DB_START_EVENTS() */ int epicsShareAPI db_start_events ( - dbEventCtx ctx,const char *taskname, void (*init_func)(void *), + dbEventCtx ctx,const char *taskname, void (*init_func)(void *), void *init_func_arg, unsigned osiPriority ) { struct event_user * const evUser = (struct event_user *) ctx; - + epicsMutexMustLock ( evUser->lock ); - /* - * only one ca_pend_event thread may be + /* + * only one ca_pend_event thread may be * started for each evUser */ if (evUser->taskid) { @@ -1044,7 +1044,7 @@ void epicsShareAPI db_event_flow_ctrl_mode_on (dbEventCtx ctx) epicsMutexMustLock ( evUser->lock ); evUser->flowCtrlMode = TRUE; epicsMutexUnlock ( evUser->lock ); - /* + /* * notify the event handler task */ epicsEventSignal(evUser->ppendsem); @@ -1063,13 +1063,13 @@ void epicsShareAPI db_event_flow_ctrl_mode_off (dbEventCtx ctx) epicsMutexMustLock ( evUser->lock ); evUser->flowCtrlMode = FALSE; epicsMutexUnlock ( evUser->lock ); - /* + /* * notify the event handler task */ epicsEventSignal (evUser->ppendsem); #ifdef DEBUG printf("fc off %lu\n", tickGet()); #endif -} +} diff --git a/src/ioc/db/dbEvent.h b/src/ioc/db/dbEvent.h index db7186849..d93e31594 100644 --- a/src/ioc/db/dbEvent.h +++ b/src/ioc/db/dbEvent.h @@ -5,13 +5,13 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ -/* +/* * $Revision-Id$ * - * Author: Jeff Hill - * Date: 030393 + * Author: Jeff Hill + * Date: 030393 */ #ifndef INCLdbEventh @@ -34,7 +34,7 @@ extern "C" { #endif -struct dbAddr; +struct dbChannel; struct db_field_log; epicsShareFunc int epicsShareAPI db_event_list ( @@ -49,7 +49,7 @@ typedef void * dbEventCtx; typedef void EXTRALABORFUNC (void *extralabor_arg); epicsShareFunc dbEventCtx epicsShareAPI db_init_events (void); epicsShareFunc int epicsShareAPI db_start_events ( - dbEventCtx ctx, const char *taskname, void (*init_func)(void *), + dbEventCtx ctx, const char *taskname, void (*init_func)(void *), void *init_func_arg, unsigned osiPriority ); epicsShareFunc void epicsShareAPI db_close_events (dbEventCtx ctx); epicsShareFunc void epicsShareAPI db_event_flow_ctrl_mode_on (dbEventCtx ctx); @@ -60,12 +60,12 @@ epicsShareFunc void epicsShareAPI db_flush_extra_labor_event (dbEventCtx); epicsShareFunc int epicsShareAPI db_post_extra_labor (dbEventCtx ctx); epicsShareFunc void epicsShareAPI db_event_change_priority ( dbEventCtx ctx, unsigned epicsPriority ); -typedef void EVENTFUNC (void *user_arg, struct dbAddr *paddr, +typedef void EVENTFUNC (void *user_arg, struct dbChannel *chan, int eventsRemaining, struct db_field_log *pfl); typedef void * dbEventSubscription; epicsShareFunc dbEventSubscription epicsShareAPI db_add_event ( - dbEventCtx ctx, struct dbAddr *paddr, + dbEventCtx ctx, struct dbChannel *chan, EVENTFUNC *user_sub, void *user_arg, unsigned select); epicsShareFunc void epicsShareAPI db_cancel_event (dbEventSubscription es); epicsShareFunc void epicsShareAPI db_post_single_event (dbEventSubscription es); diff --git a/src/ioc/db/dbNotify.c b/src/ioc/db/dbNotify.c index 7454129dc..1280291d2 100644 --- a/src/ioc/db/dbNotify.c +++ b/src/ioc/db/dbNotify.c @@ -1,19 +1,19 @@ /*************************************************************************\ * Copyright (c) 2002 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. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. -\*************************************************************************/ + * National Laboratory. + * Copyright (c) 2002 The Regents of the University of California, as + * Operator of Los Alamos National Laboratory. + * EPICS BASE Versions 3.13.7 + * and higher are distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + \*************************************************************************/ /* dbNotify.c */ /* base/src/db $Revision-Id$ */ /* * Author: Marty Kraimer * Date: 03-30-95 * Extracted from dbLink.c -*/ + */ #include #include @@ -36,7 +36,7 @@ #include "dbCommon.h" #define epicsExportSharedSymbols #include "callback.h" -#include "dbAddr.h" +#include "dbChannel.h" #include "dbScan.h" #include "dbLock.h" #include "callback.h" @@ -45,7 +45,7 @@ #include "dbNotify.h" #include "epicsTime.h" #include "cantProceed.h" - + /*putNotify.state values */ typedef enum { putNotifyNotActive, @@ -55,44 +55,44 @@ typedef enum { putNotifyPutInProgress, putNotifyUserCallbackRequested, putNotifyUserCallbackActive -}putNotifyState; +} putNotifyState; /*structure attached to ppnr field of each record*/ typedef struct putNotifyRecord { - ellCheckNode waitNode; - ELLLIST restartList; /*list of putNotifys to restart*/ + ellCheckNode waitNode; + ELLLIST restartList; /*list of putNotifys to restart*/ dbCommon *precord; -}putNotifyRecord; +} putNotifyRecord; #define MAGIC 0xfedc0123 typedef struct putNotifyPvt { - ELLNODE node; /*For free list*/ - long magic; - short state; - CALLBACK callback; - ELLLIST waitList; /*list of records for current putNotify*/ - short cancelWait; - short userCallbackWait; + ELLNODE node; /*For free list*/ + long magic; + short state; + CALLBACK callback; + ELLLIST waitList; /*list of records for current putNotify*/ + short cancelWait; + short userCallbackWait; epicsEventId cancelEvent; epicsEventId userCallbackEvent; -}putNotifyPvt; +} putNotifyPvt; /* putNotify groups can span locksets if links are dynamically modified*/ /* Thus a global lock is taken while putNotify fields are accessed */ typedef struct notifyGlobal { epicsMutexId lock; ELLLIST freeList; -}notifyGlobal; +} notifyGlobal; static notifyGlobal *pnotifyGlobal = 0; - + /*Local routines*/ static void putNotifyInit(putNotify *ppn); static void putNotifyCleanup(putNotify *ppn); static void restartCheck(putNotifyRecord *ppnr); -static void callUser(dbCommon *precord,putNotify *ppn); +static void callUser(dbCommon *precord, putNotify *ppn); static void notifyCallback(CALLBACK *pcallback); -static void putNotifyCommon(putNotify *ppn,dbCommon *precord); +static void putNotifyCommon(putNotify *ppn, dbCommon *precord); #define ellSafeAdd(list,listnode) \ { \ @@ -112,9 +112,9 @@ static void putNotifyInit(putNotify *ppn) { putNotifyPvt *pputNotifyPvt; - pputNotifyPvt = (putNotifyPvt *)ellFirst(&pnotifyGlobal->freeList); - if(pputNotifyPvt) { - ellDelete(&pnotifyGlobal->freeList,&pputNotifyPvt->node); + pputNotifyPvt = (putNotifyPvt *) ellFirst(&pnotifyGlobal->freeList); + if (pputNotifyPvt) { + ellDelete(&pnotifyGlobal->freeList, &pputNotifyPvt->node); } else { pputNotifyPvt = dbCalloc(1,sizeof(putNotifyPvt)); pputNotifyPvt->cancelEvent = epicsEventCreate(epicsEventEmpty); @@ -135,26 +135,26 @@ static void putNotifyInit(putNotify *ppn) static void putNotifyCleanup(putNotify *ppn) { - putNotifyPvt *pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; + putNotifyPvt *pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; pputNotifyPvt->state = putNotifyNotActive; - ellAdd(&pnotifyGlobal->freeList,&pputNotifyPvt->node); + ellAdd(&pnotifyGlobal->freeList, &pputNotifyPvt->node); ppn->pputNotifyPvt = 0; } - + static void restartCheck(putNotifyRecord *ppnr) { dbCommon *precord = ppnr->precord; putNotify *pfirst; putNotifyPvt *pputNotifyPvt; - + assert(precord->ppn); - pfirst = (putNotify *)ellFirst(&ppnr->restartList); - if(!pfirst) { + pfirst = (putNotify *) ellFirst(&ppnr->restartList); + if (!pfirst) { precord->ppn = 0; return; } - pputNotifyPvt = (putNotifyPvt *)pfirst->pputNotifyPvt; + pputNotifyPvt = (putNotifyPvt *) pfirst->pputNotifyPvt; assert(pputNotifyPvt->state==putNotifyWaitForRestart); /* remove pfirst from restartList */ ellSafeDelete(&ppnr->restartList,&pfirst->restartNode); @@ -165,25 +165,25 @@ static void restartCheck(putNotifyRecord *ppnr) callbackRequest(&pputNotifyPvt->callback); } -static void callUser(dbCommon *precord,putNotify *ppn) +static void callUser(dbCommon *precord, putNotify *ppn) { - putNotifyPvt *pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; + putNotifyPvt *pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); (*ppn->userCallback)(ppn); epicsMutexMustLock(pnotifyGlobal->lock); - if(pputNotifyPvt->cancelWait && pputNotifyPvt->userCallbackWait) { + if (pputNotifyPvt->cancelWait && pputNotifyPvt->userCallbackWait) { errlogPrintf("%s putNotify: both cancelWait and userCallbackWait true." - "This is illegal\n",precord->name); + "This is illegal\n", precord->name); pputNotifyPvt->cancelWait = pputNotifyPvt->userCallbackWait = 0; } - if(!pputNotifyPvt->cancelWait && !pputNotifyPvt->userCallbackWait) { + if (!pputNotifyPvt->cancelWait && !pputNotifyPvt->userCallbackWait) { putNotifyCleanup(ppn); - epicsMutexUnlock(pnotifyGlobal->lock); + epicsMutexUnlock(pnotifyGlobal->lock); return; } - if(pputNotifyPvt->cancelWait) { + if (pputNotifyPvt->cancelWait) { pputNotifyPvt->cancelWait = 0; epicsEventSignal(pputNotifyPvt->cancelEvent); epicsMutexUnlock(pnotifyGlobal->lock); @@ -195,38 +195,37 @@ static void callUser(dbCommon *precord,putNotify *ppn) epicsMutexUnlock(pnotifyGlobal->lock); return; } - -static void putNotifyCommon(putNotify *ppn,dbCommon *precord) -{ - long status=0; - dbFldDes *pfldDes = ppn->paddr->pfldDes; - putNotifyPvt *pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; - if(precord->ppn && pputNotifyPvt->state!=putNotifyRestartCallbackRequested) - { /*another putNotify owns the record */ - pputNotifyPvt->state = putNotifyWaitForRestart; +static void putNotifyCommon(putNotify *ppn, dbCommon *precord) +{ + long status; + putNotifyPvt *pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + + if (precord->ppn && + pputNotifyPvt->state != putNotifyRestartCallbackRequested) { /*another putNotify owns the record */ + pputNotifyPvt->state = putNotifyWaitForRestart; ellSafeAdd(&precord->ppnr->restartList,&ppn->restartNode); epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); return; - } else if(precord->ppn){ - assert(precord->ppn==ppn); + } else if (precord->ppn) { + assert(precord->ppn == ppn); assert(pputNotifyPvt->state==putNotifyRestartCallbackRequested); } - if(precord->pact) { + if (precord->pact) { precord->ppn = ppn; ellSafeAdd(&pputNotifyPvt->waitList,&precord->ppnr->waitNode); - pputNotifyPvt->state = putNotifyRestartInProgress; + pputNotifyPvt->state = putNotifyRestartInProgress; epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); return; } - status=dbPut(ppn->paddr,ppn->dbrType,ppn->pbuffer,ppn->nRequest); - ppn->status = (status==0) ? putNotifyOK : putNotifyError; + status = dbChannelPut(ppn->chan, ppn->dbrType, ppn->pbuffer, ppn->nRequest); + ppn->status = (status == 0) ? putNotifyOK : putNotifyError; /* Check to see if dbProcess should not be called */ - if(!status /*dont process if dbPut returned error */ - &&((ppn->paddr->pfield==(void *)&precord->proc) /*If PROC call dbProcess*/ - || (pfldDes->process_passive && precord->scan==0))) { + if (!status /*dont process if dbPut returned error */ + && ((dbChannelField(ppn->chan) == (void *) & precord->proc) /*If PROC call dbProcess*/ + || (dbChannelFldDes(ppn->chan)->process_passive && precord->scan == 0))) { precord->ppn = ppn; ellSafeAdd(&pputNotifyPvt->waitList,&precord->ppnr->waitNode); pputNotifyPvt->state = putNotifyPutInProgress; @@ -235,31 +234,31 @@ static void putNotifyCommon(putNotify *ppn,dbCommon *precord) dbScanUnlock(precord); return; } - if(pputNotifyPvt->state==putNotifyRestartCallbackRequested) { + if (pputNotifyPvt->state == putNotifyRestartCallbackRequested) { restartCheck(precord->ppnr); } pputNotifyPvt->state = putNotifyUserCallbackActive; assert(precord->ppn!=ppn); - callUser(precord,ppn); + callUser(precord, ppn); } - + static void notifyCallback(CALLBACK *pcallback) { - putNotify *ppn=NULL; - dbCommon *precord; + putNotify *ppn = NULL; + dbCommon *precord; putNotifyPvt *pputNotifyPvt; callbackGetUser(ppn,pcallback); - pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; - precord = ppn->paddr->precord; + pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + precord = dbChannelRecord(ppn->chan); dbScanLock(precord); epicsMutexMustLock(pnotifyGlobal->lock); assert(precord->ppnr); assert(pputNotifyPvt->state==putNotifyRestartCallbackRequested - || pputNotifyPvt->state==putNotifyUserCallbackRequested); + || pputNotifyPvt->state==putNotifyUserCallbackRequested); assert(ellCount(&pputNotifyPvt->waitList)==0); - if(pputNotifyPvt->cancelWait) { - if(pputNotifyPvt->state==putNotifyRestartCallbackRequested) { + if (pputNotifyPvt->cancelWait) { + if (pputNotifyPvt->state == putNotifyRestartCallbackRequested) { restartCheck(precord->ppnr); } epicsEventSignal(pputNotifyPvt->cancelEvent); @@ -267,58 +266,61 @@ static void notifyCallback(CALLBACK *pcallback) dbScanUnlock(precord); return; } - if(pputNotifyPvt->state==putNotifyRestartCallbackRequested) { - putNotifyCommon(ppn,precord); + if (pputNotifyPvt->state == putNotifyRestartCallbackRequested) { + putNotifyCommon(ppn, precord); return; } /* All done. Clean up and call userCallback */ pputNotifyPvt->state = putNotifyUserCallbackActive; assert(precord->ppn!=ppn); - callUser(precord,ppn); + callUser(precord, ppn); } void epicsShareAPI dbPutNotifyInit(void) { - if(pnotifyGlobal) return; + if (pnotifyGlobal) + return; pnotifyGlobal = dbCalloc(1,sizeof(notifyGlobal)); pnotifyGlobal->lock = epicsMutexMustCreate(); ellInit(&pnotifyGlobal->freeList); } - + void epicsShareAPI dbPutNotify(putNotify *ppn) { - dbCommon *precord = ppn->paddr->precord; - short dbfType = ppn->paddr->field_type; - long status=0; + struct dbChannel *chan = ppn->chan; + dbCommon *precord = dbChannelRecord(chan); + short dbfType = dbChannelFieldType(chan); + long status = 0; putNotifyPvt *pputNotifyPvt; assert(precord); /*check for putField disabled*/ - if(precord->disp) { - if((void *)(&precord->disp) != ppn->paddr->pfield) { - ppn->status = putNotifyPutDisabled; - (*ppn->userCallback)(ppn); - return; + if (precord->disp) { + if (dbChannelField(chan) != (void *) & precord->disp) { + ppn->status = putNotifyPutDisabled; + (*ppn->userCallback)(ppn); + return; } } /* Must handle DBF_XXXLINKs as special case. - * Only dbPutField will change link fields. + * Only dbPutField will change link fields. * Also the record is not processed as a result - */ - if(dbfType>=DBF_INLINK && dbfType<=DBF_FWDLINK) { - status=dbPutField(ppn->paddr,ppn->dbrType,ppn->pbuffer,ppn->nRequest); - ppn->status = (status==0) ? putNotifyOK : putNotifyError; + */ + if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) { + status = dbChannelPutField(ppn->chan, ppn->dbrType, ppn->pbuffer, + ppn->nRequest); + ppn->status = (status == 0) ? putNotifyOK : putNotifyError; (*ppn->userCallback)(ppn); return; } dbScanLock(precord); epicsMutexMustLock(pnotifyGlobal->lock); - pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; - if(pputNotifyPvt && (pputNotifyPvt->magic!=MAGIC)) { + pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + if (pputNotifyPvt && (pputNotifyPvt->magic != MAGIC)) { printf("dbPutNotify:pputNotifyPvt was not initialized\n"); pputNotifyPvt = 0; } - if(pputNotifyPvt) { + if (pputNotifyPvt) { assert(pputNotifyPvt->state==putNotifyUserCallbackActive); pputNotifyPvt->userCallbackWait = 1; epicsMutexUnlock(pnotifyGlobal->lock); @@ -328,38 +330,38 @@ void epicsShareAPI dbPutNotify(putNotify *ppn) epicsMutexMustLock(pnotifyGlobal->lock); putNotifyCleanup(ppn); } - pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; + pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; assert(!pputNotifyPvt); putNotifyInit(ppn); - pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; - if(!precord->ppnr) {/* make sure record has a putNotifyRecord*/ + pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + if (!precord->ppnr) {/* make sure record has a putNotifyRecord*/ precord->ppnr = dbCalloc(1,sizeof(putNotifyRecord)); precord->ppnr->precord = precord; ellInit(&precord->ppnr->restartList); } - putNotifyCommon(ppn,precord); + putNotifyCommon(ppn, precord); } - + void epicsShareAPI dbNotifyCancel(putNotify *ppn) { - dbCommon *precord = ppn->paddr->precord; + dbCommon *precord = dbChannelRecord(ppn->chan); putNotifyState state; putNotifyPvt *pputNotifyPvt; assert(precord); dbScanLock(precord); epicsMutexMustLock(pnotifyGlobal->lock); - pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; - if(!pputNotifyPvt || pputNotifyPvt->state==putNotifyNotActive) { + pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + if (!pputNotifyPvt || pputNotifyPvt->state == putNotifyNotActive) { epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); return; } state = pputNotifyPvt->state; /*If callback is scheduled or active wait for it to complete*/ - if(state==putNotifyUserCallbackRequested - || state==putNotifyRestartCallbackRequested - || state==putNotifyUserCallbackActive) { + if (state == putNotifyUserCallbackRequested || state + == putNotifyRestartCallbackRequested || state + == putNotifyUserCallbackActive) { pputNotifyPvt->cancelWait = 1; epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); @@ -369,24 +371,26 @@ void epicsShareAPI dbNotifyCancel(putNotify *ppn) epicsMutexUnlock(pnotifyGlobal->lock); return; } - switch(state) { - case putNotifyNotActive: break; + switch (state) { + case putNotifyNotActive: + break; case putNotifyWaitForRestart: assert(precord->ppn); assert(precord->ppn!=ppn); - ellSafeDelete(&precord->ppnr->restartList,&ppn->restartNode); + ellSafeDelete(&precord->ppnr->restartList,&ppn->restartNode) + ; break; case putNotifyRestartInProgress: - case putNotifyPutInProgress: - { /*Take all records out of wait list */ - putNotifyRecord *ppnrWait; + case putNotifyPutInProgress: { /*Take all records out of wait list */ + putNotifyRecord *ppnrWait; - while((ppnrWait = (putNotifyRecord *)ellFirst(&pputNotifyPvt->waitList))){ - ellSafeDelete(&pputNotifyPvt->waitList,&ppnrWait->waitNode); - restartCheck(ppnrWait); - } + while ((ppnrWait = (putNotifyRecord *) ellFirst(&pputNotifyPvt->waitList))) { + ellSafeDelete(&pputNotifyPvt->waitList,&ppnrWait->waitNode); + restartCheck(ppnrWait); } - if(precord->ppn==ppn) restartCheck(precord->ppnr); + } + if (precord->ppn == ppn) + restartCheck(precord->ppnr); break; default: printf("dbNotify: illegal state for notifyCallback\n"); @@ -396,29 +400,29 @@ void epicsShareAPI dbNotifyCancel(putNotify *ppn) epicsMutexUnlock(pnotifyGlobal->lock); dbScanUnlock(precord); } - + void epicsShareAPI dbNotifyCompletion(dbCommon *precord) { - putNotify *ppn = precord->ppn; + putNotify *ppn = precord->ppn; putNotifyPvt *pputNotifyPvt; epicsMutexMustLock(pnotifyGlobal->lock); assert(ppn); assert(precord->ppnr); - pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; - if(pputNotifyPvt->state!=putNotifyRestartInProgress - && pputNotifyPvt->state!=putNotifyPutInProgress) { + pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + if (pputNotifyPvt->state != putNotifyRestartInProgress + && pputNotifyPvt->state != putNotifyPutInProgress) { epicsMutexUnlock(pnotifyGlobal->lock); return; } ellSafeDelete(&pputNotifyPvt->waitList,&precord->ppnr->waitNode); - if((ellCount(&pputNotifyPvt->waitList)!=0)) { + if ((ellCount(&pputNotifyPvt->waitList)!=0)) { restartCheck(precord->ppnr); - } else if(pputNotifyPvt->state == putNotifyPutInProgress) { + } else if (pputNotifyPvt->state == putNotifyPutInProgress) { pputNotifyPvt->state = putNotifyUserCallbackRequested; restartCheck(precord->ppnr); callbackRequest(&pputNotifyPvt->callback); - } else if(pputNotifyPvt->state == putNotifyRestartInProgress) { + } else if (pputNotifyPvt->state == putNotifyRestartInProgress) { pputNotifyPvt->state = putNotifyRestartCallbackRequested; callbackRequest(&pputNotifyPvt->callback); } else { @@ -432,142 +436,140 @@ void epicsShareAPI dbNotifyAdd(dbCommon *pfrom, dbCommon *pto) putNotify *ppn = pfrom->ppn; putNotifyPvt *pputNotifyPvt; - if(pto->pact) return; /*if active it will not be processed*/ + if (pto->pact) + return; /*if active it will not be processed*/ epicsMutexMustLock(pnotifyGlobal->lock); - if(!pto->ppnr) {/* make sure record has a putNotifyRecord*/ + if (!pto->ppnr) {/* make sure record has a putNotifyRecord*/ pto->ppnr = dbCalloc(1,sizeof(putNotifyRecord)); pto->ppnr->precord = pto; ellInit(&pto->ppnr->restartList); } assert(ppn); - pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; - if(!(pto->ppn) - && (pputNotifyPvt->state==putNotifyPutInProgress) - && (pto!=ppn->paddr->precord)) { + pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + if (!pto->ppn && + (pputNotifyPvt->state == putNotifyPutInProgress) && + (pto != dbChannelRecord(ppn->chan))) { putNotifyPvt *pputNotifyPvt; pto->ppn = pfrom->ppn; - pputNotifyPvt = (putNotifyPvt *)pfrom->ppn->pputNotifyPvt; + pputNotifyPvt = (putNotifyPvt *) pfrom->ppn->pputNotifyPvt; ellSafeAdd(&pputNotifyPvt->waitList,&pto->ppnr->waitNode); } epicsMutexUnlock(pnotifyGlobal->lock); } - + typedef struct tpnInfo { epicsEventId callbackDone; - putNotify *ppn; -}tpnInfo; + putNotify *ppn; +} tpnInfo; static void dbtpnCallback(putNotify *ppn) { putNotifyStatus status = ppn->status; - tpnInfo *ptpnInfo = (tpnInfo *)ppn->usrPvt; + tpnInfo *ptpnInfo = (tpnInfo *) ppn->usrPvt; + const char *pname = dbChannelRecord(ppn->chan)->name; - if(status==0) - printf("dbtpnCallback: success record=%s\n",ppn->paddr->precord->name); + if (status == 0) + printf("dbtpnCallback: success record=%s\n", pname); else - printf("%s dbtpnCallback putNotify.status %d\n",ppn->paddr->precord->name,(int)status); + printf("%s dbtpnCallback putNotify.status %d\n", + pname, (int) status); epicsEventSignal(ptpnInfo->callbackDone); } static void tpnThread(void *pvt) { - tpnInfo *ptpnInfo = (tpnInfo *)pvt; - putNotify *ppn = (putNotify *)ptpnInfo->ppn; + tpnInfo *ptpnInfo = (tpnInfo *) pvt; + putNotify *ppn = (putNotify *) ptpnInfo->ppn; dbPutNotify(ppn); epicsEventWait(ptpnInfo->callbackDone); dbNotifyCancel(ppn); epicsEventDestroy(ptpnInfo->callbackDone); - free((void *)ppn->paddr); + free(ppn->pbuffer); + dbChannelDelete(ppn->chan); free(ppn); free(ptpnInfo); } -long epicsShareAPI dbtpn(char *pname,char *pvalue) +long epicsShareAPI dbtpn(char *pname, char *pvalue) { - long status; - tpnInfo *ptpnInfo; - DBADDR *pdbaddr=NULL; - putNotify *ppn=NULL; - char *psavevalue; - int len; + struct dbChannel *chan; + tpnInfo *ptpnInfo; + putNotify *ppn; + char *pbuffer; - len = strlen(pvalue); - /*allocate space for value immediately following DBADDR*/ - pdbaddr = dbCalloc(1,sizeof(DBADDR) + len+1); - psavevalue = (char *)(pdbaddr + 1); - strcpy(psavevalue,pvalue); - status = dbNameToAddr(pname,pdbaddr); - if(status) { - errMessage(status, "dbtpn: dbNameToAddr"); - free((void *)pdbaddr); - return(-1); + pbuffer = strdup(pvalue); + chan = dbChannelCreate(pname); + if (!chan) { + printf("dbtpn: No such channel"); + return -1; } - ppn = dbCalloc(1,sizeof(putNotify)); - ppn->paddr = pdbaddr; - ppn->pbuffer = psavevalue; + + ppn = dbCalloc(1, sizeof(putNotify)); + ppn->chan = chan; + ppn->pbuffer = pbuffer; ppn->nRequest = 1; ppn->dbrType = DBR_STRING; ppn->userCallback = dbtpnCallback; - ptpnInfo = dbCalloc(1,sizeof(tpnInfo)); + ptpnInfo = dbCalloc(1, sizeof(tpnInfo)); ptpnInfo->ppn = ppn; ptpnInfo->callbackDone = epicsEventCreate(epicsEventEmpty); ppn->usrPvt = ptpnInfo; - epicsThreadCreate("dbtpn",epicsThreadPriorityHigh, - epicsThreadGetStackSize(epicsThreadStackMedium), - tpnThread,ptpnInfo); - return(0); + epicsThreadCreate("dbtpn", epicsThreadPriorityHigh, + epicsThreadGetStackSize(epicsThreadStackMedium), tpnThread, ptpnInfo); + return 0; } - + int epicsShareAPI dbNotifyDump(void) { epicsMutexLockStatus lockStatus; dbRecordType *pdbRecordType; dbRecordNode *pdbRecordNode; - dbCommon *precord; - putNotify *ppn; - putNotify *ppnRestart; + dbCommon *precord; + putNotify *ppn; + putNotify *ppnRestart; putNotifyRecord *ppnrWait; int itry; - - - for(itry=0; itry<100; itry++) { + + for (itry = 0; itry < 100; itry++) { lockStatus = epicsMutexTryLock(pnotifyGlobal->lock); - if(lockStatus==epicsMutexLockOK) break; + if (lockStatus == epicsMutexLockOK) + break; epicsThreadSleep(.05); } - for(pdbRecordType = (dbRecordType *)ellFirst(&pdbbase->recordTypeList); - pdbRecordType; - pdbRecordType = (dbRecordType *)ellNext(&pdbRecordType->node)) { - for (pdbRecordNode=(dbRecordNode *)ellFirst(&pdbRecordType->recList); - pdbRecordNode; - pdbRecordNode = (dbRecordNode *)ellNext(&pdbRecordNode->node)) { + for (pdbRecordType = (dbRecordType *) ellFirst(&pdbbase->recordTypeList); pdbRecordType; pdbRecordType + = (dbRecordType *) ellNext(&pdbRecordType->node)) { + for (pdbRecordNode = (dbRecordNode *) ellFirst(&pdbRecordType->recList); pdbRecordNode; pdbRecordNode + = (dbRecordNode *) ellNext(&pdbRecordNode->node)) { putNotifyPvt *pputNotifyPvt; precord = pdbRecordNode->precord; - if (!precord->name[0] || - pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) + if (!precord->name[0] || pdbRecordNode->flags & DBRN_FLAGS_ISALIAS) + continue; + if (!precord->ppn) + continue; + if (!precord->ppnr) + continue; + if (dbChannelRecord(precord->ppn->chan) != precord) continue; - if(!precord->ppn) continue; - if(!precord->ppnr) continue; - if(precord->ppn->paddr->precord != precord) continue; ppn = precord->ppn; - pputNotifyPvt = (putNotifyPvt *)ppn->pputNotifyPvt; - printf("%s state %d ppn %p\n waitList\n", - precord->name,pputNotifyPvt->state,(void*)ppn); - ppnrWait = (putNotifyRecord *)ellFirst(&pputNotifyPvt->waitList); - while(ppnrWait) { - printf(" %s pact %d\n", - ppnrWait->precord->name,ppnrWait->precord->pact); - ppnrWait = (putNotifyRecord *)ellNext(&ppnrWait->waitNode.node); + pputNotifyPvt = (putNotifyPvt *) ppn->pputNotifyPvt; + printf("%s state %d ppn %p\n waitList\n", precord->name, + pputNotifyPvt->state, (void*) ppn); + ppnrWait = (putNotifyRecord *) ellFirst(&pputNotifyPvt->waitList); + while (ppnrWait) { + printf(" %s pact %d\n", ppnrWait->precord->name, + ppnrWait->precord->pact); + ppnrWait = (putNotifyRecord *) ellNext(&ppnrWait->waitNode.node); } printf(" restartList\n"); - ppnRestart = (putNotify *)ellFirst(&precord->ppnr->restartList); - while(ppnRestart) { - printf(" %p\n", (void *)ppnRestart); - ppnRestart = (putNotify *)ellNext(&ppnRestart->restartNode.node); + ppnRestart = (putNotify *) ellFirst(&precord->ppnr->restartList); + while (ppnRestart) { + printf(" %p\n", (void *) ppnRestart); + ppnRestart = (putNotify *) ellNext(&ppnRestart->restartNode.node); } } } - if(lockStatus==epicsMutexLockOK) epicsMutexUnlock(pnotifyGlobal->lock); - return(0); + if (lockStatus == epicsMutexLockOK) + epicsMutexUnlock(pnotifyGlobal->lock); + return (0); } diff --git a/src/ioc/db/dbNotify.h b/src/ioc/db/dbNotify.h index 5c57c0d90..b7ab1460f 100644 --- a/src/ioc/db/dbNotify.h +++ b/src/ioc/db/dbNotify.h @@ -5,7 +5,7 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbNotify.h */ @@ -18,12 +18,12 @@ #include "callback.h" #ifdef __cplusplus - /* for brain dead C++ compilers */ - struct dbCommon; - struct putNotify; extern "C" { #endif - + +struct dbCommon; +struct putNotify; + typedef struct ellCheckNode{ ELLNODE node; int isOnList; @@ -40,7 +40,7 @@ typedef struct putNotify{ ellCheckNode restartNode; /*The following members MUST be set by user*/ void (*userCallback)(struct putNotify *); - struct dbAddr *paddr; /*dbAddr set by dbNameToAddr*/ + struct dbChannel *chan; /*dbChannel*/ void *pbuffer; /*address of data*/ long nRequest; /*number of elements to be written*/ short dbrType; /*database request type*/ @@ -55,7 +55,7 @@ typedef struct putNotify{ epicsShareFunc void epicsShareAPI dbPutNotify(putNotify *pputNotify); epicsShareFunc void epicsShareAPI dbNotifyCancel(putNotify *pputNotify); -/*dbPutNotifyMapType convience function for old database access*/ +/*dbPutNotifyMapType convenience function for old database access*/ epicsShareFunc int epicsShareAPI dbPutNotifyMapType (putNotify *ppn, short oldtype); /* dbPutNotifyInit called by iocInit */ @@ -70,9 +70,9 @@ epicsShareFunc void epicsShareAPI dbNotifyCompletion(struct dbCommon *precord); /* dbtpn is test routine for put notify */ epicsShareFunc long epicsShareAPI dbtpn(char *recordname,char *value); -/* dbNotifyDump is an INVASIVE debug utility. Dont use this needlessly*/ +/* dbNotifyDump is an INVASIVE debug utility. Don't use this needlessly*/ epicsShareFunc int epicsShareAPI dbNotifyDump(void); - + /* This module provides code to handle put notify. If a put causes a record to * be processed, then a user supplied callback is called when that record * and all records processed because of that record complete processing. @@ -87,8 +87,8 @@ epicsShareFunc int epicsShareAPI dbNotifyDump(void); * * After dbPutNotify is called it may not called for the same putNotify * until the putCallback is complete. The use can call dbNotifyCancel - * to cancel the operation. - * + * to cancel the operation. + * * The user callback is called when the operation is completed. * * The other global routines (dbNotifyAdd and dbNotifyCompletion) are called by: diff --git a/src/ioc/db/dbPutNotifyBlocker.cpp b/src/ioc/db/dbPutNotifyBlocker.cpp index e91234b67..7acebdbd5 100644 --- a/src/ioc/db/dbPutNotifyBlocker.cpp +++ b/src/ioc/db/dbPutNotifyBlocker.cpp @@ -5,21 +5,21 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ -/* +/* * $Revision-Id$ * - * + * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 - * + * * Copyright, 1986, The Regents of the University of California. - * - * - * Author: + * + * + * Author: * Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 @@ -47,7 +47,7 @@ #include "dbPutNotifyBlocker.h" dbPutNotifyBlocker::dbPutNotifyBlocker ( epicsMutex & mutexIn ) : - mutex ( mutexIn ), pNotify ( 0 ), + mutex ( mutexIn ), pNotify ( 0 ), maxValueSize ( sizeof ( this->dbrScalarValue ) ) { memset ( & this->pn, '\0', sizeof ( this->pn ) ); @@ -55,7 +55,7 @@ dbPutNotifyBlocker::dbPutNotifyBlocker ( epicsMutex & mutexIn ) : this->pn.pbuffer = & this->dbrScalarValue; } -dbPutNotifyBlocker::~dbPutNotifyBlocker () +dbPutNotifyBlocker::~dbPutNotifyBlocker () { } @@ -70,7 +70,7 @@ void dbPutNotifyBlocker::destructor ( epicsGuard < epicsMutex > & guard ) this->~dbPutNotifyBlocker (); } -void dbPutNotifyBlocker::cancel ( +void dbPutNotifyBlocker::cancel ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); @@ -82,7 +82,7 @@ void dbPutNotifyBlocker::cancel ( this->block.signal (); } -void dbPutNotifyBlocker::expandValueBuf ( +void dbPutNotifyBlocker::expandValueBuf ( epicsGuard < epicsMutex > & guard, unsigned long newSize ) { guard.assertIdenticalMutex ( this->mutex ); @@ -127,9 +127,9 @@ extern "C" void putNotifyCompletion ( putNotify *ppn ) } } -void dbPutNotifyBlocker::initiatePutNotify ( - epicsGuard < epicsMutex > & guard, cacWriteNotify & notify, - struct dbAddr & addr, unsigned type, unsigned long count, +void dbPutNotifyBlocker::initiatePutNotify ( + epicsGuard < epicsMutex > & guard, cacWriteNotify & notify, + struct dbChannel * dbch, unsigned type, unsigned long count, const void * pValue ) { guard. assertIdenticalMutex ( this->mutex ); @@ -163,7 +163,7 @@ void dbPutNotifyBlocker::initiatePutNotify ( throw cacChannel::badType(); } - int status = dbPutNotifyMapType ( + int status = dbPutNotifyMapType ( &this->pn, static_cast ( type ) ); if ( status ) { this->pNotify = 0; @@ -171,7 +171,7 @@ void dbPutNotifyBlocker::initiatePutNotify ( } this->pn.nRequest = static_cast < unsigned > ( count ); - this->pn.paddr = &addr; + this->pn.chan = dbch; this->pn.userCallback = putNotifyCompletion; this->pn.usrPvt = this; @@ -191,22 +191,22 @@ void dbPutNotifyBlocker::show ( unsigned level ) const this->show ( guard, level ); } -void dbPutNotifyBlocker::show ( +void dbPutNotifyBlocker::show ( epicsGuard < epicsMutex > &, unsigned level ) const { - printf ( "put notify blocker at %p\n", + printf ( "put notify blocker at %p\n", static_cast ( this ) ); if ( level > 0u ) { this->block.show ( level - 1u ); } } -dbSubscriptionIO * dbPutNotifyBlocker::isSubscription () +dbSubscriptionIO * dbPutNotifyBlocker::isSubscription () { return 0; } -void * dbPutNotifyBlocker::operator new ( size_t size, +void * dbPutNotifyBlocker::operator new ( size_t size, tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); @@ -220,7 +220,7 @@ void * dbPutNotifyBlocker::operator new ( size_t ) // X aCC 361 } #ifdef CXX_PLACEMENT_DELETE -void dbPutNotifyBlocker::operator delete ( void *pCadaver, +void dbPutNotifyBlocker::operator delete ( void *pCadaver, tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); diff --git a/src/ioc/db/dbPutNotifyBlocker.h b/src/ioc/db/dbPutNotifyBlocker.h index da2cc9fcb..bca0d715b 100644 --- a/src/ioc/db/dbPutNotifyBlocker.h +++ b/src/ioc/db/dbPutNotifyBlocker.h @@ -5,20 +5,20 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ -/* +/* * $Revision-Id$ * - * + * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 - * + * * Copyright, 1986, The Regents of the University of California. - * - * + * + * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 @@ -43,24 +43,24 @@ class dbPutNotifyBlocker : public dbBaseIO { public: dbPutNotifyBlocker ( epicsMutex & ); void destructor ( epicsGuard < epicsMutex > & ); - void initiatePutNotify ( epicsGuard < epicsMutex > &, - cacWriteNotify &, struct dbAddr &, + void initiatePutNotify ( epicsGuard < epicsMutex > &, + cacWriteNotify &, struct dbChannel *, unsigned type, unsigned long count, const void * pValue ); void cancel ( epicsGuard < epicsMutex > & ); void show ( epicsGuard < epicsMutex > &, unsigned level ) const; void show ( unsigned level ) const; - void * operator new ( size_t size, + void * operator new ( size_t size, tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & ); - epicsPlacementDeleteOperator (( void *, + epicsPlacementDeleteOperator (( void *, tsFreeList < dbPutNotifyBlocker, 64, epicsMutexNOOP > & )) private: putNotify pn; // - // Include a union of all scalar types + // Include a union of all scalar types // including fixed length strings so - // that in many cases we can avoid + // that in many cases we can avoid // allocating another buffer - // + // union { dbr_string_t strval; dbr_short_t shrtval; @@ -76,7 +76,7 @@ private: cacWriteNotify * pNotify; unsigned long maxValueSize; dbSubscriptionIO * isSubscription (); - void expandValueBuf ( + void expandValueBuf ( epicsGuard < epicsMutex > &, unsigned long newSize ); friend void putNotifyCompletion ( putNotify * ppn ); dbPutNotifyBlocker ( const dbPutNotifyBlocker & ); diff --git a/src/ioc/db/dbSubscriptionIO.cpp b/src/ioc/db/dbSubscriptionIO.cpp index 4fd190d3f..a53fd0086 100644 --- a/src/ioc/db/dbSubscriptionIO.cpp +++ b/src/ioc/db/dbSubscriptionIO.cpp @@ -4,20 +4,20 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ -/* +/* * $Revision-Id$ * - * + * * L O S A L A M O S * Los Alamos National Laboratory * Los Alamos, New Mexico 87545 - * + * * Copyright, 1986, The Regents of the University of California. - * - * + * + * * Author Jeffrey O. Hill * johill@lanl.gov * 505 665 1831 @@ -41,18 +41,18 @@ #include "dbChannelIO.h" #include "db_access_routines.h" -dbSubscriptionIO::dbSubscriptionIO ( +dbSubscriptionIO::dbSubscriptionIO ( epicsGuard < epicsMutex > & guard, epicsMutex & mutexIn, - dbContext &, dbChannelIO & chanIO, - dbAddr & addr, cacStateNotify & notifyIn, unsigned typeIn, + dbContext &, dbChannelIO & chanIO, + dbChannel * dbch, cacStateNotify & notifyIn, unsigned typeIn, unsigned long countIn, unsigned maskIn, dbEventCtx ctx ) : - mutex ( mutexIn ), count ( countIn ), notify ( notifyIn ), + mutex ( mutexIn ), count ( countIn ), notify ( notifyIn ), chan ( chanIO ), es ( 0 ), type ( typeIn ), id ( 0u ) { guard.assertIdenticalMutex ( this->mutex ); { epicsGuardRelease < epicsMutex > unguard ( guard ); - this->es = db_add_event ( ctx, & addr, + this->es = db_add_event ( ctx, dbch, dbSubscriptionEventCallback, (void *) this, maskIn ); if ( this->es == 0 ) { throw std::bad_alloc(); @@ -62,7 +62,7 @@ dbSubscriptionIO::dbSubscriptionIO ( } } -dbSubscriptionIO::~dbSubscriptionIO () +dbSubscriptionIO::~dbSubscriptionIO () { } @@ -72,7 +72,7 @@ void dbSubscriptionIO::destructor ( epicsGuard < epicsMutex > & guard ) this->~dbSubscriptionIO (); } -void dbSubscriptionIO::unsubscribe ( +void dbSubscriptionIO::unsubscribe ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); @@ -86,11 +86,11 @@ void dbSubscriptionIO::unsubscribe ( } } -void dbSubscriptionIO::channelDeleteException ( +void dbSubscriptionIO::channelDeleteException ( epicsGuard < epicsMutex > & guard ) { guard.assertIdenticalMutex ( this->mutex ); - this->notify.exception ( guard, ECA_CHANDESTROY, + this->notify.exception ( guard, ECA_CHANDESTROY, this->chan.pName(guard), this->type, this->count ); } @@ -112,21 +112,21 @@ void dbSubscriptionIO::operator delete ( void * ) __FILE__, __LINE__ ); } -void * dbSubscriptionIO::operator new ( size_t size, +void * dbSubscriptionIO::operator new ( size_t size, tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & freeList ) { return freeList.allocate ( size ); } #ifdef CXX_PLACEMENT_DELETE -void dbSubscriptionIO::operator delete ( void * pCadaver, +void dbSubscriptionIO::operator delete ( void * pCadaver, tsFreeList < dbSubscriptionIO, 256, epicsMutexNOOP > & freeList ) { freeList.release ( pCadaver ); } #endif -extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbAddr * /* paddr */, +extern "C" void dbSubscriptionEventCallback ( void *pPrivate, struct dbChannel * /* dbch */, int /* eventsRemaining */, struct db_field_log *pfl ) { dbSubscriptionIO * pIO = static_cast < dbSubscriptionIO * > ( pPrivate ); @@ -139,19 +139,19 @@ void dbSubscriptionIO::show ( unsigned level ) const this->show ( guard, level ); } -void dbSubscriptionIO::show ( +void dbSubscriptionIO::show ( epicsGuard < epicsMutex > & guard, unsigned level ) const { guard.assertIdenticalMutex ( this->mutex ); - printf ( "Data base subscription IO at %p\n", + printf ( "Data base subscription IO at %p\n", static_cast ( this ) ); if ( level > 0u ) { short tmpType; if ( this->type < SHRT_MAX ) { tmpType = static_cast < short > ( this->type ); printf ( "\ttype %s, count %lu, channel at %p\n", - dbf_type_to_text ( tmpType ), this->count, + dbf_type_to_text ( tmpType ), this->count, static_cast ( &this->chan ) ); } else { @@ -161,7 +161,7 @@ void dbSubscriptionIO::show ( } } -dbSubscriptionIO * dbSubscriptionIO::isSubscription () +dbSubscriptionIO * dbSubscriptionIO::isSubscription () { return this; } diff --git a/src/ioc/db/db_access.c b/src/ioc/db/db_access.c index 5596f1a0e..479d501fa 100644 --- a/src/ioc/db/db_access.c +++ b/src/ioc/db/db_access.c @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* $Revision-Id$ */ @@ -24,6 +24,7 @@ #include "epicsConvert.h" #include "dbDefs.h" +#include "dbChannel.h" #include "errlog.h" #include "ellLib.h" #include "epicsTime.h" @@ -42,10 +43,6 @@ #include "dbEvent.h" #include "db_access_routines.h" -#ifndef NULL -#define NULL 0 -#endif - #define oldDBF_STRING 0 #define oldDBF_INT 1 @@ -108,46 +105,26 @@ extern unsigned short dbDBRnewToDBRold[DBR_ENUM+1]; typedef char DBSTRING[MAX_STRING_SIZE]; -#ifndef MAX_STRING_SIZE -#define MAX_STRING_SIZE 40 -#endif - -/* - * DB_PROCESS - * - * process database records - */ -void db_process(struct dbAddr *paddr) +struct dbChannel * dbChannel_create(const char *pname) { - long status = dbProcess(paddr->precord); - - if (status) errMessage(status, "db_process failed"); -} - -/* - * DB_NAME_TO_ADDR - */ -int epicsShareAPI db_name_to_addr(const char *pname, struct dbAddr *paddr) -{ - long status; + dbChannel *chan = dbChannelCreate(pname); short ftype; - status = dbNameToAddr(pname, paddr); - if (!status) { - ftype = paddr->dbr_field_type; - if (INVALID_DB_REQ(ftype)) { - errlogPrintf("%s dbNameToAddr failed\n", pname); - return -2; - } - paddr->dbr_field_type = dbDBRnewToDBRold[ftype]; - return 0; + if (!chan) + return NULL; + + ftype = chan->addr.dbr_field_type; + if (INVALID_DB_REQ(ftype)) { + dbChannelDelete(chan); + return NULL; } - else - return -1; + + chan->addr.dbr_field_type = dbDBRnewToDBRold[ftype]; + return chan; } - -int epicsShareAPI db_get_field(struct dbAddr *paddr, - int buffer_type, void *pbuffer, int no_elements, void *pfl) + +int dbChannel_get(struct dbChannel *chan, + int buffer_type, void *pbuffer, long no_elements, void *pfl) { long nRequest = no_elements; int result = db_get_field_and_count( @@ -825,15 +802,14 @@ int epicsShareAPI db_get_field_and_count( if (status) return -1; return 0; } - -/* DB_PUT_FIELD put a field and convert it to the desired type */ -int epicsShareAPI db_put_field(struct dbAddr *paddr, int src_type, - const void *psrc, int no_elements) +int dbChannel_put(struct dbChannel *chan, int src_type, + const void *psrc, long no_elements) { + dbAddr *paddr = & chan->addr; long status; - switch(src_type) { + switch (src_type) { case(oldDBR_STRING): status = dbPutField(paddr, DBR_STRING, psrc, no_elements); break; @@ -993,7 +969,7 @@ int epicsShareAPI db_put_field(struct dbAddr *paddr, int src_type, return 0; } - + epicsShareFunc int epicsShareAPI dbPutNotifyMapType (putNotify *ppn, short oldtype) { switch(oldtype) { diff --git a/src/ioc/db/db_access_routines.h b/src/ioc/db/db_access_routines.h index a1f445c6f..efe20b95c 100644 --- a/src/ioc/db/db_access_routines.h +++ b/src/ioc/db/db_access_routines.h @@ -5,7 +5,7 @@ * Operator of Los Alamos National Laboratory. * EPICS BASE Versions 3.13.7 * and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* base/include/db_access_routines.h */ @@ -22,23 +22,19 @@ extern "C" { #endif #include "shareLib.h" -#include "dbAddr.h" epicsShareExtern struct dbBase *pdbbase; epicsShareExtern volatile int interruptAccept; /* - * old db access API - * (included here because these routines use dbAccess.h and their - * prototypes must also be included in db_access.h) + * Adaptors for db_access users */ -epicsShareFunc int epicsShareAPI db_name_to_addr( - const char *pname, DBADDR *paddr); -epicsShareFunc int epicsShareAPI db_put_field( - DBADDR *paddr, int src_type,const void *psrc, int no_elements); -epicsShareFunc int epicsShareAPI db_get_field( - DBADDR *paddr, int dest_type,void *pdest, int no_elements, void *pfl); +epicsShareFunc struct dbChannel * dbChannel_create(const char *pname); +epicsShareFunc int dbChannel_get(struct dbChannel *chan, + int buffer_type, void *pbuffer, long no_elements, void *pfl); +epicsShareFunc int dbChannel_put(struct dbChannel *chan, int src_type, + const void *psrc, long no_elements); epicsShareFunc int epicsShareAPI db_get_field_and_count( struct dbAddr *paddr, int buffer_type, void *pbuffer, long *nRequest, void *pfl); diff --git a/src/ioc/db/db_test.c b/src/ioc/db/db_test.c index 0ad5ad2e2..57c325907 100644 --- a/src/ioc/db/db_test.c +++ b/src/ioc/db/db_test.c @@ -1,12 +1,12 @@ /*************************************************************************\ * Copyright (c) 2002 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. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. -\*************************************************************************/ + * National Laboratory. + * Copyright (c) 2002 The Regents of the University of California, as + * Operator of Los Alamos National Laboratory. + * EPICS BASE Versions 3.13.7 + * and higher are distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + \*************************************************************************/ /* share/src/db @(#)db_test.c 1.10 2/3/94 */ /* database access subroutines */ /* @@ -23,706 +23,230 @@ #include "cadef.h" #include "epicsStdioRedirect.h" #define epicsExportSharedSymbols +#include "dbChannel.h" #include "db_access_routines.h" #include "dbNotify.h" #include "db_test.h" #include "dbCommon.h" - #define MAX_ELEMS 10 -int epicsShareAPI gft(char *pname) + +int gft(char *pname) { - char tgf_buffer[MAX_ELEMS*MAX_STRING_SIZE+sizeof(struct dbr_ctrl_double)]; - struct dbAddr addr; - struct dbAddr *paddr = &addr; - short number_elements; - int i; - int status; + char tgf_buffer[MAX_ELEMS*MAX_STRING_SIZE + sizeof(struct dbr_ctrl_double)]; + struct dbChannel *chan; + struct dbCommon *precord; + long elements; + short type; + int i; - if (pname==0 || ((*pname < ' ') || (*pname > 'z'))) { - printf("\nusage \"pv name\"\n"); - return(1); - } - /* convert name to database address */ - status=db_name_to_addr(pname,&addr); - if(status) { - printf("db_name_to_addr failed\n"); - return(1); - } - printf(" Record Name: %s\n",pname); - printf("Record Address: 0x%p\n",(void *)addr.precord); - printf(" Field Type: %d\n",addr.dbr_field_type); - printf(" Field Address: 0x%p\n",addr.pfield); - printf(" Field Size: %d\n",addr.field_size); - printf(" No Elements: %ld\n",addr.no_elements); - number_elements = - ((addr.no_elements > MAX_ELEMS)?MAX_ELEMS:addr.no_elements); + chan = dbChannel_create(pname); + if (!chan) { + printf("Channel couldn't be created\n"); + return 1; + } - for(i=0; i<=LAST_BUFFER_TYPE; i++) { - if(addr.dbr_field_type==0) { - if( (i!=DBR_STRING) - && (i!=DBR_STS_STRING) - && (i!=DBR_TIME_STRING) - && (i!=DBR_GR_STRING) - && (i!=DBR_CTRL_STRING)) continue; - } - if(db_get_field(paddr,i,tgf_buffer,number_elements,NULL)<0) - printf("\t%s Failed\n",dbr_text[i]); - else - ca_dump_dbr (i,number_elements,tgf_buffer); - } - return(0); + precord = dbChannelRecord(chan); + elements = dbChannelElements(chan); + type = dbChannelExportType(chan); + + printf(" Record Name: %s\n", precord->name); + printf("Record Address: 0x%p\n", precord); + printf(" Export Type: %d\n", type); + printf(" Field Address: 0x%p\n", dbChannelData(chan)); + printf(" Field Size: %d\n", dbChannelElementSize(chan)); + printf(" No Elements: %ld\n", elements); + + if (elements > MAX_ELEMS) + elements = MAX_ELEMS; + + for (i = 0; i <= LAST_BUFFER_TYPE; i++) { + if (type == 0) { + if ((i != DBR_STRING) && (i != DBR_STS_STRING) && + (i != DBR_TIME_STRING) && (i != DBR_GR_STRING) && + (i != DBR_CTRL_STRING)) + continue; + } + if (dbChannel_get(chan, i, tgf_buffer, elements, NULL) < 0) + printf("\t%s Failed\n", dbr_text[i]); + else + ca_dump_dbr(i, elements, tgf_buffer); + } + + dbChannelDelete(chan); + return 0; } - + /* * TPF * Test put field */ -int epicsShareAPI pft(char *pname,char *pvalue) +int epicsShareAPI pft(char *pname, char *pvalue) { - struct dbAddr addr; - struct dbAddr *paddr = &addr; - char buffer[500]; - short shortvalue; - long longvalue; - float floatvalue; - unsigned char charvalue; - double doublevalue; - int status; + struct dbChannel *chan; + struct dbCommon *precord; + long elements; + short type; + char buffer[500]; + short shortvalue; + long longvalue; + float floatvalue; + unsigned char charvalue; + double doublevalue; - if (pname==0 || pvalue==0 - || ((*pname < ' ') || (*pname > 'z')) - || ((*pvalue < ' ') || (*pvalue > 'z'))){ - printf("\nusage \"pv name\",\"value\"\n"); - return(1); - } + chan = dbChannel_create(pname); + if (!chan) { + printf("Channel couldn't be created\n"); + return 1; + } - /* convert name to database address */ + precord = dbChannelRecord(chan); + elements = dbChannelElements(chan); + type = dbChannelExportType(chan); - /* convert name to database address */ - status=db_name_to_addr(pname,&addr); - if(status) { - printf("db_name_to_addr failed\n"); - return(1); - } - printf(" Record Name: %s\n",pname); - printf("Record Address: 0x%p\n",(void *)addr.precord); - printf(" Field Type: %d\n",addr.dbr_field_type); - printf(" Field Address: 0x%p\n",addr.pfield); - printf(" Field Size: %d\n",addr.field_size); - printf(" No Elements: %ld\n",addr.no_elements); - if (db_put_field(paddr,DBR_STRING,pvalue,1) < 0) printf("\n\t failed "); - if (db_get_field(paddr,DBR_STRING,buffer,1,NULL) < 0) printf("\n\tfailed"); - else ca_dump_dbr(DBR_STRING,1,buffer); - if(addr.dbr_field_type<=DBF_STRING || addr.dbr_field_type==DBF_ENUM) - return(0); - if(sscanf(pvalue,"%hd",&shortvalue)==1) { - if (db_put_field(paddr,DBR_SHORT,&shortvalue,1) < 0) - printf("\n\t SHORT failed "); - if (db_get_field(paddr,DBR_SHORT,buffer,1,NULL) < 0) - printf("\n\t SHORT GET failed"); - else ca_dump_dbr(DBR_SHORT,1,buffer); - } - if(sscanf(pvalue,"%ld",&longvalue)==1) { - if (db_put_field(paddr,DBR_LONG,&longvalue,1) < 0) - printf("\n\t LONG failed "); - if (db_get_field(paddr,DBR_LONG,buffer,1,NULL) < 0) - printf("\n\t LONG GET failed"); - else ca_dump_dbr(DBR_LONG,1,buffer); - } - if(epicsScanFloat(pvalue, &floatvalue)==1) { - if (db_put_field(paddr,DBR_FLOAT,&floatvalue,1) < 0) - printf("\n\t FLOAT failed "); - if (db_get_field(paddr,DBR_FLOAT,buffer,1,NULL) < 0) - printf("\n\t FLOAT GET failed"); - else ca_dump_dbr(DBR_FLOAT,1,buffer); - } - if(epicsScanFloat(pvalue, &floatvalue)==1) { - doublevalue=floatvalue; - if (db_put_field(paddr,DBR_DOUBLE,&doublevalue,1) < 0) - printf("\n\t DOUBLE failed "); - if (db_get_field(paddr,DBR_DOUBLE,buffer,1,NULL) < 0) - printf("\n\t DOUBLE GET failed"); - else ca_dump_dbr(DBR_DOUBLE,1,buffer); - } - if(sscanf(pvalue,"%hd",&shortvalue)==1) { - charvalue=(unsigned char)shortvalue; - if (db_put_field(paddr,DBR_CHAR,&charvalue,1) < 0) - printf("\n\t CHAR failed "); - if (db_get_field(paddr,DBR_CHAR,buffer,1,NULL) < 0) - printf("\n\t CHAR GET failed"); - else ca_dump_dbr(DBR_CHAR,1,buffer); - } - if(sscanf(pvalue,"%hd",&shortvalue)==1) { - if (db_put_field(paddr,DBR_ENUM,&shortvalue,1) < 0) - printf("\n\t ENUM failed "); - if (db_get_field(paddr,DBR_ENUM,buffer,1,NULL) < 0) - printf("\n\t ENUM GET failed"); - else ca_dump_dbr(DBR_ENUM,1,buffer); - } - printf("\n"); - return(0); -} + printf(" Record Name: %s\n", precord->name); + printf("Record Address: 0x%p\n", precord); + printf(" Export Type: %d\n", type); + printf(" Field Address: 0x%p\n", dbChannelData(chan)); + printf(" Field Size: %d\n", dbChannelElementSize(chan)); + printf(" No Elements: %ld\n", elements); -#if 0 -/* - * PRINT_RETURNED - * - * print out the values in a database access interface structure - */ -static void print_returned(type,count,pbuffer) - short type; - char *pbuffer; - short count; -{ - short i; + if (dbChannel_put(chan, DBR_STRING,pvalue, 1) < 0) + printf("\n\t failed "); + if (dbChannel_get(chan, DBR_STRING,buffer, 1, NULL) < 0) + printf("\n\tfailed"); + else + ca_dump_dbr(DBR_STRING,1, buffer); - printf("%s\t",dbr_text[type]); - switch(type){ - case (DBR_STRING): - for(i=0; istatus,pvalue->severity); - printf("\tValue: %s",pvalue->value); - break; - } - case (DBR_STS_ENUM): - case (DBR_STS_SHORT): - { - struct dbr_sts_short *pvalue - = (struct dbr_sts_short *)pbuffer; - short *pshort = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pshort++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",*pshort); - } - break; - } - case (DBR_STS_FLOAT): - { - struct dbr_sts_float *pvalue - = (struct dbr_sts_float *)pbuffer; - float *pfloat = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pfloat++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%6.4f ",*pfloat); - } - break; - } - case (DBR_STS_CHAR): - { - struct dbr_sts_char *pvalue - = (struct dbr_sts_char *)pbuffer; - unsigned char *pchar = &pvalue->value; - short value; - - printf("%2d %2d",pvalue->status,pvalue->severity); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pchar++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - value=*pchar; - printf("%d ",value); - } - break; - } - case (DBR_STS_LONG): - { - struct dbr_sts_long *pvalue - = (struct dbr_sts_long *)pbuffer; - dbr_long_t *plong = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,plong++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",*plong); - } - break; - } - case (DBR_STS_DOUBLE): - { - struct dbr_sts_double *pvalue - = (struct dbr_sts_double *)pbuffer; - double *pdouble = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pdouble++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%6.4f ",(float)(*pdouble)); - } - break; - } - case (DBR_TIME_STRING): - { - struct dbr_time_string *pvalue - = (struct dbr_time_string *) pbuffer; - printf("%2d %2d",pvalue->status,pvalue->severity); - printf("\tTimeStamp: %u %u", - pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); - printf("\tValue: "); - printf("%s",pvalue->value); - break; - } - case (DBR_TIME_SHORT): - case (DBR_TIME_ENUM): - { - struct dbr_time_short *pvalue - = (struct dbr_time_short *)pbuffer; - short *pshort = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - printf("\tTimeStamp: %u %u", - pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pshort++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",*pshort); - } - break; - } - case (DBR_TIME_FLOAT): - { - struct dbr_time_float *pvalue - = (struct dbr_time_float *)pbuffer; - float *pfloat = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - printf("\tTimeStamp: %u %u", - pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pfloat++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%6.4f ",*pfloat); - } - break; - } - case (DBR_TIME_CHAR): - { - struct dbr_time_char *pvalue - = (struct dbr_time_char *)pbuffer; - unsigned char *pchar = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - printf("\tTimeStamp: %u %u", - pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pchar++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",(short)(*pchar)); - } - break; - } - case (DBR_TIME_LONG): - { - struct dbr_time_long *pvalue - = (struct dbr_time_long *)pbuffer; - dbr_long_t *plong = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - printf("\tTimeStamp: %u %u", - pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,plong++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",*plong); - } - break; - } - case (DBR_TIME_DOUBLE): - { - struct dbr_time_double *pvalue - = (struct dbr_time_double *)pbuffer; - double *pdouble = &pvalue->value; - printf("%2d %2d",pvalue->status,pvalue->severity); - printf("\tTimeStamp: %u %u", - pvalue->stamp.secPastEpoch, pvalue->stamp.nsec); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pdouble++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%6.4f ",(float)(*pdouble)); - } - break; - } - case (DBR_GR_SHORT): - { - struct dbr_gr_short *pvalue - = (struct dbr_gr_short *)pbuffer; - short *pshort = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf("\n\t%8d %8d %8d %8d %8d %8d", - pvalue->upper_disp_limit,pvalue->lower_disp_limit, - pvalue->upper_alarm_limit,pvalue->upper_warning_limit, - pvalue->lower_warning_limit,pvalue->lower_alarm_limit); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pshort++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",*pshort); - } - break; - } - case (DBR_GR_FLOAT): - { - struct dbr_gr_float *pvalue - = (struct dbr_gr_float *)pbuffer; - float *pfloat = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", - pvalue->precision, - pvalue->upper_disp_limit,pvalue->lower_disp_limit, - pvalue->upper_alarm_limit,pvalue->upper_warning_limit, - pvalue->lower_warning_limit,pvalue->lower_alarm_limit); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pfloat++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%6.4f ",*pfloat); - } - break; - } - case (DBR_GR_ENUM): - case (DBR_CTRL_ENUM): - { - struct dbr_gr_enum *pvalue - = (struct dbr_gr_enum *)pbuffer; - printf("%2d %2d",pvalue->status, - pvalue->severity); - printf("\tValue: %d",pvalue->value); - if(pvalue->no_str>0) { - printf("\n\t%3d",pvalue->no_str); - for (i = 0; i < pvalue->no_str; i++) - printf("\n\t%.26s",pvalue->strs[i]); - } - break; - } - case (DBR_GR_CHAR): - { - struct dbr_gr_char *pvalue - = (struct dbr_gr_char *)pbuffer; - unsigned char *pchar = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf("\n\t%8d %8d %8d %8d %8d %8d", - pvalue->upper_disp_limit,pvalue->lower_disp_limit, - pvalue->upper_alarm_limit,pvalue->upper_warning_limit, - pvalue->lower_warning_limit,pvalue->lower_alarm_limit); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pchar++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",(short)(*pchar)); - } - break; - } - case (DBR_GR_LONG): - { - struct dbr_gr_long *pvalue - = (struct dbr_gr_long *)pbuffer; - dbr_long_t *plong = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf("\n\t%8d %8d %8d %8d %8d %8d", - pvalue->upper_disp_limit,pvalue->lower_disp_limit, - pvalue->upper_alarm_limit,pvalue->upper_warning_limit, - pvalue->lower_warning_limit,pvalue->lower_alarm_limit); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,plong++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",*plong); - } - break; - } - case (DBR_GR_DOUBLE): - { - struct dbr_gr_double *pvalue - = (struct dbr_gr_double *)pbuffer; - double *pdouble = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", - pvalue->precision, - (float)(pvalue->upper_disp_limit), - (float)(pvalue->lower_disp_limit), - (float)(pvalue->upper_alarm_limit), - (float)(pvalue->upper_warning_limit), - (float)(pvalue->lower_warning_limit), - (float)(pvalue->lower_alarm_limit)); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pdouble++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%6.4f ",(float)(*pdouble)); - } - break; - } - case (DBR_CTRL_SHORT): - { - struct dbr_ctrl_short *pvalue - = (struct dbr_ctrl_short *)pbuffer; - short *pshort = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf("\n\t%8d %8d %8d %8d %8d %8d", - pvalue->upper_disp_limit,pvalue->lower_disp_limit, - pvalue->upper_alarm_limit,pvalue->upper_warning_limit, - pvalue->lower_warning_limit,pvalue->lower_alarm_limit); - printf(" %8d %8d", - pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pshort++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",*pshort); - } - break; - } - case (DBR_CTRL_FLOAT): - { - struct dbr_ctrl_float *pvalue - = (struct dbr_ctrl_float *)pbuffer; - float *pfloat = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", - pvalue->precision, - pvalue->upper_disp_limit,pvalue->lower_disp_limit, - pvalue->upper_alarm_limit,pvalue->upper_warning_limit, - pvalue->lower_warning_limit,pvalue->lower_alarm_limit); - printf(" %8.3f %8.3f", - pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pfloat++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%6.4f ",*pfloat); - } - break; - } - case (DBR_CTRL_CHAR): - { - struct dbr_ctrl_char *pvalue - = (struct dbr_ctrl_char *)pbuffer; - unsigned char *pchar = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf("\n\t%8d %8d %8d %8d %8d %8d", - pvalue->upper_disp_limit,pvalue->lower_disp_limit, - pvalue->upper_alarm_limit,pvalue->upper_warning_limit, - pvalue->lower_warning_limit,pvalue->lower_alarm_limit); - printf(" %8d %8d", - pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pchar++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%4d ",(short)(*pchar)); - } - break; - } - case (DBR_CTRL_LONG): - { - struct dbr_ctrl_long *pvalue - = (struct dbr_ctrl_long *)pbuffer; - dbr_long_t *plong = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf("\n\t%8d %8d %8d %8d %8d %8d", - pvalue->upper_disp_limit,pvalue->lower_disp_limit, - pvalue->upper_alarm_limit,pvalue->upper_warning_limit, - pvalue->lower_warning_limit,pvalue->lower_alarm_limit); - printf(" %8d %8d", - pvalue->upper_ctrl_limit,pvalue->lower_ctrl_limit); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,plong++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%d ",*plong); - } - break; - } - case (DBR_CTRL_DOUBLE): - { - struct dbr_ctrl_double *pvalue - = (struct dbr_ctrl_double *)pbuffer; - double *pdouble = &pvalue->value; - printf("%2d %2d %.8s",pvalue->status,pvalue->severity, - pvalue->units); - printf(" %3d\n\t%8.3f %8.3f %8.3f %8.3f %8.3f %8.3f", - pvalue->precision, - (float)(pvalue->upper_disp_limit), - (float)(pvalue->lower_disp_limit), - (float)(pvalue->upper_alarm_limit), - (float)(pvalue->upper_warning_limit), - (float)(pvalue->lower_warning_limit), - (float)(pvalue->lower_alarm_limit)); - printf(" %8.3f %8.3f", - (float)(pvalue->upper_ctrl_limit), - (float)(pvalue->lower_ctrl_limit)); - if(count==1) printf("\tValue: "); - for (i = 0; i < count; i++,pdouble++){ - if(count!=1 && (i%10 == 0)) printf("\n"); - printf("%6.6f ",(float)(*pdouble)); - } - break; - } - case (DBR_STSACK_STRING): - { - struct dbr_stsack_string *pvalue - = (struct dbr_stsack_string *)pbuffer; - printf("%2d %2d",pvalue->status,pvalue->severity); - printf(" %2d %2d",pvalue->ackt,pvalue->acks); - printf(" %s",pvalue->value); - break; - } - case (DBR_CLASS_NAME): - { - printf(" %s",(char *)pbuffer); - break; - } + if (sscanf(pvalue, "%hd", &shortvalue) == 1) { + if (dbChannel_put(chan, DBR_SHORT,&shortvalue, 1) < 0) + printf("\n\t SHORT failed "); + if (dbChannel_get(chan, DBR_SHORT,buffer, 1, NULL) < 0) + printf("\n\t SHORT GET failed"); + else + ca_dump_dbr(DBR_SHORT,1, buffer); + } + if (sscanf(pvalue, "%ld", &longvalue) == 1) { + if (dbChannel_put(chan, DBR_LONG,&longvalue, 1) < 0) + printf("\n\t LONG failed "); + if (dbChannel_get(chan, DBR_LONG,buffer, 1, NULL) < 0) + printf("\n\t LONG GET failed"); + else + ca_dump_dbr(DBR_LONG,1, buffer); + } + if (epicsScanFloat(pvalue, &floatvalue) == 1) { + if (dbChannel_put(chan, DBR_FLOAT,&floatvalue, 1) < 0) + printf("\n\t FLOAT failed "); + if (dbChannel_get(chan, DBR_FLOAT,buffer, 1, NULL) < 0) + printf("\n\t FLOAT GET failed"); + else + ca_dump_dbr(DBR_FLOAT,1, buffer); + } + if (epicsScanFloat(pvalue, &floatvalue) == 1) { + doublevalue = floatvalue; + if (dbChannel_put(chan, DBR_DOUBLE,&doublevalue, 1) < 0) + printf("\n\t DOUBLE failed "); + if (dbChannel_get(chan, DBR_DOUBLE,buffer, 1, NULL) < 0) + printf("\n\t DOUBLE GET failed"); + else + ca_dump_dbr(DBR_DOUBLE,1, buffer); + } + if (sscanf(pvalue, "%hd", &shortvalue) == 1) { + charvalue = (unsigned char) shortvalue; + if (dbChannel_put(chan, DBR_CHAR,&charvalue, 1) < 0) + printf("\n\t CHAR failed "); + if (dbChannel_get(chan, DBR_CHAR,buffer, 1, NULL) < 0) + printf("\n\t CHAR GET failed"); + else + ca_dump_dbr(DBR_CHAR,1, buffer); + } + if (sscanf(pvalue, "%hd", &shortvalue) == 1) { + if (dbChannel_put(chan, DBR_ENUM,&shortvalue, 1) < 0) + printf("\n\t ENUM failed "); + if (dbChannel_get(chan, DBR_ENUM,buffer, 1, NULL) < 0) + printf("\n\t ENUM GET failed"); + else + ca_dump_dbr(DBR_ENUM,1, buffer); } printf("\n"); + return (0); } -#endif - + + typedef struct tpnInfo { epicsEventId callbackDone; - putNotify *ppn; -}tpnInfo; + putNotify *ppn; + struct dbChannel *chan; +} tpnInfo; static void tpnCallback(putNotify *ppn) { - struct dbAddr *pdbaddr = (struct dbAddr *)ppn->paddr; - tpnInfo *ptpnInfo = (tpnInfo *)ppn->usrPvt; + tpnInfo *ptpnInfo = (tpnInfo *) ppn->usrPvt; putNotifyStatus status = ppn->status; - char *pname = pdbaddr->precord->name; + const char *pname = dbChannelRecord(ppn->chan)->name; - if(status==0) - printf("tpnCallback: success record=%s\n",pname); + if (status == 0) + printf("tpnCallback: success record=%s\n", pname); else - printf("%s tpnCallback status = %d\n",pname,status); + printf("%s tpnCallback status = %d\n", pname, status); epicsEventSignal(ptpnInfo->callbackDone); } static void tpnThread(void *pvt) { - tpnInfo *ptpnInfo = (tpnInfo *)pvt; - putNotify *ppn = (putNotify *)ptpnInfo->ppn; + tpnInfo *ptpnInfo = (tpnInfo *) pvt; + putNotify *ppn = (putNotify *) ptpnInfo->ppn; dbPutNotify(ppn); epicsEventWait(ptpnInfo->callbackDone); dbNotifyCancel(ppn); epicsEventDestroy(ptpnInfo->callbackDone); - free((void *)ppn->paddr); + free(ppn->pbuffer); + dbChannelDelete(ppn->chan); free(ppn); free(ptpnInfo); } - -int epicsShareAPI tpn(char *pname,char *pvalue) +int epicsShareAPI tpn(char *pname, char *pvalue) { - long status; - tpnInfo *ptpnInfo; - struct dbAddr *pdbaddr=NULL; - putNotify *ppn=NULL; - char *psavevalue; - int len; + struct dbChannel *chan; + tpnInfo *ptpnInfo; + putNotify *ppn; + char *pbuffer; - if (pname==0 || pvalue==0 - || ((*pname < ' ') || (*pname > 'z')) - || ((*pvalue < ' ') || (*pvalue > 'z'))){ - printf("\nusage \"pv name\",\"value\"\n"); - return(1); + chan = dbChannel_create(pname); + if (!chan) { + printf("Channel couldn't be created\n"); + return 1; } - len = strlen(pvalue); - /*allocate space for value immediately following DBADDR*/ - pdbaddr = calloc(1,sizeof(struct dbAddr) + len+1); - if(!pdbaddr) { - printf("calloc failed\n"); - return(-1); + + pbuffer = strdup(pvalue); + ppn = calloc(1, sizeof(putNotify)); + if (!ppn) { + printf("calloc failed\n"); + return -1; } - psavevalue = (char *)(pdbaddr + 1); - strcpy(psavevalue,pvalue); - status = db_name_to_addr(pname,pdbaddr); - if(status) { - printf("db_name_to_addr failed\n"); - free((void *)pdbaddr); - return(-1); - } - ppn = calloc(1,sizeof(putNotify)); - if(!pdbaddr) { - printf("calloc failed\n"); - return(-1); - } - ppn->paddr = pdbaddr; - ppn->pbuffer = psavevalue; + ppn->chan = chan; + ppn->pbuffer = pbuffer; ppn->nRequest = 1; - if(dbPutNotifyMapType(ppn,DBR_STRING)) { - printf("dbPutNotifyMapType failed\n"); - printf("calloc failed\n"); - return(-1); + if (dbPutNotifyMapType(ppn, DBR_STRING)) { + printf("dbPutNotifyMapType failed\n"); + printf("calloc failed\n"); + return -1; } ppn->userCallback = tpnCallback; - ptpnInfo = calloc(1,sizeof(tpnInfo)); - if(!ptpnInfo) { - printf("calloc failed\n"); - return(-1); + ptpnInfo = calloc(1, sizeof(tpnInfo)); + if (!ptpnInfo) { + printf("calloc failed\n"); + return -1; } ptpnInfo->ppn = ppn; ptpnInfo->callbackDone = epicsEventCreate(epicsEventEmpty); ppn->usrPvt = ptpnInfo; - epicsThreadCreate("tpn",epicsThreadPriorityHigh, - epicsThreadGetStackSize(epicsThreadStackMedium), - tpnThread,ptpnInfo); - return(0); + epicsThreadCreate("tpn", epicsThreadPriorityHigh, + epicsThreadGetStackSize(epicsThreadStackMedium), tpnThread, ptpnInfo); + return 0; } diff --git a/src/ioc/rsrv/camessage.c b/src/ioc/rsrv/camessage.c index 09015f667..f51b81c5e 100644 --- a/src/ioc/rsrv/camessage.c +++ b/src/ioc/rsrv/camessage.c @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* @@ -45,7 +45,7 @@ #include "rsrv.h" #include "server.h" -#define RECORD_NAME(PADDR) ((PADDR)->precord->name) +#define RECORD_NAME(CHAN) (dbChannelRecord(CHAN)->name) static EVENTFUNC read_reply; @@ -60,9 +60,9 @@ typedef struct rsrv_put_notify { putNotify dbPutNotify; caHdrLargeArray msg; /* - * Include a union of all scalar types + * Include a union of all scalar types * including fixed length strings so - * that in many cases we can avoid + * that in many cases we can avoid * allocating another buffer and only * use an rsrv_put_notify from its * free list. @@ -173,8 +173,8 @@ va_list args /* * allocate plenty of space for a sprintf() buffer */ - localStatus = cas_copy_in_header ( client, - CA_PROTO_ERROR, maxDiagLen, 0, 0, cid, status, + localStatus = cas_copy_in_header ( client, + CA_PROTO_ERROR, maxDiagLen, 0, 0, cid, status, ( void * ) &pReqOut ); if ( localStatus != ECA_NORMAL ) { errlogPrintf ( "caserver: Unable to deliver err msg \"%s\" to client because \"%s\"\n", @@ -187,7 +187,7 @@ va_list args * copy back the request protocol * (in network byte order) */ - if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) && + if ( ( curp->m_postsize >= 0xffff || curp->m_count >= 0xffff ) && CA_V49( client->minor_version_number ) ) { ca_uint32_t *pLW = ( ca_uint32_t * ) ( pReqOut + 1 ); pReqOut->m_cmmd = htons ( curp->m_cmmd ); @@ -222,7 +222,7 @@ va_list args size += (ca_uint32_t) (diagLen + 1u); } else { - errlogPrintf ( + errlogPrintf ( "caserver: vsend_err: epicsVsnprintf detected " "error message truncation, pFormat = \"%s\"\n", pformat ); @@ -282,9 +282,9 @@ static void log_header ( "CAS: Request from %s => cmmd=%d cid=0x%x type=%d count=%d postsize=%u\n", hostName, mp->m_cmmd, mp->m_cid, mp->m_dataType, mp->m_count, mp->m_postsize); - epicsPrintf ( -"CAS: Request from %s => available=0x%x \tN=%u paddr=%p\n", - hostName, mp->m_available, mnum, (pciu?(void *)&pciu->addr:NULL)); + epicsPrintf ( +"CAS: Request from %s => available=0x%x \tN=%u dbch=%p\n", + hostName, mp->m_available, mnum, (pciu?(void *)&pciu->dbch:NULL)); if (mp->m_cmmd==CA_PROTO_WRITE && mp->m_dataType==DBF_STRING && pPayLoad ) { epicsPrintf ( @@ -297,7 +297,7 @@ static void log_header ( * logBadIdWithFileAndLineno() */ static void logBadIdWithFileAndLineno( -struct client *client, +struct client *client, caHdrLargeArray *mp, const void *pPayload, char *pFileName, @@ -314,10 +314,10 @@ unsigned lineno /* * bad_udp_cmd_action() */ -static int bad_udp_cmd_action ( caHdrLargeArray *mp, +static int bad_udp_cmd_action ( caHdrLargeArray *mp, void *pPayload, struct client *pClient ) { - log_header ("invalid (damaged?) request code from UDP", + log_header ("invalid (damaged?) request code from UDP", pClient, mp, pPayload, 0); return RSRV_ERROR; } @@ -325,13 +325,13 @@ static int bad_udp_cmd_action ( caHdrLargeArray *mp, /* * udp_echo_action() */ -static int udp_echo_action ( caHdrLargeArray *mp, +static int udp_echo_action ( caHdrLargeArray *mp, void *pPayload, struct client *pClient ) { char *pPayloadOut; int status; SEND_LOCK ( pClient ); - status = cas_copy_in_header ( pClient, mp->m_cmmd, mp->m_postsize, + status = cas_copy_in_header ( pClient, mp->m_cmmd, mp->m_postsize, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, ( void * ) &pPayloadOut ); if ( status == ECA_NORMAL ) { @@ -345,13 +345,13 @@ static int udp_echo_action ( caHdrLargeArray *mp, /* * bad_tcp_cmd_action() */ -static int bad_tcp_cmd_action ( caHdrLargeArray *mp, void *pPayload, +static int bad_tcp_cmd_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { const char *pCtx = "invalid (damaged?) request code from TCP"; log_header ( pCtx, client, mp, pPayload, 0 ); - /* + /* * by default, clients dont recover * from this */ @@ -365,7 +365,7 @@ static int bad_tcp_cmd_action ( caHdrLargeArray *mp, void *pPayload, /* * tcp_version_action() */ -static int tcp_version_action ( caHdrLargeArray *mp, void *pPayload, +static int tcp_version_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { double tmp; @@ -406,13 +406,13 @@ static int tcp_version_action ( caHdrLargeArray *mp, void *pPayload, /* * tcp_echo_action() */ -static int tcp_echo_action ( caHdrLargeArray *mp, +static int tcp_echo_action ( caHdrLargeArray *mp, void *pPayload, struct client *pClient ) { char *pPayloadOut; int status; SEND_LOCK ( pClient ); - status = cas_copy_in_header ( pClient, mp->m_cmmd, mp->m_postsize, + status = cas_copy_in_header ( pClient, mp->m_cmmd, mp->m_postsize, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, ( void * ) &pPayloadOut ); if ( status == ECA_NORMAL ) { @@ -426,7 +426,7 @@ static int tcp_echo_action ( caHdrLargeArray *mp, /* * events_on_action () */ -static int events_on_action ( caHdrLargeArray *mp, +static int events_on_action ( caHdrLargeArray *mp, void *pPayload, struct client *pClient ) { db_event_flow_ctrl_mode_off ( pClient->evuser ); @@ -436,7 +436,7 @@ static int events_on_action ( caHdrLargeArray *mp, /* * events_off_action () */ -static int events_off_action ( caHdrLargeArray *mp, +static int events_off_action ( caHdrLargeArray *mp, void *pPayload, struct client *pClient ) { db_event_flow_ctrl_mode_on ( pClient->evuser ); @@ -462,8 +462,8 @@ static void no_read_access_event ( struct client *pClient, * on failure to pre v41 clients */ if ( ! CA_V41 ( pClient->minor_version_number ) ) { - send_err ( &pevext->msg, ECA_GETFAIL, pClient, - RECORD_NAME ( &pevext->pciu->addr ) ); + send_err ( &pevext->msg, ECA_GETFAIL, pClient, + RECORD_NAME ( pevext->pciu->dbch ) ); return; } @@ -473,30 +473,30 @@ static void no_read_access_event ( struct client *pClient, * event/put/get callback. * * Fetched value is zerod in case they - * use it even when the status indicates + * use it even when the status indicates * failure. * * The m_cid field in the protocol * header is abused to carry the status */ - status = cas_copy_in_header ( pClient, pevext->msg.m_cmmd, pevext->size, - pevext->msg.m_dataType, pevext->msg.m_count, ECA_NORDACCESS, + status = cas_copy_in_header ( pClient, pevext->msg.m_cmmd, pevext->size, + pevext->msg.m_dataType, pevext->msg.m_count, ECA_NORDACCESS, pevext->msg.m_available, ( void * ) &pPayloadOut ); if ( status == ECA_NORMAL ) { memset ( pPayloadOut, 0, pevext->size ); cas_commit_msg ( pClient, pevext->size ); } else { - send_err ( &pevext->msg, status, pClient, + send_err ( &pevext->msg, status, pClient, "server unable to load read access denied response into protocol buffer PV=\"%s max bytes=%u\"", - RECORD_NAME ( &pevext->pciu->addr ), rsrvSizeofLargeBufTCP ); + RECORD_NAME ( pevext->pciu->dbch ), rsrvSizeofLargeBufTCP ); } } /* * read_reply() */ -static void read_reply ( void *pArg, struct dbAddr *paddr, +static void read_reply ( void *pArg, struct dbChannel *dbch, int eventsRemaining, db_field_log *pfl ) { ca_uint32_t cid; @@ -513,7 +513,7 @@ static void read_reply ( void *pArg, struct dbAddr *paddr, SEND_LOCK ( pClient ); - /* + /* * New clients recv the status of the * operation directly to the * event/put/get callback. @@ -543,7 +543,7 @@ static void read_reply ( void *pArg, struct dbAddr *paddr, pevext->msg.m_dataType, item_count, cid, pevext->msg.m_available, &pPayload ); if ( status != ECA_NORMAL ) { - send_err ( &pevext->msg, status, pClient, + send_err ( &pevext->msg, status, pClient, "server unable to load read (or subscription update) response " "into protocol buffer PV=\"%s\" max bytes=%u", RECORD_NAME ( paddr ), rsrvSizeofLargeBufTCP ); @@ -564,19 +564,19 @@ static void read_reply ( void *pArg, struct dbAddr *paddr, return; } - status = db_get_field_and_count( - paddr, pevext->msg.m_dataType, pPayload, &item_count, pfl); + status = dbChannel_get_count ( dbch, pevext->msg.m_dataType, + pPayload, &item_count, pfl); if ( status < 0 ) { /* * I cant wait to redesign this protocol from scratch! */ if ( ! v41 ) { /* - * old client & plain get + * old client & plain get * continue to return an exception * on failure */ - send_err ( &pevext->msg, ECA_GETFAIL, pClient, RECORD_NAME ( paddr ) ); + send_err ( &pevext->msg, ECA_GETFAIL, pClient, RECORD_NAME ( dbch ) ); } else { /* New clients recv the status of the operation directly to the @@ -598,8 +598,8 @@ static void read_reply ( void *pArg, struct dbAddr *paddr, } } else { - int cacStatus = caNetConvert ( - pevext->msg.m_dataType, pPayload, pPayload, + int cacStatus = caNetConvert ( + pevext->msg.m_dataType, pPayload, pPayload, TRUE /* host -> net format */, item_count ); if ( cacStatus == ECA_NORMAL ) { ca_uint32_t data_size = @@ -655,18 +655,18 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p SEND_LOCK ( pClient ); if ( INVALID_DB_REQ ( mp->m_dataType ) ) { - send_err ( mp, ECA_BADTYPE, pClient, RECORD_NAME ( &pciu->addr ) ); + send_err ( mp, ECA_BADTYPE, pClient, RECORD_NAME ( pciu->dbch ) ); SEND_UNLOCK ( pClient ); return RSRV_ERROR; } payloadSize = dbr_size_n ( mp->m_dataType, mp->m_count ); - status = cas_copy_in_header ( pClient, mp->m_cmmd, payloadSize, + status = cas_copy_in_header ( pClient, mp->m_cmmd, payloadSize, mp->m_dataType, mp->m_count, pciu->cid, mp->m_available, &pPayload ); if ( status != ECA_NORMAL ) { - send_err ( mp, status, pClient, + send_err ( mp, status, pClient, "server unable to load read response into protocol buffer PV=\"%s\" max bytes=%u", - RECORD_NAME ( &pciu->addr ), rsrvSizeofLargeBufTCP ); + RECORD_NAME ( pciu->dbch ), rsrvSizeofLargeBufTCP ); SEND_UNLOCK ( pClient ); return RSRV_OK; } @@ -682,25 +682,25 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p else{ status = ECA_GETFAIL; } - send_err ( mp, status, - pClient, RECORD_NAME ( &pciu->addr ) ); + send_err ( mp, status, + pClient, RECORD_NAME ( pciu->dbch ) ); SEND_UNLOCK ( pClient ); return RSRV_OK; } - status = db_get_field ( &pciu->addr, mp->m_dataType, + status = dbChannel_get ( pciu->dbch, mp->m_dataType, pPayload, mp->m_count, 0 ); if ( status < 0 ) { - send_err ( mp, ECA_GETFAIL, pClient, RECORD_NAME ( &pciu->addr ) ); + send_err ( mp, ECA_GETFAIL, pClient, RECORD_NAME ( pciu->dbch ) ); SEND_UNLOCK ( pClient ); return RSRV_OK; } - status = caNetConvert ( - mp->m_dataType, pPayload, pPayload, + status = caNetConvert ( + mp->m_dataType, pPayload, pPayload, TRUE /* host -> net format */, mp->m_count ); if ( status != ECA_NORMAL ) { - send_err ( mp, status, pClient, RECORD_NAME ( &pciu->addr ) ); + send_err ( mp, status, pClient, RECORD_NAME ( pciu->dbch ) ); SEND_UNLOCK ( pClient ); return RSRV_OK; } @@ -717,7 +717,7 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p } else { pStr[payloadSize-1] = '\0'; - errlogPrintf ( + errlogPrintf ( "caserver: read_action: detected DBR_STRING w/o nill termination " "in response from db_get_field, pPayload = \"%s\"\n", pStr ); @@ -754,11 +754,11 @@ static int read_notify_action ( caHdrLargeArray *mp, void *pPayload, struct clie * favor of the standard db event calling * mechanism- routine(userarg, paddr). See * events added above. - * + * * Hold argument set true so the send message * buffer is not flushed once each call. */ - read_reply ( &evext, &pciu->addr, TRUE, NULL ); + read_reply ( &evext, pciu->dbch, TRUE, NULL ); return RSRV_OK; } @@ -766,7 +766,7 @@ static int read_notify_action ( caHdrLargeArray *mp, void *pPayload, struct clie /* * write_action() */ -static int write_action ( caHdrLargeArray *mp, +static int write_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { struct channel_in_use *pciu; @@ -791,25 +791,25 @@ static int write_action ( caHdrLargeArray *mp, } SEND_LOCK(client); send_err( - mp, - status, - client, - RECORD_NAME(&pciu->addr)); + mp, + status, + client, + RECORD_NAME ( pciu->dbch )); SEND_UNLOCK(client); return RSRV_OK; } - status = caNetConvert ( - mp->m_dataType, pPayload, pPayload, + status = caNetConvert ( + mp->m_dataType, pPayload, pPayload, FALSE /* net -> host format */, mp->m_count ); if ( status != ECA_NORMAL ) { log_header ("invalid data type", client, mp, pPayload, 0); SEND_LOCK(client); send_err( - mp, - status, - client, - RECORD_NAME(&pciu->addr)); + mp, + status, + client, + RECORD_NAME ( pciu->dbch )); SEND_UNLOCK(client); return RSRV_ERROR; } @@ -817,10 +817,10 @@ static int write_action ( caHdrLargeArray *mp, asWritePvt = asTrapWriteBefore ( pciu->asClientPVT, pciu->client->pUserName ? pciu->client->pUserName : "", pciu->client->pHostName ? pciu->client->pHostName : "", - (void *) &pciu->addr ); + pciu->dbch ); - dbStatus = db_put_field( - &pciu->addr, + dbStatus = dbChannel_put( + pciu->dbch, mp->m_dataType, pPayload, mp->m_count); @@ -830,10 +830,10 @@ static int write_action ( caHdrLargeArray *mp, if (dbStatus < 0) { SEND_LOCK(client); send_err( - mp, - ECA_PUTFAIL, - client, - RECORD_NAME(&pciu->addr)); + mp, + ECA_PUTFAIL, + client, + RECORD_NAME ( pciu->dbch )); SEND_UNLOCK(client); } @@ -852,8 +852,8 @@ static int host_name_action ( caHdrLargeArray *mp, void *pPayload, int chanCount; epicsMutexMustLock ( client->chanListLock ); - chanCount = - ellCount ( &client->chanList ) + + chanCount = + ellCount ( &client->chanList ) + ellCount ( &client->chanPendingUpdateARList ); epicsMutexUnlock( client->chanListLock ); @@ -872,13 +872,13 @@ static int host_name_action ( caHdrLargeArray *mp, void *pPayload, pName = (char *) pPayload; size = strlen(pName)+1; if (size > 512) { - log_header ( "bad (very long) host name", + log_header ( "bad (very long) host name", client, mp, pPayload, 0 ); SEND_LOCK(client); send_err( - mp, - ECA_INTERNAL, - client, + mp, + ECA_INTERNAL, + client, "bad (very long) host name"); SEND_UNLOCK(client); return RSRV_ERROR; @@ -889,7 +889,7 @@ static int host_name_action ( caHdrLargeArray *mp, void *pPayload, */ pMalloc = malloc(size); if(!pMalloc){ - log_header ( "no space in pool for new host name", + log_header ( "no space in pool for new host name", client, mp, pPayload, 0 ); SEND_LOCK(client); send_err( @@ -901,8 +901,8 @@ static int host_name_action ( caHdrLargeArray *mp, void *pPayload, return RSRV_ERROR; } strncpy( - pMalloc, - pName, + pMalloc, + pName, size-1); pMalloc[size-1]='\0'; @@ -912,13 +912,13 @@ static int host_name_action ( caHdrLargeArray *mp, void *pPayload, free ( pName ); } - DLOG (2, ( "CAS: host_name_action for \"%s\"\n", + DLOG (2, ( "CAS: host_name_action for \"%s\"\n", client->pHostName ? client->pHostName : "" ) ); return RSRV_OK; } - + /* * client_name_action() */ @@ -931,8 +931,8 @@ static int client_name_action ( caHdrLargeArray *mp, void *pPayload, int chanCount; epicsMutexMustLock ( client->chanListLock ); - chanCount = - ellCount ( &client->chanList ) + + chanCount = + ellCount ( &client->chanList ) + ellCount ( &client->chanPendingUpdateARList ); epicsMutexUnlock( client->chanListLock ); @@ -951,13 +951,13 @@ static int client_name_action ( caHdrLargeArray *mp, void *pPayload, pName = (char *) pPayload; size = strlen(pName)+1; if (size > 512) { - log_header ("a very long user name was specified", + log_header ("a very long user name was specified", client, mp, pPayload, 0); SEND_LOCK(client); send_err( - mp, - ECA_INTERNAL, - client, + mp, + ECA_INTERNAL, + client, "a very long user name was specified"); SEND_UNLOCK(client); return RSRV_ERROR; @@ -968,7 +968,7 @@ static int client_name_action ( caHdrLargeArray *mp, void *pPayload, */ pMalloc = malloc(size); if(!pMalloc){ - log_header ("no memory for new user name", + log_header ("no memory for new user name", client, mp, pPayload, 0); SEND_LOCK(client); send_err( @@ -980,8 +980,8 @@ static int client_name_action ( caHdrLargeArray *mp, void *pPayload, return RSRV_ERROR; } strncpy( - pMalloc, - pName, + pMalloc, + pName, size-1); pMalloc[size-1]='\0'; @@ -999,7 +999,7 @@ static int client_name_action ( caHdrLargeArray *mp, void *pPayload, */ static struct channel_in_use *casCreateChannel ( struct client *client, -struct dbAddr *pAddr, +struct dbChannel *dbch, unsigned cid ) { @@ -1009,14 +1009,14 @@ unsigned cid int status; /* get block off free list if possible */ - pchannel = (struct channel_in_use *) + pchannel = (struct channel_in_use *) freeListCalloc(rsrvChanFreeList); if (!pchannel) { return NULL; } ellInit(&pchannel->eventq); epicsTimeGetCurrent(&pchannel->time_at_creation); - pchannel->addr = *pAddr; + pchannel->dbch = dbch; pchannel->client = client; /* * bypass read only warning @@ -1046,8 +1046,8 @@ unsigned cid * Verify that this id is not in use */ status = bucketAddItemUnsignedId ( - pCaBucket, - &pchannel->sid, + pCaBucket, + &pchannel->sid, pchannel); } while (status == S_bucket_idInUse); @@ -1116,7 +1116,7 @@ static void casAccessRightsCB(ASCLIENTPVT ascpvt, asClientStatus type) epicsMutexUnlock ( pclient->chanListLock ); /* - * Update all event call backs + * Update all event call backs */ epicsMutexMustLock(pclient->eventqLock); for (pevext = (struct event_ext *) ellFirst(&pciu->eventq); @@ -1175,8 +1175,8 @@ static void access_rights_reply ( struct channel_in_use * pciu ) } SEND_LOCK ( pciu->client ); - status = cas_copy_in_header ( - pciu->client, CA_PROTO_ACCESS_RIGHTS, 0, + status = cas_copy_in_header ( + pciu->client, CA_PROTO_ACCESS_RIGHTS, 0, 0, 0, pciu->cid, ar, 0 ); /* * OK to just ignore the request if the connection drops @@ -1197,26 +1197,28 @@ static void claim_ciu_reply ( struct channel_in_use * pciu ) if ( v42 ) { int status; ca_uint32_t nElem; + long dbElem; SEND_LOCK ( pciu->client ); - if ( pciu->addr.no_elements < 0 ) { + dbElem = dbChannelElements(pciu->dbch); + if ( dbElem < 0 ) { nElem = 0; } else { if ( ! CA_V49 ( pciu->client->minor_version_number ) ) { - if ( pciu->addr.no_elements >= 0xffff ) { + if ( dbElem >= 0xffff ) { nElem = 0xfffe; } else { - nElem = (ca_uint32_t) pciu->addr.no_elements; + nElem = (ca_uint32_t) dbElem; } } else { - nElem = (ca_uint32_t) pciu->addr.no_elements; + nElem = (ca_uint32_t) dbElem; } } - status = cas_copy_in_header ( + status = cas_copy_in_header ( pciu->client, CA_PROTO_CREATE_CHAN, 0u, - pciu->addr.dbr_field_type, nElem, pciu->cid, + dbChannelExportType(pciu->dbch), nElem, pciu->cid, pciu->sid, NULL ); if ( status == ECA_NORMAL ) { cas_commit_msg ( pciu->client, 0u ); @@ -1228,7 +1230,7 @@ static void claim_ciu_reply ( struct channel_in_use * pciu ) /* * claim_ciu_action() */ -static int claim_ciu_action ( caHdrLargeArray *mp, +static int claim_ciu_action ( caHdrLargeArray *mp, void *pPayload, client *client ) { int status; @@ -1243,47 +1245,48 @@ static int claim_ciu_action ( caHdrLargeArray *mp, client->minor_version_number = mp->m_available; if (CA_V44(client->minor_version_number)) { - struct dbAddr tmp_addr; + struct dbChannel *dbch; char *pName = (char *) pPayload; /* * check the sanity of the message */ if (mp->m_postsize<=1) { - log_header ( "empty PV name in UDP search request?", + log_header ( "empty PV name in UDP search request?", client, mp, pPayload, 0 ); return RSRV_OK; } pName[mp->m_postsize-1] = '\0'; - status = db_name_to_addr (pName, &tmp_addr); - if (status) { + dbch = dbChannel_create (pName); + if (!dbch) { return RSRV_OK; } - DLOG ( 2, ("CAS: claim_ciu_action found '%s', type %d, count %d\n", - pName, tmp_addr.dbr_field_type, tmp_addr.no_elements) ); - + DLOG ( 2, ("CAS: claim_ciu_action found '%s', type %d, count %d\n", + pName, dbChannelExportType(dbch), dbChannelElements(dbch)) ); + pciu = casCreateChannel ( - client, - &tmp_addr, + client, + dbch, mp->m_cid); if (!pciu) { - log_header ("no memory to create new channel", + log_header ("no memory to create new channel", client, mp, pPayload, 0); SEND_LOCK(client); - send_err(mp, - ECA_ALLOCMEM, - client, - RECORD_NAME(&tmp_addr)); + send_err(mp, + ECA_ALLOCMEM, + client, + RECORD_NAME(dbch)); SEND_UNLOCK(client); + dbChannelDelete(dbch); return RSRV_ERROR; } } else { epicsMutexMustLock(prsrv_cast_client->chanListLock); /* - * clients which dont claim their + * clients which dont claim their * channel in use block prior to * timeout must reconnect */ @@ -1302,8 +1305,8 @@ static int claim_ciu_action ( caHdrLargeArray *mp, return RSRV_ERROR; } - /* - * duplicate claim message are unacceptable + /* + * duplicate claim message are unacceptable * (so we disconnect the client) */ if (pciu->client!=prsrv_cast_client) { @@ -1327,7 +1330,7 @@ static int claim_ciu_action ( caHdrLargeArray *mp, * who is claiming it */ ellDelete( - &prsrv_cast_client->chanList, + &prsrv_cast_client->chanList, &pciu->node); epicsMutexUnlock(prsrv_cast_client->chanListLock); @@ -1343,12 +1346,12 @@ static int claim_ciu_action ( caHdrLargeArray *mp, */ status = asAddClient( &pciu->asClientPVT, - asDbGetMemberPvt(&pciu->addr), - asDbGetAsl(&pciu->addr), + asDbGetMemberPvt(pciu->dbch), + asDbGetAsl(pciu->dbch), client->pUserName ? client->pUserName : "", - client->pHostName ? client->pHostName : ""); + client->pHostName ? client->pHostName : ""); if(status != 0 && status != S_asLib_asNotActive){ - log_header ("No room for security table", + log_header ("No room for security table", client, mp, pPayload, 0); SEND_LOCK(client); send_err(mp, ECA_ALLOCMEM, client, "No room for security table"); @@ -1357,7 +1360,7 @@ static int claim_ciu_action ( caHdrLargeArray *mp, } /* - * store ptr to channel in use block + * store ptr to channel in use block * in access security private */ asPutClientPvt(pciu->asClientPVT, pciu); @@ -1366,7 +1369,7 @@ static int claim_ciu_action ( caHdrLargeArray *mp, * register for asynch updates of access rights changes */ status = asRegisterClientCallback( - pciu->asClientPVT, + pciu->asClientPVT, casAccessRightsCB); if ( status == S_asLib_asNotActive ) { epicsMutexMustLock ( client->chanListLock ); @@ -1378,10 +1381,10 @@ static int claim_ciu_action ( caHdrLargeArray *mp, claim_ciu_reply ( pciu ); } else if (status!=0) { - log_header ("No room for access security state change subscription", + log_header ("No room for access security state change subscription", client, mp, pPayload, 0); SEND_LOCK(client); - send_err(mp, ECA_ALLOCMEM, client, + send_err(mp, ECA_ALLOCMEM, client, "No room for access security state change subscription"); SEND_UNLOCK(client); return RSRV_ERROR; @@ -1429,7 +1432,7 @@ static void write_notify_call_back(putNotify *ppn) db_post_extra_labor(pClient->evuser); } -/* +/* * write_notify_reply() * (called by the CA server event task via the extra labor interface) */ @@ -1477,18 +1480,18 @@ static void write_notify_reply ( struct client * pClient ) asTrapWriteAfter ( asWritePvtTmp ); /* - * the channel id field is being abused to carry + * the channel id field is being abused to carry * status here */ SEND_LOCK(pClient); - localStatus = cas_copy_in_header ( - pClient, CA_PROTO_WRITE_NOTIFY, - 0u, msgtmp.m_dataType, msgtmp.m_count, status, + localStatus = cas_copy_in_header ( + pClient, CA_PROTO_WRITE_NOTIFY, + 0u, msgtmp.m_dataType, msgtmp.m_count, status, msgtmp.m_available, 0 ); if ( localStatus != ECA_NORMAL ) { /* * inability to aquire buffer space - * Indicates corruption + * Indicates corruption */ errlogPrintf("CA server corrupted - put call back(s) discarded\n"); SEND_UNLOCK ( pClient ); @@ -1511,11 +1514,11 @@ static void write_notify_reply ( struct client * pClient ) */ static void sendAllUpdateAS ( struct client *client ) { - struct channel_in_use *pciu; + struct channel_in_use *pciu; epicsMutexMustLock ( client->chanListLock ); - pciu = ( struct channel_in_use * ) + pciu = ( struct channel_in_use * ) ellGet ( & client->chanPendingUpdateARList ); while ( pciu ) { if ( pciu->state == rsrvCS_pendConnectRespUpdatePendAR ) { @@ -1531,14 +1534,14 @@ static void sendAllUpdateAS ( struct client *client ) } pciu->state = rsrvCS_inService; ellAdd ( & client->chanList, & pciu->node ); - pciu = ( struct channel_in_use * ) + pciu = ( struct channel_in_use * ) ellGet ( & client->chanPendingUpdateARList ); } epicsMutexUnlock( client->chanListLock ); } -/* +/* * rsrv_extra_labor() * (called by the CA server event task via the extra labor interface) */ @@ -1562,8 +1565,8 @@ static void putNotifyErrorReply ( struct client *client, caHdrLargeArray *mp, in * the cid field abused to contain status * during put cb replies */ - status = cas_copy_in_header ( client, CA_PROTO_WRITE_NOTIFY, - 0u, mp->m_dataType, mp->m_count, statusCA, + status = cas_copy_in_header ( client, CA_PROTO_WRITE_NOTIFY, + 0u, mp->m_dataType, mp->m_count, statusCA, mp->m_available, 0 ); if ( status != ECA_NORMAL ) { SEND_UNLOCK ( client ); @@ -1578,28 +1581,28 @@ static void putNotifyErrorReply ( struct client *client, caHdrLargeArray *mp, in void initializePutNotifyFreeList (void) { if ( ! rsrvPutNotifyFreeList ) { - freeListInitPvt ( &rsrvPutNotifyFreeList, + freeListInitPvt ( &rsrvPutNotifyFreeList, sizeof(struct rsrv_put_notify), 512 ); assert ( rsrvPutNotifyFreeList ); } } -static struct rsrv_put_notify * +static struct rsrv_put_notify * rsrvAllocPutNotify ( struct channel_in_use * pciu ) { struct rsrv_put_notify *pNotify; - + if ( rsrvPutNotifyFreeList ) { pNotify = (RSRVPUTNOTIFY *) freeListCalloc ( rsrvPutNotifyFreeList ); if ( pNotify ) { - pNotify->dbPutNotify.pbuffer = + pNotify->dbPutNotify.pbuffer = &pNotify->dbrScalarValue; - pNotify->valueSize = + pNotify->valueSize = sizeof (pNotify->dbrScalarValue); pNotify->dbPutNotify.usrPvt = pciu; - pNotify->dbPutNotify.paddr = &pciu->addr; - pNotify->dbPutNotify.userCallback = + pNotify->dbPutNotify.chan = pciu->dbch; + pNotify->dbPutNotify.userCallback = write_notify_call_back; } } @@ -1609,18 +1612,18 @@ static struct rsrv_put_notify * return pNotify; } -static int rsrvExpandPutNotify ( +static int rsrvExpandPutNotify ( struct rsrv_put_notify * pNotify, unsigned sizeNeeded ) { int booleanStatus; - + if ( sizeNeeded > pNotify->valueSize ) { /* - * try to use the union embeded in the free list - * item, but allocate a random sized block if they + * try to use the union embeded in the free list + * item, but allocate a random sized block if they * writing a vector. */ - if ( pNotify->valueSize > + if ( pNotify->valueSize > sizeof (pNotify->dbrScalarValue) ) { free ( pNotify->dbPutNotify.pbuffer ); } @@ -1633,9 +1636,9 @@ static int rsrvExpandPutNotify ( /* * revert back to the embedded union */ - pNotify->dbPutNotify.pbuffer = + pNotify->dbPutNotify.pbuffer = &pNotify->dbrScalarValue; - pNotify->valueSize = + pNotify->valueSize = sizeof (pNotify->dbrScalarValue); booleanStatus = FALSE; } @@ -1643,7 +1646,7 @@ static int rsrvExpandPutNotify ( else { booleanStatus = TRUE; } - + return booleanStatus; } @@ -1651,7 +1654,7 @@ unsigned rsrvSizeOfPutNotify ( struct rsrv_put_notify *pNotify ) { unsigned size = sizeof ( *pNotify ); if ( pNotify ) { - if ( pNotify->valueSize > + if ( pNotify->valueSize > sizeof ( pNotify->dbrScalarValue ) ) { size += pNotify->valueSize; } @@ -1659,7 +1662,7 @@ unsigned rsrvSizeOfPutNotify ( struct rsrv_put_notify *pNotify ) return size; } -void rsrvFreePutNotify ( client *pClient, +void rsrvFreePutNotify ( client *pClient, struct rsrv_put_notify *pNotify ) { if ( pNotify ) { @@ -1671,16 +1674,16 @@ void rsrvFreePutNotify ( client *pClient, epicsMutexUnlock ( pClient->putNotifyLock ); /* - * if any possiblity that the put notify is + * if any possiblity that the put notify is * outstanding then cancel it */ if ( busyTmp ) { dbNotifyCancel ( &pNotify->dbPutNotify ); - } + } epicsMutexMustLock ( pClient->putNotifyLock ); if ( pNotify->onExtraLaborQueue ) { - ellDelete ( &pClient->putNotifyQue, + ellDelete ( &pClient->putNotifyQue, &pNotify->node ); } busyTmp = pNotify->busy; @@ -1692,7 +1695,7 @@ void rsrvFreePutNotify ( client *pClient, asTrapWriteAfter ( asWritePvtTmp ); } - if ( pNotify->valueSize > + if ( pNotify->valueSize > sizeof(pNotify->dbrScalarValue) ) { free ( pNotify->dbPutNotify.pbuffer ); } @@ -1703,7 +1706,7 @@ void rsrvFreePutNotify ( client *pClient, /* * write_notify_action() */ -static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, +static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { unsigned size; @@ -1732,7 +1735,7 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, if ( pciu->pPutNotify ) { /* - * serialize concurrent put notifies + * serialize concurrent put notifies */ epicsMutexMustLock(client->putNotifyLock); while(pciu->pPutNotify->busy){ @@ -1757,7 +1760,7 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, busyTmp = pciu->pPutNotify->busy; if ( busyTmp ) { if ( pciu->pPutNotify->onExtraLaborQueue ) { - ellDelete ( &client->putNotifyQue, + ellDelete ( &client->putNotifyQue, &pciu->pPutNotify->node ); } pciu->pPutNotify->busy = FALSE; @@ -1765,9 +1768,9 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, pciu->pPutNotify->asWritePvt = 0; } epicsMutexUnlock(client->putNotifyLock); - + if ( busyTmp ) { - log_header("put call back time out", client, + log_header("put call back time out", client, &pciu->pPutNotify->msg, pciu->pPutNotify->dbPutNotify.pbuffer, 0); asTrapWriteAfter ( asWritePvtTmp ); putNotifyErrorReply (client, &pciu->pPutNotify->msg, ECA_PUTCBINPROG); @@ -1784,15 +1787,15 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, * send error and go to next request * if there isnt enough memory left */ - log_header ( "no memory to initiate put notify", + log_header ( "no memory to initiate put notify", client, mp, pPayload, 0 ); putNotifyErrorReply (client, mp, ECA_ALLOCMEM); return RSRV_ERROR; } } - + if ( ! rsrvExpandPutNotify ( pciu->pPutNotify, size ) ) { - log_header ( "no memory to initiate vector put notify", + log_header ( "no memory to initiate vector put notify", client, mp, pPayload, 0 ); putNotifyErrorReply ( client, mp, ECA_ALLOCMEM ); return RSRV_ERROR; @@ -1803,8 +1806,8 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, pciu->pPutNotify->msg = *mp; pciu->pPutNotify->dbPutNotify.nRequest = mp->m_count; - status = caNetConvert ( - mp->m_dataType, pPayload, pciu->pPutNotify->dbPutNotify.pbuffer, + status = caNetConvert ( + mp->m_dataType, pPayload, pciu->pPutNotify->dbPutNotify.pbuffer, FALSE /* net -> host format */, mp->m_count ); if ( status != ECA_NORMAL ) { log_header ("invalid data type", client, mp, pPayload, 0); @@ -1819,11 +1822,11 @@ static int write_notify_action ( caHdrLargeArray *mp, void *pPayload, return RSRV_OK; } - pciu->pPutNotify->asWritePvt = asTrapWriteBefore ( + pciu->pPutNotify->asWritePvt = asTrapWriteBefore ( pciu->asClientPVT, pciu->client->pUserName ? pciu->client->pUserName : "", pciu->client->pHostName ? pciu->client->pHostName : "", - (void *) &pciu->addr ); + pciu->dbch ); dbPutNotify(&pciu->pPutNotify->dbPutNotify); @@ -1849,10 +1852,10 @@ static int event_add_action (caHdrLargeArray *mp, void *pPayload, struct client } /* - * stop further use of server if memory becomes scarse + * stop further use of server if memory becomes scarce */ spaceAvailOnFreeList = freeListItemsAvail ( rsrvEventFreeList ) > 0; - if ( osiSufficentSpaceInPool(sizeof(*pevext)) || spaceAvailOnFreeList ) { + if ( osiSufficentSpaceInPool(sizeof(*pevext)) || spaceAvailOnFreeList ) { pevext = (struct event_ext *) freeListCalloc (rsrvEventFreeList); } else { @@ -1860,14 +1863,14 @@ static int event_add_action (caHdrLargeArray *mp, void *pPayload, struct client } if (!pevext) { - log_header ("no memory to add subscription", + log_header ("no memory to add subscription", client, mp, pPayload, 0); SEND_LOCK(client); send_err( mp, - ECA_ALLOCMEM, - client, - RECORD_NAME(&pciu->addr)); + ECA_ALLOCMEM, + client, + RECORD_NAME(pciu->dbch)); SEND_UNLOCK(client); return RSRV_ERROR; } @@ -1881,15 +1884,15 @@ static int event_add_action (caHdrLargeArray *mp, void *pPayload, struct client ellAdd( &pciu->eventq, &pevext->node); epicsMutexUnlock(client->eventqLock); - pevext->pdbev = db_add_event (client->evuser, &pciu->addr, + pevext->pdbev = db_add_event (client->evuser, pciu->dbch, read_reply, pevext, pevext->mask); if (pevext->pdbev == NULL) { - log_header ("no memory to add subscription to db", + log_header ("no memory to add subscription to db", client, mp, pPayload, 0); SEND_LOCK(client); - send_err (mp, ECA_ALLOCMEM, client, - "subscription install into record %s failed", - RECORD_NAME(&pciu->addr)); + send_err (mp, ECA_ALLOCMEM, client, + "subscription install into record %s failed", + RECORD_NAME(pciu->dbch)); SEND_UNLOCK(client); return RSRV_ERROR; } @@ -1903,7 +1906,7 @@ static int event_add_action (caHdrLargeArray *mp, void *pPayload, struct client * of the socket would block. This prevents * a application program initiated deadlock. * - * However when I am reconnecting I reissue + * However when I am reconnecting I reissue * the monitors and I could get deadlocked. * The client is blocked sending and the server * task for the client is blocked sending in @@ -1914,7 +1917,7 @@ static int event_add_action (caHdrLargeArray *mp, void *pPayload, struct client * post_single_event() below instead of calling * read_reply() in this module. This is a complete * fix since a monitor setup is the only request - * soliciting a reply in the client which is + * soliciting a reply in the client which is * issued from inside of service.c (from inside * of the part of the ca client which services * messages sent by the server). @@ -1940,13 +1943,13 @@ static int event_add_action (caHdrLargeArray *mp, void *pPayload, struct client /* * clear_channel_reply() */ -static int clear_channel_reply ( caHdrLargeArray *mp, +static int clear_channel_reply ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { struct event_ext *pevext; struct channel_in_use *pciu; int status; - + /* * * Verify the channel @@ -1957,43 +1960,43 @@ static int clear_channel_reply ( caHdrLargeArray *mp, logBadId ( client, mp, pPayload ); return RSRV_ERROR; } - + rsrvFreePutNotify ( client, pciu->pPutNotify ); - + while (TRUE){ epicsMutexMustLock(client->eventqLock); pevext = (struct event_ext *) ellGet(&pciu->eventq); epicsMutexUnlock(client->eventqLock); - + if(!pevext){ break; } - + if (pevext->pdbev) { db_cancel_event (pevext->pdbev); } freeListFree(rsrvEventFreeList, pevext); } - + db_flush_extra_labor_event ( client->evuser ); - + /* * send delete confirmed message */ SEND_LOCK(client); - status = cas_copy_in_header ( client, CA_PROTO_CLEAR_CHANNEL, - 0u, mp->m_dataType, mp->m_count, mp->m_cid, + status = cas_copy_in_header ( client, CA_PROTO_CLEAR_CHANNEL, + 0u, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, NULL ); if ( status != ECA_NORMAL ) { SEND_UNLOCK(client); return RSRV_ERROR; } - + cas_commit_msg ( client, 0u ); SEND_UNLOCK(client); - + epicsMutexMustLock ( client->chanListLock ); - if ( pciu->state == rsrvCS_inService || + if ( pciu->state == rsrvCS_inService || pciu->state == rsrvCS_pendConnectResp ) { ellDelete ( &client->chanList, &pciu->node ); } @@ -2004,22 +2007,22 @@ static int clear_channel_reply ( caHdrLargeArray *mp, else { epicsMutexUnlock( client->chanListLock ); SEND_LOCK(client); - send_err(mp, ECA_INTERNAL, client, + send_err(mp, ECA_INTERNAL, client, "channel was in strange state or corrupted during cleanup"); SEND_UNLOCK(client); return RSRV_ERROR; } epicsMutexUnlock( client->chanListLock ); - + /* * remove from access control list */ status = asRemoveClient(&pciu->asClientPVT); if(status != 0 && status != S_asLib_asNotActive){ - errMessage(status, RECORD_NAME(&pciu->addr)); + errMessage(status, RECORD_NAME(pciu->dbch)); return RSRV_ERROR; } - + LOCK_CLIENTQ; status = bucketRemoveItemUnsignedId (pCaBucket, &pciu->sid); if(status != S_bucket_success){ @@ -2031,11 +2034,12 @@ static int clear_channel_reply ( caHdrLargeArray *mp, rsrvChannelCount--; UNLOCK_CLIENTQ; + dbChannelDelete(pciu->dbch); freeListFree(rsrvChanFreeList, pciu); - + return RSRV_OK; } - + /* * * event_cancel_reply() @@ -2049,7 +2053,7 @@ static int event_cancel_reply ( caHdrLargeArray *mp, void *pPayload, struct clie struct channel_in_use *pciu; struct event_ext *pevext; int status; - + /* * * Verify the channel @@ -2060,7 +2064,7 @@ static int event_cancel_reply ( caHdrLargeArray *mp, void *pPayload, struct clie logBadId ( client, mp, pPayload ); return RSRV_ERROR; } - + /* * search events on this channel for a match * (there are usually very few monitors per channel) @@ -2068,38 +2072,38 @@ static int event_cancel_reply ( caHdrLargeArray *mp, void *pPayload, struct clie epicsMutexMustLock(client->eventqLock); for (pevext = (struct event_ext *) ellFirst(&pciu->eventq); pevext; pevext = (struct event_ext *) ellNext(&pevext->node)){ - + if (pevext->msg.m_available == mp->m_available) { ellDelete(&pciu->eventq, &pevext->node); break; } } epicsMutexUnlock(client->eventqLock); - + /* - * Not Found- return an exception event + * Not Found- return an exception event */ if(!pevext){ SEND_LOCK(client); - send_err(mp, ECA_BADMONID, client, RECORD_NAME(&pciu->addr)); + send_err(mp, ECA_BADMONID, client, RECORD_NAME(pciu->dbch)); SEND_UNLOCK(client); return RSRV_ERROR; } - + /* * cancel monitor activity in progress */ if (pevext->pdbev) { db_cancel_event (pevext->pdbev); } - + /* * send delete confirmed message */ SEND_LOCK(client); - status = cas_copy_in_header ( client, pevext->msg.m_cmmd, - 0u, pevext->msg.m_dataType, pevext->msg.m_count, pevext->msg.m_cid, + status = cas_copy_in_header ( client, pevext->msg.m_cmmd, + 0u, pevext->msg.m_dataType, pevext->msg.m_count, pevext->msg.m_cid, pevext->msg.m_available, NULL ); if ( status != ECA_NORMAL ) { SEND_UNLOCK(client); @@ -2107,7 +2111,7 @@ static int event_cancel_reply ( caHdrLargeArray *mp, void *pPayload, struct clie } cas_commit_msg ( client, 0 ); SEND_UNLOCK(client); - + freeListFree (rsrvEventFreeList, pevext); return RSRV_OK; @@ -2120,8 +2124,8 @@ static int read_sync_reply ( caHdrLargeArray *mp, void *pPayload, struct client { int status; SEND_LOCK(client); - status = cas_copy_in_header ( client, mp->m_cmmd, - 0u, mp->m_dataType, mp->m_count, mp->m_cid, + status = cas_copy_in_header ( client, mp->m_cmmd, + 0u, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, NULL ); if ( status != ECA_NORMAL ) { SEND_UNLOCK(client); @@ -2132,17 +2136,17 @@ static int read_sync_reply ( caHdrLargeArray *mp, void *pPayload, struct client return RSRV_OK; } -/* +/* * search_fail_reply() * - * Only when requested by the client + * Only when requested by the client * send search failed reply */ static void search_fail_reply ( caHdrLargeArray *mp, void *pPayload, struct client *client) { int status; SEND_LOCK ( client ); - status = cas_copy_in_header ( client, CA_PROTO_NOT_FOUND, + status = cas_copy_in_header ( client, CA_PROTO_NOT_FOUND, 0u, mp->m_dataType, mp->m_count, mp->m_cid, mp->m_available, NULL ); if ( status != ECA_NORMAL ) { SEND_UNLOCK ( client ); @@ -2180,16 +2184,16 @@ int rsrv_version_reply ( struct client *client ) SEND_LOCK ( client ); /* * sequence number is specified zero when we copy in the - * header because we dont know it until we receive a datagram + * header because we dont know it until we receive a datagram * from the client */ - status = cas_copy_in_header ( client, CA_PROTO_VERSION, - 0, 0, CA_MINOR_PROTOCOL_REVISION, + status = cas_copy_in_header ( client, CA_PROTO_VERSION, + 0, 0, CA_MINOR_PROTOCOL_REVISION, 0, 0, 0 ); if ( status != ECA_NORMAL ) { SEND_UNLOCK ( client ); return RSRV_ERROR; - } + } cas_commit_msg ( client, 0 ); SEND_UNLOCK ( client ); return RSRV_OK; @@ -2200,7 +2204,6 @@ int rsrv_version_reply ( struct client *client ) */ static int search_reply_udp ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { - struct dbAddr tmp_addr; ca_uint16_t *pMinorVersion; char *pName = (char *) pPayload; int status; @@ -2215,35 +2218,34 @@ static int search_reply_udp ( caHdrLargeArray *mp, void *pPayload, struct client * check the sanity of the message */ if (mp->m_postsize<=1) { - log_header ("empty PV name in UDP search request?", + log_header ("empty PV name in UDP search request?", client, mp, pPayload, 0); return RSRV_OK; } pName[mp->m_postsize-1] = '\0'; - + /* Exit quickly if channel not on this node */ - status = db_name_to_addr (pName, &tmp_addr); - if (status) { + if (dbChannelTest(pName)) { DLOG ( 2, ( "CAS: Lookup for channel \"%s\" failed\n", pPayLoad ) ); if (mp->m_dataType == DOREPLY) search_fail_reply ( mp, pPayload, client ); return RSRV_OK; } - + /* - * stop further use of server if memory becomes scarse + * stop further use of server if memory becomes scarce */ spaceAvailOnFreeList = freeListItemsAvail ( rsrvChanFreeList ) > 0 && freeListItemsAvail ( rsrvEventFreeList ) > reasonableMonitorSpace; - spaceNeeded = sizeof (struct channel_in_use) + + spaceNeeded = sizeof (struct channel_in_use) + reasonableMonitorSpace * sizeof (struct event_ext); - if ( ! ( osiSufficentSpaceInPool(spaceNeeded) || spaceAvailOnFreeList ) ) { + if ( ! ( osiSufficentSpaceInPool(spaceNeeded) || spaceAvailOnFreeList ) ) { SEND_LOCK(client); send_err ( mp, ECA_ALLOCMEM, client, "Server memory exhausted" ); SEND_UNLOCK(client); return RSRV_OK; } - + /* * starting with V4.4 the count field is used (abused) * to store the minor version number of the client. @@ -2259,49 +2261,58 @@ static int search_reply_udp ( caHdrLargeArray *mp, void *pPayload, struct client type = ca_server_port; } else { + struct dbChannel *dbch; struct channel_in_use *pchannel; - - pchannel = casCreateChannel ( client, &tmp_addr, mp->m_cid ); + + dbch = dbChannel_create(pName); + if (!dbch) { + DLOG ( 2, ( "CAS: dbChannel Test of \"%s\" OK but Create failed\n", pName ) ); + if (mp->m_dataType == DOREPLY) + search_fail_reply ( mp, pPayload, client ); + return RSRV_OK; + } + pchannel = casCreateChannel ( client, dbch, mp->m_cid ); if (!pchannel) { SEND_LOCK(client); - send_err ( mp, ECA_ALLOCMEM, client, - RECORD_NAME ( &tmp_addr ) ); + send_err ( mp, ECA_ALLOCMEM, client, + RECORD_NAME ( dbch ) ); SEND_UNLOCK ( client ); + dbChannelDelete(dbch); return RSRV_OK; } sid = pchannel->sid; - if ( tmp_addr.no_elements < 0 ) { + if ( dbChannelElements(dbch) < 0 ) { count = 0; } - else if ( tmp_addr.no_elements > 0xffff ) { + else if ( dbChannelElements(dbch) > 0xffff ) { count = 0xfffe; } else { - count = (ca_uint16_t) tmp_addr.no_elements; + count = (ca_uint16_t) dbChannelElements(dbch); } - type = (ca_uint16_t) tmp_addr.dbr_field_type; + type = (ca_uint16_t) dbChannelExportType(dbch); } - + SEND_LOCK ( client ); - status = cas_copy_in_header ( client, CA_PROTO_SEARCH, - sizeof(*pMinorVersion), type, count, - sid, mp->m_available, + status = cas_copy_in_header ( client, CA_PROTO_SEARCH, + sizeof(*pMinorVersion), type, count, + sid, mp->m_available, ( void * ) &pMinorVersion ); if ( status != ECA_NORMAL ) { SEND_UNLOCK ( client ); return RSRV_ERROR; } - + /* * Starting with CA V4.1 the minor version number * is appended to the end of each search reply. - * This value is ignored by earlier clients. + * This value is ignored by earlier clients. */ *pMinorVersion = htons ( CA_MINOR_PROTOCOL_REVISION ); - + cas_commit_msg ( client, sizeof ( *pMinorVersion ) ); SEND_UNLOCK ( client ); - + return RSRV_OK; } @@ -2370,7 +2381,7 @@ typedef int (*pProtoStubTCP) (caHdrLargeArray *mp, void *pPayload, struct client /* * TCP protocol jump table */ -static const pProtoStubTCP tcpJumpTable[] = +static const pProtoStubTCP tcpJumpTable[] = { tcp_version_action, event_add_action, @@ -2406,7 +2417,7 @@ static const pProtoStubTCP tcpJumpTable[] = * UDP protocol jump table */ typedef int (*pProtoStubUDP) (caHdrLargeArray *mp, void *pPayload, struct client *client); -static const pProtoStubUDP udpJumpTable[] = +static const pProtoStubUDP udpJumpTable[] = { udp_version_action, bad_udp_cmd_action, @@ -2447,7 +2458,7 @@ int camessage ( struct client *client ) unsigned msgsize; unsigned bytes_left; int status = RSRV_ERROR; - + if ( ! pCaBucket ) { pCaBucket = bucketCreate(CAS_HASH_TABLE_SIZE); if(!pCaBucket){ @@ -2467,11 +2478,11 @@ int camessage ( struct client *client ) client->recvBytesToDrain = 0u; } } - + DLOG ( 2, ( "CAS: Parsing %d(decimal) bytes\n", recv->cnt ) ); - + while ( 1 ) - { + { caHdrLargeArray msg; caHdr *mp; void *pBody; @@ -2482,7 +2493,7 @@ int camessage ( struct client *client ) status = RSRV_OK; break; } - + mp = (caHdr *) &client->recv.buf[client->recv.stk]; msg.m_cmmd = ntohs ( mp->m_cmmd ); msg.m_postsize = ntohs ( mp->m_postsize ); @@ -2508,13 +2519,13 @@ int camessage ( struct client *client ) } /* - * disconnect clients that dont send 8 byte + * disconnect clients that dont send 8 byte * aligned payloads */ if ( msgsize & 0x7 ) { - send_err ( &msg, ECA_INTERNAL, client, + send_err ( &msg, ECA_INTERNAL, client, "CAS: Missaligned protocol rejected" ); - log_header ( "CAS: Missaligned protocol rejected", + log_header ( "CAS: Missaligned protocol rejected", client, &msg, 0, nmsg ); status = RSRV_ERROR; break; @@ -2529,10 +2540,10 @@ int camessage ( struct client *client ) if ( msgsize > client->recv.maxstk ) { casExpandRecvBuffer ( client, msgsize ); if ( msgsize > client->recv.maxstk ) { - send_err ( &msg, ECA_TOLARGE, client, + send_err ( &msg, ECA_TOLARGE, client, "CAS: Server unable to load large request message. Max bytes=%lu", rsrvSizeofLargeBufTCP ); - log_header ( "CAS: server unable to load large request message", + log_header ( "CAS: server unable to load large request message", client, &msg, 0, nmsg ); assert ( client->recv.cnt <= client->recv.maxstk ); assert ( msgsize >= bytes_left ); @@ -2550,12 +2561,12 @@ int camessage ( struct client *client ) status = RSRV_OK; break; } - + nmsg++; - + if ( CASDEBUG > 2 ) log_header (NULL, client, &msg, pBody, nmsg); - + if ( client == prsrv_cast_client ) { if ( msg.m_cmmd < NELEMENTS ( udpJumpTable ) ) { status = ( *udpJumpTable[msg.m_cmmd] )( &msg, pBody, client ); @@ -2581,10 +2592,10 @@ int camessage ( struct client *client ) return bad_tcp_cmd_action ( &msg, pBody, client ); } } - + client->recv.stk += msgsize; } - + return status; } @@ -2595,8 +2606,8 @@ int rsrvCheckPut (const struct channel_in_use *pciu) { /* * SPC_NOMOD fields are always unwritable - */ - if (pciu->addr.special==SPC_NOMOD) { + */ + if (dbChannelSpecial(pciu->dbch) == SPC_NOMOD) { return 0; } else { diff --git a/src/ioc/rsrv/caservertask.c b/src/ioc/rsrv/caservertask.c index 21bcba9f4..5554d9ad7 100644 --- a/src/ioc/rsrv/caservertask.c +++ b/src/ioc/rsrv/caservertask.c @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* @@ -33,7 +33,6 @@ #include "freeList.h" #include "errlog.h" #include "db_field_log.h" -#include "dbAddr.h" #include "dbEvent.h" #include "dbCommon.h" #include "epicsStdioRedirect.h" @@ -73,22 +72,22 @@ static void req_server (void *pParm) epicsSignalInstallSigPipeIgnore (); taskwdInsert ( epicsThreadGetIdSelf (), NULL, NULL ); - + rsrvCurrentClient = epicsThreadPrivateCreate (); if ( envGetConfigParamPtr ( &EPICS_CAS_SERVER_PORT ) ) { - ca_server_port = envGetInetPortConfigParam ( &EPICS_CAS_SERVER_PORT, + ca_server_port = envGetInetPortConfigParam ( &EPICS_CAS_SERVER_PORT, (unsigned short) CA_SERVER_PORT ); } else { - ca_server_port = envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT, + ca_server_port = envGetInetPortConfigParam ( &EPICS_CA_SERVER_PORT, (unsigned short) CA_SERVER_PORT ); } if (IOC_sock != 0 && IOC_sock != INVALID_SOCKET) { epicsSocketDestroy ( IOC_sock ); } - + /* * Open the socket. Use ARPA Internet address format and stream * sockets. Format described in . @@ -103,7 +102,7 @@ static void req_server (void *pParm) /* Zero the sock_addr structure */ memset ( (void *) &serverAddr, 0, sizeof ( serverAddr ) ); serverAddr.sin_family = AF_INET; - serverAddr.sin_addr.s_addr = htonl (INADDR_ANY); + serverAddr.sin_addr.s_addr = htonl (INADDR_ANY); serverAddr.sin_port = htons ( ca_server_port ); /* get server's Internet address */ @@ -116,12 +115,12 @@ static void req_server (void *pParm) * work correctly) */ serverAddr.sin_port = ntohs (0); - status = bind ( IOC_sock, + status = bind ( IOC_sock, (struct sockaddr *) &serverAddr, sizeof ( serverAddr ) ); } if ( status < 0 ) { char sockErrBuf[64]; - epicsSocketConvertErrnoToString ( + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf ( "CAS: Socket bind error was \"%s\"\n", sockErrBuf ); @@ -134,13 +133,13 @@ static void req_server (void *pParm) } addrSize = ( osiSocklen_t ) sizeof ( serverAddr ); - status = getsockname ( IOC_sock, + status = getsockname ( IOC_sock, (struct sockaddr *)&serverAddr, &addrSize); if ( status ) { char sockErrBuf[64]; - epicsSocketConvertErrnoToString ( + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); - errlogPrintf ( "CAS: getsockname() error %s\n", + errlogPrintf ( "CAS: getsockname() error %s\n", sockErrBuf ); epicsThreadSuspendSelf (); } @@ -149,7 +148,7 @@ static void req_server (void *pParm) if ( portChange ) { errlogPrintf ( "cas warning: Configured TCP port was unavailable.\n"); - errlogPrintf ( "cas warning: Using dynamically assigned TCP port %hu,\n", + errlogPrintf ( "cas warning: Using dynamically assigned TCP port %hu,\n", ca_server_port ); errlogPrintf ( "cas warning: but now two or more servers share the same UDP port.\n"); errlogPrintf ( "cas warning: Depending on your IP kernel this server may not be\n" ); @@ -192,13 +191,13 @@ static void req_server (void *pParm) clientSock = epicsSocketAccept ( IOC_sock, &sockAddr, &addLen ); if ( clientSock == INVALID_SOCKET ) { char sockErrBuf[64]; - epicsSocketConvertErrnoToString ( + epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) ); errlogPrintf("CAS: Client accept error was \"%s\"\n", sockErrBuf ); epicsThreadSleep(15.0); continue; - } + } else { epicsThreadId id; struct client *pClient; @@ -282,14 +281,14 @@ int rsrv_init (void) castcp_ctl = ctlPause; /* - * go down two levels so that we are below + * go down two levels so that we are below * the TCP and event threads started on behalf * of individual clients */ - tbs = epicsThreadHighestPriorityLevelBelow ( + tbs = epicsThreadHighestPriorityLevelBelow ( epicsThreadPriorityCAServerLow, &priorityOfConnectDaemon ); if ( tbs == epicsThreadBooleanStatusSuccess ) { - tbs = epicsThreadHighestPriorityLevelBelow ( + tbs = epicsThreadHighestPriorityLevelBelow ( priorityOfConnectDaemon, &priorityOfConnectDaemon ); if ( tbs != epicsThreadBooleanStatusSuccess ) { priorityOfConnectDaemon = epicsThreadPriorityCAServerLow; @@ -330,7 +329,7 @@ int rsrv_pause (void) return RSRV_OK; } -static unsigned countChanListBytes ( +static unsigned countChanListBytes ( struct client *client, ELLLIST * pList ) { struct channel_in_use * pciu; @@ -349,7 +348,7 @@ static unsigned countChanListBytes ( return bytes_reserved; } -static void showChanList ( +static void showChanList ( struct client * client, ELLLIST * pList ) { unsigned i = 0u; @@ -357,8 +356,8 @@ static void showChanList ( epicsMutexMustLock ( client->chanListLock ); pciu = (struct channel_in_use *) pList->node.next; while ( pciu ){ - printf( "\t%s(%d%c%c)", - pciu->addr.precord->name, + printf( "\t%s(%d%c%c)", + dbChannelRecord(pciu->dbch)->name, ellCount ( &pciu->eventq ), asCheckGet ( pciu->asClientPVT ) ? 'r': '-', rsrvCheckPut ( pciu ) ? 'w': '-' ); @@ -398,28 +397,28 @@ static void log_one_client (struct client *client, unsigned level) send_delay = epicsTimeDiffInSeconds(¤t,&client->time_at_last_send); recv_delay = epicsTimeDiffInSeconds(¤t,&client->time_at_last_recv); - printf ( "%s %s(%s): User=\"%s\", V%u.%u, %d Channels, Priority=%u\n", + printf ( "%s %s(%s): User=\"%s\", V%u.%u, %d Channels, Priority=%u\n", pproto, clientHostName, client->pHostName ? client->pHostName : "", client->pUserName ? client->pUserName : "", CA_MAJOR_PROTOCOL_REVISION, client->minor_version_number, - ellCount(&client->chanList) + + ellCount(&client->chanList) + ellCount(&client->chanPendingUpdateARList), client->priority ); if ( level >= 1 ) { - printf ("\tTask Id=%p, Socket FD=%d\n", - (void *) client->tid, client->sock); - printf( - "\tSecs since last send %6.2f, Secs since last receive %6.2f\n", + printf ("\tTask Id=%p, Socket FD=%d\n", + (void *) client->tid, client->sock); + printf( + "\tSecs since last send %6.2f, Secs since last receive %6.2f\n", send_delay, recv_delay); - printf( - "\tUnprocessed request bytes=%u, Undelivered response bytes=%u\n", + printf( + "\tUnprocessed request bytes=%u, Undelivered response bytes=%u\n", client->recv.cnt - client->recv.stk, client->send.stk ); - printf( - "\tState=%s%s%s\n", + printf( + "\tState=%s%s%s\n", state[client->disconnect?1:0], client->send.type == mbtLargeTCP ? " jumbo-send-buf" : "", client->recv.type == mbtLargeTCP ? " jumbo-recv-buf" : ""); @@ -428,9 +427,9 @@ static void log_one_client (struct client *client, unsigned level) if ( level >= 2u ) { unsigned bytes_reserved = 0; bytes_reserved += sizeof(struct client); - bytes_reserved += countChanListBytes ( + bytes_reserved += countChanListBytes ( client, & client->chanList ); - bytes_reserved += countChanListBytes ( + bytes_reserved += countChanListBytes ( client, & client->chanPendingUpdateARList ); printf( "\t%d bytes allocated\n", bytes_reserved); showChanList ( client, & client->chanList ); @@ -485,20 +484,20 @@ void epicsShareAPI casr (unsigned level) printf( "UDP Server:\n" ); log_one_client(prsrv_cast_client, level); } - + if (level>=2u) { bytes_reserved = 0u; - bytes_reserved += sizeof (struct client) * + bytes_reserved += sizeof (struct client) * freeListItemsAvail (rsrvClientFreeList); bytes_reserved += sizeof (struct channel_in_use) * freeListItemsAvail (rsrvChanFreeList); bytes_reserved += sizeof(struct event_ext) * freeListItemsAvail (rsrvEventFreeList); - bytes_reserved += MAX_TCP * + bytes_reserved += MAX_TCP * freeListItemsAvail ( rsrvSmallBufFreeListTCP ); - bytes_reserved += rsrvSizeofLargeBufTCP * + bytes_reserved += rsrvSizeofLargeBufTCP * freeListItemsAvail ( rsrvLargeBufFreeListTCP ); - bytes_reserved += rsrvSizeOfPutNotify ( 0 ) * + bytes_reserved += rsrvSizeOfPutNotify ( 0 ) * freeListItemsAvail ( rsrvPutNotifyFreeList ); printf( "There are currently %u bytes on the server's free list\n", (unsigned int) bytes_reserved); @@ -525,7 +524,7 @@ void epicsShareAPI casr (unsigned level) } } -/* +/* * destroy_client () */ void destroy_client ( struct client *client ) @@ -533,7 +532,7 @@ void destroy_client ( struct client *client ) if ( ! client ) { return; } - + if ( client->tid != 0 ) { taskwdRemove ( client->tid ); } @@ -603,12 +602,12 @@ void destroy_client ( struct client *client ) if ( client->pHostName ) { free ( client->pHostName ); - } + } freeListFree ( rsrvClientFreeList, client ); } -static void destroyAllChannels ( +static void destroyAllChannels ( struct client * client, ELLLIST * pList ) { if ( !client->chanListLock || !client->eventqLock ) { @@ -651,7 +650,7 @@ static void destroyAllChannels ( rsrvChannelCount--; UNLOCK_CLIENTQ; if ( status != S_bucket_success ) { - errPrintf ( status, __FILE__, __LINE__, + errPrintf ( status, __FILE__, __LINE__, "Bad id=%d at close", pciu->sid); } status = asRemoveClient(&pciu->asClientPVT); @@ -691,7 +690,7 @@ void destroy_tcp_client ( struct client *client ) if ( client->evuser ) { db_close_events (client->evuser); } - + destroy_client ( client ); } @@ -710,7 +709,7 @@ struct client * create_client ( SOCKET sock, int proto ) spaceAvailOnFreeList = freeListItemsAvail ( rsrvClientFreeList ) > 0 && freeListItemsAvail ( rsrvSmallBufFreeListTCP ) > 0; spaceNeeded = sizeof (struct client) + MAX_TCP; - if ( ! ( osiSufficentSpaceInPool(spaceNeeded) || spaceAvailOnFreeList ) ) { + if ( ! ( osiSufficentSpaceInPool(spaceNeeded) || spaceAvailOnFreeList ) ) { epicsSocketDestroy ( sock ); epicsPrintf ("CAS: no space in pool for a new client (below max block thresh)\n"); return NULL; @@ -721,7 +720,7 @@ struct client * create_client ( SOCKET sock, int proto ) epicsSocketDestroy ( sock ); epicsPrintf ("CAS: no space in pool for a new client (alloc failed)\n"); return NULL; - } + } client->sock = sock; client->proto = proto; @@ -737,8 +736,8 @@ struct client * create_client ( SOCKET sock, int proto ) return NULL; } - client->pUserName = NULL; - client->pHostName = NULL; + client->pUserName = NULL; + client->pHostName = NULL; ellInit ( & client->chanList ); ellInit ( & client->chanPendingUpdateARList ); ellInit ( & client->putNotifyQue ); @@ -776,7 +775,7 @@ struct client * create_client ( SOCKET sock, int proto ) epicsTimeGetCurrent ( &client->time_at_last_recv ); client->minor_version_number = CA_UKN_MINOR_VERSION; client->recvBytesToDrain = 0u; - + return client; } @@ -791,10 +790,10 @@ void casAttachThreadToClient ( struct client *pClient ) void casExpandSendBuffer ( struct client *pClient, ca_uint32_t size ) { - if ( pClient->send.type == mbtSmallTCP && rsrvSizeofLargeBufTCP > MAX_TCP + if ( pClient->send.type == mbtSmallTCP && rsrvSizeofLargeBufTCP > MAX_TCP && size <= rsrvSizeofLargeBufTCP ) { int spaceAvailOnFreeList = freeListItemsAvail ( rsrvLargeBufFreeListTCP ) > 0; - if ( osiSufficentSpaceInPool(rsrvSizeofLargeBufTCP) || spaceAvailOnFreeList ) { + if ( osiSufficentSpaceInPool(rsrvSizeofLargeBufTCP) || spaceAvailOnFreeList ) { char *pNewBuf = ( char * ) freeListCalloc ( rsrvLargeBufFreeListTCP ); if ( pNewBuf ) { memcpy ( pNewBuf, pClient->send.buf, pClient->send.stk ); @@ -812,7 +811,7 @@ void casExpandRecvBuffer ( struct client *pClient, ca_uint32_t size ) if ( pClient->recv.type == mbtSmallTCP && rsrvSizeofLargeBufTCP > MAX_TCP && size <= rsrvSizeofLargeBufTCP) { int spaceAvailOnFreeList = freeListItemsAvail ( rsrvLargeBufFreeListTCP ) > 0; - if ( osiSufficentSpaceInPool(rsrvSizeofLargeBufTCP) || spaceAvailOnFreeList ) { + if ( osiSufficentSpaceInPool(rsrvSizeofLargeBufTCP) || spaceAvailOnFreeList ) { char *pNewBuf = ( char * ) freeListCalloc ( rsrvLargeBufFreeListTCP ); if ( pNewBuf ) { assert ( pClient->recv.cnt >= pClient->recv.stk ); @@ -849,26 +848,26 @@ struct client *create_tcp_client ( SOCKET sock ) * see TCP(4P) this seems to make unsolicited single events much * faster. I take care of queue up as load increases. */ - status = setsockopt ( sock, IPPROTO_TCP, TCP_NODELAY, + status = setsockopt ( sock, IPPROTO_TCP, TCP_NODELAY, (char *) &intTrue, sizeof (intTrue) ); if (status < 0) { errlogPrintf ( "CAS: TCP_NODELAY option set failed\n" ); destroy_client ( client ); return NULL; } - - /* + + /* * turn on KEEPALIVE so if the client crashes * this task will find out and exit */ - status = setsockopt ( sock, SOL_SOCKET, SO_KEEPALIVE, + status = setsockopt ( sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &intTrue, sizeof (intTrue) ); if ( status < 0 ) { errlogPrintf ( "CAS: SO_KEEPALIVE option set failed\n" ); destroy_client ( client ); return NULL; } - + /* * some concern that vxWorks will run out of mBuf's * if this change is made @@ -876,8 +875,8 @@ struct client *create_tcp_client ( SOCKET sock ) * joh 11-10-98 */ #if 0 - /* - * set TCP buffer sizes to be synergistic + /* + * set TCP buffer sizes to be synergistic * with CA internal buffering */ i = MAX_MSG_SIZE; @@ -895,7 +894,7 @@ struct client *create_tcp_client ( SOCKET sock ) return NULL; } #endif - + addrSize = sizeof ( client->addr ); status = getpeername ( sock, (struct sockaddr *)&client->addr, &addrSize ); @@ -928,8 +927,8 @@ struct client *create_tcp_client ( SOCKET sock ) } } - status = db_start_events ( client->evuser, "CAS-event", - NULL, NULL, priorityOfEvents ); + status = db_start_events ( client->evuser, "CAS-event", + NULL, NULL, priorityOfEvents ); if ( status != DB_EVENT_OK ) { errlogPrintf ( "CAS: unable to start the event facility\n" ); destroy_tcp_client ( client ); diff --git a/src/ioc/rsrv/server.h b/src/ioc/rsrv/server.h index 6d1dfc38f..b1a6c7b24 100644 --- a/src/ioc/rsrv/server.h +++ b/src/ioc/rsrv/server.h @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* @@ -25,7 +25,7 @@ #include "epicsEvent.h" #include "bucketLib.h" #include "asLib.h" -#include "dbAddr.h" +#include "dbChannel.h" #include "dbNotify.h" #define CA_MINOR_PROTOCOL_REVISION 13 #include "caProto.h" @@ -64,7 +64,7 @@ struct message_buffer { char *buf; unsigned stk; unsigned maxstk; - unsigned cnt; + unsigned cnt; enum messageBufferType type; }; @@ -98,16 +98,16 @@ typedef struct client { char disconnect; /* disconnect detected */ } client; -enum rsrvChanState { +enum rsrvChanState { rsrvCS_invalid, rsrvCS_pendConnectResp, rsrvCS_inService, rsrvCS_pendConnectRespUpdatePendAR, - rsrvCS_inServiceUpdatePendAR + rsrvCS_inServiceUpdatePendAR }; /* - * per channel structure + * per channel structure * (stored in chanList or chanPendingUpdateARList off of a client block) */ struct channel_in_use { @@ -118,7 +118,7 @@ struct channel_in_use { const unsigned cid; /* client id */ const unsigned sid; /* server id */ epicsTimeStamp time_at_creation; /* for UDP timeout */ - struct dbAddr addr; + struct dbChannel *dbch; ASCLIENTPVT asClientPVT; enum rsrvChanState state; }; @@ -156,7 +156,7 @@ enum ctl {ctlRun, ctlPause, ctlExit}; # define DLOG(LEVEL,ARGSINPAREN) \ if (CASDEBUG > LEVEL) errlogPrintf ARGSINPAREN #else -# define DLOG(LEVEL,ARGSINPAREN) +# define DLOG(LEVEL,ARGSINPAREN) #endif GLBLTYPE int CASDEBUG; @@ -168,13 +168,13 @@ GLBLTYPE ELLLIST beaconAddrList; GLBLTYPE epicsMutexId clientQlock; GLBLTYPE struct client *prsrv_cast_client; GLBLTYPE BUCKET *pCaBucket; -GLBLTYPE void *rsrvClientFreeList; +GLBLTYPE void *rsrvClientFreeList; GLBLTYPE void *rsrvChanFreeList; GLBLTYPE void *rsrvEventFreeList; -GLBLTYPE void *rsrvSmallBufFreeListTCP; -GLBLTYPE void *rsrvLargeBufFreeListTCP; +GLBLTYPE void *rsrvSmallBufFreeListTCP; +GLBLTYPE void *rsrvLargeBufFreeListTCP; GLBLTYPE unsigned rsrvSizeofLargeBufTCP; -GLBLTYPE void *rsrvPutNotifyFreeList; +GLBLTYPE void *rsrvPutNotifyFreeList; GLBLTYPE unsigned rsrvChannelCount; GLBLTYPE epicsEventId casudp_startStopEvent; @@ -207,7 +207,7 @@ int camessage ( struct client *client ); void rsrv_extra_labor ( void * pArg ); int rsrvCheckPut ( const struct channel_in_use *pciu ); int rsrv_version_reply ( struct client *client ); -void rsrvFreePutNotify ( struct client *pClient, +void rsrvFreePutNotify ( struct client *pClient, struct rsrv_put_notify *pNotify ); void initializePutNotifyFreeList (void); unsigned rsrvSizeOfPutNotify ( struct rsrv_put_notify *pNotify ); @@ -221,9 +221,9 @@ void casExpandRecvBuffer ( struct client *pClient, ca_uint32_t size ); * outgoing protocol maintenance */ void casExpandSendBuffer ( struct client *pClient, ca_uint32_t size ); -int cas_copy_in_header ( +int cas_copy_in_header ( struct client *pClient, ca_uint16_t response, ca_uint32_t payloadSize, - ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, + ca_uint16_t dataType, ca_uint32_t nElem, ca_uint32_t cid, ca_uint32_t responseSpecific, void **pPayload ); void cas_set_header_cid ( struct client *pClient, ca_uint32_t ); void cas_set_header_count (struct client *pClient, ca_uint32_t count); From e37c5c99bb2061f7ecd02431f0b22eb3c3949f1a Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:21:42 -0400 Subject: [PATCH 12/81] adapt db_get_field_and_count to dbChannel_get_count --- src/ioc/db/db_access.c | 9 +++++---- src/ioc/db/db_access_routines.h | 5 ++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ioc/db/db_access.c b/src/ioc/db/db_access.c index 479d501fa..3ac6660ff 100644 --- a/src/ioc/db/db_access.c +++ b/src/ioc/db/db_access.c @@ -127,8 +127,8 @@ int dbChannel_get(struct dbChannel *chan, int buffer_type, void *pbuffer, long no_elements, void *pfl) { long nRequest = no_elements; - int result = db_get_field_and_count( - paddr, buffer_type, pbuffer, &nRequest, pfl); + int result = dbChannel_get_count( + chan, buffer_type, pbuffer, &nRequest, pfl); if (nRequest < no_elements) { /* The database request returned fewer elements than requested, so * fill the remainder of the buffer with zeros. @@ -145,10 +145,11 @@ int dbChannel_get(struct dbChannel *chan, /* Performs the work of the public db_get_field API, but also returns the number * of elements actually copied to the buffer. The caller is responsible for * zeroing the remaining part of the buffer. */ -int epicsShareAPI db_get_field_and_count( - struct dbAddr *paddr, int buffer_type, +int epicsShareAPI dbChannel_get_count( + struct dbChannel *chan, int buffer_type, void *pbuffer, long *nRequest, void *pfl) { + dbAddr *paddr=&chan->addr; long status; long options; long i; diff --git a/src/ioc/db/db_access_routines.h b/src/ioc/db/db_access_routines.h index efe20b95c..380afb0fa 100644 --- a/src/ioc/db/db_access_routines.h +++ b/src/ioc/db/db_access_routines.h @@ -35,9 +35,8 @@ epicsShareFunc int dbChannel_get(struct dbChannel *chan, int buffer_type, void *pbuffer, long no_elements, void *pfl); epicsShareFunc int dbChannel_put(struct dbChannel *chan, int src_type, const void *psrc, long no_elements); -epicsShareFunc int epicsShareAPI db_get_field_and_count( - struct dbAddr *paddr, int buffer_type, - void *pbuffer, long *nRequest, void *pfl); +epicsShareFunc int epicsShareAPI dbChannel_get_count(struct dbChannel *chan, + int buffer_type, void *pbuffer, long *nRequest, void *pfl); #ifdef __cplusplus From b4dbcf2b1a05f20aa077fec3e5411122c8510c5f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:42 -0400 Subject: [PATCH 13/81] Don't parse field modifiers in dbChannelTest() dbChannelDelete() returns void update tests. --- src/ioc/db/dbChannel.c | 103 ++++++++++++-------------------- src/ioc/db/dbChannel.h | 2 +- src/ioc/db/test/dbChannelTest.c | 49 +++++++++------ 3 files changed, 70 insertions(+), 84 deletions(-) diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index 97fe9ec91..a8bb291bb 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -211,32 +211,30 @@ static const yajl_callbacks chf_callbacks = static const yajl_parser_config chf_config = { 0, 1 }; /* allowComments = NO , checkUTF8 = YES */ -static long json_validate(const char *json) +static void * chf_malloc(void *ctx, unsigned int sz) { - yajl_handle yh = yajl_alloc(NULL, &chf_config, NULL, NULL); - size_t jlen = strlen(json); - yajl_status ys; - - if (!yh) - return S_db_noMemory; - - ys = yajl_parse(yh, (const unsigned char *) json, jlen); - if (ys == yajl_status_insufficient_data) - ys = yajl_parse_complete(yh); - - yajl_free(yh); - - if (ys == yajl_status_ok) - return 0; - - return S_db_notFound; + return malloc(sz); /* FIXME: free-list */ } -static long chf_parse(dbChannel *chan, const char *json) +static void * chf_realloc(void *ctx, void *ptr, unsigned int sz) +{ + return realloc(ptr, sz); /* FIXME: free-list */ +} + +static void chf_free(void *ctx, void *ptr) +{ + return free(ptr); /* FIXME: free-list */ +} + +static const yajl_alloc_funcs chf_alloc = + { chf_malloc, chf_realloc, chf_free }; + +static long chf_parse(dbChannel *chan, const char **pjson) { parseContext parser = { chan, NULL, 0 }; - yajl_handle yh = yajl_alloc(&chf_callbacks, &chf_config, NULL, &parser); + yajl_handle yh = yajl_alloc(&chf_callbacks, &chf_config, &chf_alloc, &parser); + const char *json = *pjson; size_t jlen = strlen(json); yajl_status ys; long status; @@ -251,13 +249,14 @@ static long chf_parse(dbChannel *chan, const char *json) switch (ys) { case yajl_status_ok: status = 0; + *pjson += yajl_get_bytes_consumed(yh); break; case yajl_status_error: { unsigned char *err; err = yajl_get_error(yh, 1, (const unsigned char *) json, jlen); - printf("dbChannelFind: %s\n", err); + printf("dbChannelCreate: %s\n", err); yajl_free_error(yh, err); } /* fall through */ default: @@ -267,7 +266,7 @@ static long chf_parse(dbChannel *chan, const char *json) if (parser.filter) { assert(status); parser.filter->fif->parse_abort(parser.filter); - free(parser.filter); + free(parser.filter); /* FIXME: free-list */ } yajl_free(yh); return status; @@ -302,37 +301,9 @@ long dbChannelTest(const char *name) return S_db_notFound; status = pvNameLookup(&dbEntry, &name); - if (status) - goto finish; - /* Test field modifiers */ - while (*name) { - switch (*name) { - case '$': - switch (dbEntry.pflddes->field_type) { - case DBF_STRING: - case DBF_INLINK: - case DBF_OUTLINK: - case DBF_FWDLINK: - break; - default: - status = S_dbLib_fieldNotFound; - goto finish; - } - break; - case '{': - status = json_validate(name); - goto finish; - default: - status = S_dbLib_fieldNotFound; - goto finish; - } - name++; - } - - finish: - dbFinishEntry(&dbEntry); - return status; + dbFinishEntry(&dbEntry); + return status; } /* Stolen from dbAccess.c: */ @@ -372,9 +343,9 @@ dbChannel * dbChannelCreate(const char *name) if (status) goto finish; - // FIXME: Use free-list + /* FIXME: Use free-list */ chan = (dbChannel *) callocMustSucceed(1, sizeof(*chan), "dbChannelCreate"); - chan->name = strdup(name); // FIXME? + chan->name = strdup(name); /* FIXME: free-list */ ellInit(&chan->filters); paddr = &chan->addr; @@ -391,9 +362,8 @@ dbChannel * dbChannelCreate(const char *name) paddr->dbr_field_type = mapDBFToDBR[dbfType]; /* Handle field modifiers */ - while (*pname) { - switch (*pname) { - case '$': + if (*pname) { + if (*pname == '$') { /* Some field types can be accessed as char arrays */ if (dbfType == DBF_STRING) { paddr->no_elements = pflddes->size; @@ -409,15 +379,20 @@ dbChannel * dbChannelCreate(const char *name) status = S_dbLib_fieldNotFound; goto finish; } - break; - case '{': - status = chf_parse(chan, pname); - goto finish; - default: + pname++; + } + + /* JSON may follow a $ */ + if (*pname == '{') { + status = chf_parse(chan, &pname); + if (status) goto finish; + } + + /* Make sure there's nothing else */ + if (*pname) { status = S_dbLib_fieldNotFound; goto finish; } - pname++; } if (paddr->special == SPC_DBADDR) { @@ -533,7 +508,7 @@ void dbChannelFilterShow(dbChannel *chan, int level) } } -long dbChannelDelete(dbChannel *chan) +void dbChannelDelete(dbChannel *chan) { chFilter *filter; diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index 3c5d2e2b3..b42f41410 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -102,7 +102,7 @@ epicsShareFunc long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer, long nRequest); epicsShareFunc void dbChannelShow(dbChannel *chan, int level); epicsShareFunc void dbChannelFilterShow(dbChannel *chan, int level); -epicsShareFunc long dbChannelDelete(dbChannel *chan); +epicsShareFunc void dbChannelDelete(dbChannel *chan); epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif); epicsShareFunc const chFilterIf * dbFindFilter(const char *key, size_t len); diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index 58db707c5..50543c038 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -132,31 +132,42 @@ MAIN(dbChannelTest) { dbChannel *pch; - testPlan(64); + testPlan(67); testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.dbx", ".:..", NULL)); testOk(!!pdbbase, "pdbbase was set"); r = e = 0; - /* Regular record and field names */ - testOk1(!dbChannelTest("x")); + /* dbChannelTest() checks record and field names */ testOk1(!dbChannelTest("x.NAME")); + testOk1(!dbChannelTest("x.VAL")); + testOk1(!dbChannelTest("x.")); + testOk1(!dbChannelTest("x")); + testOk(dbChannelTest("y"), "Test, nonexistent record"); + testOk(dbChannelTest("x.NOFIELD"), "Test, nonexistent field"); - /* Long string field modifier */ + /* dbChannelTest() allows but ignores field modifiers */ testOk1(!dbChannelTest("x.NAME$")); + testOk1(!dbChannelTest("x.{}")); + testOk1(!dbChannelTest("x.VAL{\"json\":true}")); + + /* dbChannelCreate() accepts field modifiers */ + testOk1(!!(pch = dbChannelCreate("x.{}"))); + if (pch) dbChannelDelete(pch); + testOk1(!!(pch = dbChannelCreate("x.VAL{}"))); + if (pch) dbChannelDelete(pch); testOk1(!!(pch = dbChannelCreate("x.NAME$"))); - testOk1(pch->addr.no_elements > 1); - testOk1(!dbChannelDelete(pch)); + testOk1(pch && pch->addr.no_elements > 1); + if (pch) dbChannelDelete(pch); + testOk1(!!(pch = dbChannelCreate("x.NAME${}"))); + testOk1(pch && pch->addr.no_elements > 1); + if (pch) dbChannelDelete(pch); - /* JSON field modifier validation */ - testOk1(!dbChannelTest("x.{\"json\":true}")); - - /* Ensure bad PVs get rejected */ - testOk(dbChannelTest("y"), "Test nonexistent record"); - testOk(dbChannelCreate("y") == NULL, "Create nonexistent record"); - - testOk1(dbChannelTest("x.{not-json}")); - testOk1(!dbChannelCreate("x.{\"none\":null}")); + /* dbChannelCreate() rejects bad PVs */ + testOk(!dbChannelCreate("y"), "Create, bad record"); + testOk(!dbChannelCreate("x.NOFIELD"), "Create, bad field"); + testOk(!dbChannelCreate("x.{not-json}"), "Create, bad JSON"); + testOk(!dbChannelCreate("x.{\"none\":null}"), "Create, bad filter"); dbRegisterFilter("any", &testIf); @@ -177,7 +188,7 @@ MAIN(dbChannelTest) e = e_start | e_null | e_end; testOk1(!!(pch = dbChannelCreate("x.{\"any\":null}"))); e = e_close; - testOk1(!dbChannelDelete(pch)); + if (pch) dbChannelDelete(pch); dbRegisterFilter("scalar", &testIf); @@ -188,19 +199,19 @@ MAIN(dbChannelTest) dbChannelShow(pch, 0); e = e_close; - testOk1(!dbChannelDelete(pch)); + if (pch) dbChannelDelete(pch); e = e_start | e_start_array | e_boolean | e_integer | e_end_array | e_end; testOk1(!!(pch = dbChannelCreate("x.{\"any\":[true,1]}"))); e = e_close; - testOk1(!dbChannelDelete(pch)); + if (pch) dbChannelDelete(pch); e = e_start | e_start_map | e_map_key | e_double | e_string | e_end_map | e_end; testOk1(!!(pch = dbChannelCreate("x.{\"any\":{\"a\":2.7183,\"b\":\"c\"}}"))); e = e_close; - testOk1(!dbChannelDelete(pch)); + if (pch) dbChannelDelete(pch); /* More event rejection */ r = r_scalar; From 1d274aba0dbdb6d7433024cb06995e2f11d87516 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:43 -0400 Subject: [PATCH 14/81] Make dbChannel_create() call dbChannelOpen() --- src/ioc/db/dbContext.cpp | 6 ------ src/ioc/db/db_access.c | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ioc/db/dbContext.cpp b/src/ioc/db/dbContext.cpp index 99f257e7e..38c982f84 100644 --- a/src/ioc/db/dbContext.cpp +++ b/src/ioc/db/dbContext.cpp @@ -109,12 +109,6 @@ cacChannel & dbContext::createChannel ( // X aCC 361 throw cacChannel::unsupportedByService (); } - long status = dbChannelOpen ( dbch ); - if (status) { - dbChannelDelete ( dbch ); - throw cacChannel::notConnected (); - } - try { return * new ( this->dbChannelIOFreeList ) dbChannelIO ( this->mutex, notifyIn, dbch, *this ); diff --git a/src/ioc/db/db_access.c b/src/ioc/db/db_access.c index 3ac6660ff..b705e0b35 100644 --- a/src/ioc/db/db_access.c +++ b/src/ioc/db/db_access.c @@ -120,6 +120,12 @@ struct dbChannel * dbChannel_create(const char *pname) } chan->addr.dbr_field_type = dbDBRnewToDBRold[ftype]; + + if (dbChannelOpen(chan)) { + dbChannelDelete(chan); + return NULL; + } + return chan; } From 07b3b7e32d64d4b929f24867b9912bcbcda2dce9 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:43 -0400 Subject: [PATCH 15/81] Make casr print the full channel name, not just the record name. --- src/ioc/rsrv/caservertask.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ioc/rsrv/caservertask.c b/src/ioc/rsrv/caservertask.c index 5554d9ad7..febd74f49 100644 --- a/src/ioc/rsrv/caservertask.c +++ b/src/ioc/rsrv/caservertask.c @@ -356,8 +356,8 @@ static void showChanList ( epicsMutexMustLock ( client->chanListLock ); pciu = (struct channel_in_use *) pList->node.next; while ( pciu ){ - printf( "\t%s(%d%c%c)", - dbChannelRecord(pciu->dbch)->name, + printf( "\t%s (%d%c%c)", + dbChannelName(pciu->dbch), ellCount ( &pciu->eventq ), asCheckGet ( pciu->asClientPVT ) ? 'r': '-', rsrvCheckPut ( pciu ) ? 'w': '-' ); From 4c2eba502aecb87753fda87acc580926fab2dd2c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:21:43 -0400 Subject: [PATCH 16/81] Rename dbChannelData() to dbChannelField() dbChannel_get() now uses dbChannelGetField() dbChannel_put() now uses dbChannelPutField() Conflicts: src/db/db_access.c --- src/ioc/db/dbChannel.c | 15 ++++- src/ioc/db/dbChannel.h | 4 +- src/ioc/db/dbChannelIO.cpp | 2 +- src/ioc/db/dbEvent.c | 6 +- src/ioc/db/db_access.c | 117 ++++++++++++++++++------------------- src/ioc/db/db_test.c | 4 +- 6 files changed, 79 insertions(+), 69 deletions(-) diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index a8bb291bb..4010b7a9d 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -469,20 +469,29 @@ short dbChannelSpecial(dbChannel *chan) return chan->addr.special; } -void * dbChannelData(dbChannel *chan) +void * dbChannelField(dbChannel *chan) { void * pdata = chan->addr.pfield; /* FIXME: offer to filters? */ return pdata; } -long dbChannelPut(dbChannel *chan, short type, const void *pbuffer, long nRequest) +long dbChannelGetField(dbChannel *chan, short type, void *pbuffer, + long *options, long *nRequest, void *pfl) +{ + /* FIXME: offer to filters? */ + return dbGetField(&chan->addr, type, pbuffer, options, nRequest, pfl); +} + +long dbChannelPut(dbChannel *chan, short type, const void *pbuffer, + long nRequest) { /* FIXME: offer to filters? */ return dbPut(&chan->addr, type, pbuffer, nRequest); } -long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer, long nRequest) +long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer, + long nRequest) { /* FIXME: offer to filters? */ return dbPutField(&chan->addr, type, pbuffer, nRequest); diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index b42f41410..82c79abb2 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -95,7 +95,9 @@ epicsShareFunc short dbChannelFieldType(dbChannel *chan); epicsShareFunc short dbChannelExportType(dbChannel *chan); epicsShareFunc short dbChannelElementSize(dbChannel *chan); epicsShareFunc short dbChannelSpecial(dbChannel *chan); -epicsShareFunc void * dbChannelData(dbChannel *chan); +epicsShareFunc void * dbChannelField(dbChannel *chan); +epicsShareFunc long dbChannelGetField(dbChannel *chan, short type, + void *pbuffer, long *options, long *nRequest, void *pfl); epicsShareFunc long dbChannelPut(dbChannel *chan, short type, const void *pbuffer, long nRequest); epicsShareFunc long dbChannelPutField(dbChannel *chan, short type, diff --git a/src/ioc/db/dbChannelIO.cpp b/src/ioc/db/dbChannelIO.cpp index 9bf9b0d52..e9afafb83 100644 --- a/src/ioc/db/dbChannelIO.cpp +++ b/src/ioc/db/dbChannelIO.cpp @@ -158,7 +158,7 @@ void dbChannelIO::show ( printf ( "\ttype %s, element count %li, field at %p\n", dbf_type_to_text ( dbChannelExportType ( this->dbch ) ), dbChannelElements ( this->dbch ), - dbChannelData ( this->dbch ) ); + dbChannelField ( this->dbch ) ); if ( level > 1u ) { dbChannelFilterShow ( this->dbch, level - 2u ); this->serviceIO.show ( level - 2u ); diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index d53a2e4f2..2280309a8 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -730,8 +730,8 @@ static void db_post_single_event_private (struct evSubscrip *event) * address */ memcpy(& pLog->field, - dbChannelData(chan), - dbChannelElementSize(chan)); + dbChannelField(chan), + dbChannelElementSize(chan)); event->pLastLog = pLog; } @@ -778,7 +778,7 @@ unsigned int caEventMask * Only send event msg if they are waiting on the field which * changed or pval==NULL and waiting on alarms and alarms changed */ - if ( (dbChannelData(event->chan) == (void *)pField || pField==NULL) && + if ( (dbChannelField(event->chan) == (void *)pField || pField==NULL) && (caEventMask & event->select) ) { db_post_single_event_private (event); } diff --git a/src/ioc/db/db_access.c b/src/ioc/db/db_access.c index b705e0b35..a0e4fb7b7 100644 --- a/src/ioc/db/db_access.c +++ b/src/ioc/db/db_access.c @@ -168,7 +168,7 @@ int epicsShareAPI dbChannel_get_count( switch(buffer_type) { case(oldDBR_STRING): - status = dbGetField(paddr, DBR_STRING, pbuffer, &zero, nRequest, pfl); + status = dbChannelGetField(chan, DBR_STRING, pbuffer, &zero, nRequest, pfl); break; /* case(oldDBR_INT): */ case(oldDBR_SHORT): @@ -295,7 +295,7 @@ int epicsShareAPI dbChannel_get_count( pold->status = newSt.status; pold->severity = newSt.severity; options = 0; - status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options, + status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options, nRequest, pfl); } break; @@ -333,7 +333,7 @@ int epicsShareAPI dbChannel_get_count( pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; - status = dbGetField(paddr, DBR_SHORT, &pold->value, &options, + status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options, nRequest, pfl); } break; @@ -351,7 +351,7 @@ int epicsShareAPI dbChannel_get_count( pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; - status = dbGetField(paddr, DBR_FLOAT, &pold->value, &options, + status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options, nRequest, pfl); } break; @@ -369,7 +369,7 @@ int epicsShareAPI dbChannel_get_count( pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; - status = dbGetField(paddr, DBR_ENUM, &pold->value, &options, + status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &options, nRequest, pfl); } break; @@ -387,7 +387,7 @@ int epicsShareAPI dbChannel_get_count( pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; - status = dbGetField(paddr, DBR_CHAR, &pold->value, &options, + status = dbChannelGetField(chan, DBR_CHAR, &pold->value, &options, nRequest, pfl); } break; @@ -405,7 +405,7 @@ int epicsShareAPI dbChannel_get_count( pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; - status = dbGetField(paddr, DBR_LONG, &pold->value, &options, + status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options, nRequest, pfl); } break; @@ -423,7 +423,7 @@ int epicsShareAPI dbChannel_get_count( pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; - status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options, + status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options, nRequest, pfl); } break; @@ -453,7 +453,7 @@ int epicsShareAPI dbChannel_get_count( pold->lower_warning_limit = newSt.lower_warning_limit; pold->lower_alarm_limit = newSt.lower_alarm_limit; options = 0; - status = dbGetField(paddr, DBR_SHORT, &pold->value, &options, + status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options, nRequest, pfl); } break; @@ -483,7 +483,7 @@ int epicsShareAPI dbChannel_get_count( pold->upper_warning_limit = epicsConvertDoubleToFloat(newSt.upper_warning_limit); pold->lower_warning_limit = epicsConvertDoubleToFloat(newSt.lower_warning_limit); options = 0; - status = dbGetField(paddr, DBR_FLOAT, &pold->value, &options, + status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options, nRequest, pfl); } break; @@ -511,7 +511,7 @@ int epicsShareAPI dbChannel_get_count( pold->lower_warning_limit = newSt.lower_warning_limit; pold->lower_alarm_limit = newSt.lower_alarm_limit; options = 0; - status = dbGetField(paddr, DBR_UCHAR, &pold->value, &options, + status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &options, nRequest, pfl); } break; @@ -538,7 +538,7 @@ int epicsShareAPI dbChannel_get_count( pold->lower_warning_limit = newSt.lower_warning_limit; pold->lower_alarm_limit = newSt.lower_alarm_limit; options = 0; - status = dbGetField(paddr, DBR_LONG, &pold->value, &options, + status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options, nRequest, pfl); } break; @@ -568,7 +568,7 @@ int epicsShareAPI dbChannel_get_count( pold->lower_warning_limit = newSt.lower_warning_limit; pold->lower_alarm_limit = newSt.lower_alarm_limit; options = 0; - status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options, + status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options, nRequest, pfl); } break; @@ -601,7 +601,7 @@ int epicsShareAPI dbChannel_get_count( pold->upper_ctrl_limit = newSt.upper_ctrl_limit; pold->lower_ctrl_limit = newSt.lower_ctrl_limit; options = 0; - status = dbGetField(paddr, DBR_SHORT, &pold->value, &options, + status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &options, nRequest, pfl); } break; @@ -634,7 +634,7 @@ int epicsShareAPI dbChannel_get_count( pold->upper_ctrl_limit = epicsConvertDoubleToFloat(newSt.upper_ctrl_limit); pold->lower_ctrl_limit = epicsConvertDoubleToFloat(newSt.lower_ctrl_limit); options = 0; - status = dbGetField(paddr, DBR_FLOAT, &pold->value, &options, + status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &options, nRequest, pfl); } break; @@ -661,7 +661,7 @@ int epicsShareAPI dbChannel_get_count( strncpy(pold->strs[i], newSt.strs[i], sizeof(pold->strs[i])); /*now get values*/ options = 0; - status = dbGetField(paddr, DBR_ENUM, &pold->value, &options, + status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &options, nRequest, pfl); } break; @@ -692,7 +692,7 @@ int epicsShareAPI dbChannel_get_count( pold->upper_ctrl_limit = newSt.upper_ctrl_limit; pold->lower_ctrl_limit = newSt.lower_ctrl_limit; options = 0; - status = dbGetField(paddr, DBR_UCHAR, &pold->value, &options, + status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &options, nRequest, pfl); } break; @@ -723,7 +723,7 @@ int epicsShareAPI dbChannel_get_count( pold->upper_ctrl_limit = newSt.upper_ctrl_limit; pold->lower_ctrl_limit = newSt.lower_ctrl_limit; options = 0; - status = dbGetField(paddr, DBR_LONG, &pold->value, &options, + status = dbChannelGetField(chan, DBR_LONG, &pold->value, &options, nRequest, pfl); } break; @@ -756,7 +756,7 @@ int epicsShareAPI dbChannel_get_count( pold->upper_ctrl_limit = newSt.upper_ctrl_limit; pold->lower_ctrl_limit = newSt.lower_ctrl_limit; options = 0; - status = dbGetField(paddr, DBR_DOUBLE, &pold->value, &options, + status = dbChannelGetField(chan, DBR_DOUBLE, &pold->value, &options, nRequest, pfl); } break; @@ -775,7 +775,7 @@ int epicsShareAPI dbChannel_get_count( pold->ackt = newSt.ackt; pold->acks = newSt.acks; options = 0; - status = dbGetField(paddr, DBR_STRING, pold->value, + status = dbChannelGetField(chan, DBR_STRING, pold->value, &options, nRequest, pfl); } break; @@ -791,7 +791,7 @@ int epicsShareAPI dbChannel_get_count( break; } dbInitEntry(pdbbase, &dbEntry); - status = dbFindRecord(&dbEntry, paddr->precord->name); + status = dbFindRecord(&dbEntry, dbChannelRecord(chan)->name); if (!status) name = dbGetRecordTypeName(&dbEntry); dbFinishEntry(&dbEntry); if (status) break; @@ -813,160 +813,159 @@ int epicsShareAPI dbChannel_get_count( int dbChannel_put(struct dbChannel *chan, int src_type, const void *psrc, long no_elements) { - dbAddr *paddr = & chan->addr; long status; switch (src_type) { case(oldDBR_STRING): - status = dbPutField(paddr, DBR_STRING, psrc, no_elements); + status = dbChannelPutField(chan, DBR_STRING, psrc, no_elements); break; /* case(oldDBR_INT): */ case(oldDBR_SHORT): - status = dbPutField(paddr, DBR_SHORT, psrc, no_elements); + status = dbChannelPutField(chan, DBR_SHORT, psrc, no_elements); break; case(oldDBR_FLOAT): - status = dbPutField(paddr, DBR_FLOAT, psrc, no_elements); + status = dbChannelPutField(chan, DBR_FLOAT, psrc, no_elements); break; case(oldDBR_ENUM): - status = dbPutField(paddr, DBR_ENUM, psrc, no_elements); + status = dbChannelPutField(chan, DBR_ENUM, psrc, no_elements); break; case(oldDBR_CHAR): - status = dbPutField(paddr, DBR_UCHAR, psrc, no_elements); + status = dbChannelPutField(chan, DBR_UCHAR, psrc, no_elements); break; case(oldDBR_LONG): - status = dbPutField(paddr, DBR_LONG, psrc, no_elements); + status = dbChannelPutField(chan, DBR_LONG, psrc, no_elements); break; case(oldDBR_DOUBLE): - status = dbPutField(paddr, DBR_DOUBLE, psrc, no_elements); + status = dbChannelPutField(chan, DBR_DOUBLE, psrc, no_elements); break; case(oldDBR_STS_STRING): - status = dbPutField(paddr, DBR_STRING, + status = dbChannelPutField(chan, DBR_STRING, ((const struct dbr_sts_string *)psrc)->value, no_elements); break; /* case(oldDBR_STS_INT): */ case(oldDBR_STS_SHORT): - status = dbPutField(paddr, DBR_SHORT, + status = dbChannelPutField(chan, DBR_SHORT, &((const struct dbr_sts_short *)psrc)->value, no_elements); break; case(oldDBR_STS_FLOAT): - status = dbPutField(paddr, DBR_FLOAT, + status = dbChannelPutField(chan, DBR_FLOAT, &((const struct dbr_sts_float *)psrc)->value, no_elements); break; case(oldDBR_STS_ENUM): - status = dbPutField(paddr, DBR_ENUM, + status = dbChannelPutField(chan, DBR_ENUM, &((const struct dbr_sts_enum *)psrc)->value, no_elements); break; case(oldDBR_STS_CHAR): - status = dbPutField(paddr, DBR_UCHAR, + status = dbChannelPutField(chan, DBR_UCHAR, &((const struct dbr_sts_char *)psrc)->value, no_elements); break; case(oldDBR_STS_LONG): - status = dbPutField(paddr, DBR_LONG, + status = dbChannelPutField(chan, DBR_LONG, &((const struct dbr_sts_long *)psrc)->value, no_elements); break; case(oldDBR_STS_DOUBLE): - status = dbPutField(paddr, DBR_DOUBLE, + status = dbChannelPutField(chan, DBR_DOUBLE, &((const struct dbr_sts_double *)psrc)->value, no_elements); break; case(oldDBR_TIME_STRING): - status = dbPutField(paddr, DBR_TIME, + status = dbChannelPutField(chan, DBR_TIME, ((const struct dbr_time_string *)psrc)->value, no_elements); break; /* case(oldDBR_TIME_INT): */ case(oldDBR_TIME_SHORT): - status = dbPutField(paddr, DBR_SHORT, + status = dbChannelPutField(chan, DBR_SHORT, &((const struct dbr_time_short *)psrc)->value, no_elements); break; case(oldDBR_TIME_FLOAT): - status = dbPutField(paddr, DBR_FLOAT, + status = dbChannelPutField(chan, DBR_FLOAT, &((const struct dbr_time_float *)psrc)->value, no_elements); break; case(oldDBR_TIME_ENUM): - status = dbPutField(paddr, DBR_ENUM, + status = dbChannelPutField(chan, DBR_ENUM, &((const struct dbr_time_enum *)psrc)->value, no_elements); break; case(oldDBR_TIME_CHAR): - status = dbPutField(paddr, DBR_UCHAR, + status = dbChannelPutField(chan, DBR_UCHAR, &((const struct dbr_time_char *)psrc)->value, no_elements); break; case(oldDBR_TIME_LONG): - status = dbPutField(paddr, DBR_LONG, + status = dbChannelPutField(chan, DBR_LONG, &((const struct dbr_time_long *)psrc)->value, no_elements); break; case(oldDBR_TIME_DOUBLE): - status = dbPutField(paddr, DBR_DOUBLE, + status = dbChannelPutField(chan, DBR_DOUBLE, &((const struct dbr_time_double *)psrc)->value, no_elements); break; case(oldDBR_GR_STRING): /* no struct dbr_gr_string - use dbr_sts_string instead */ - status = dbPutField(paddr, DBR_STRING, + status = dbChannelPutField(chan, DBR_STRING, ((const struct dbr_sts_string *)psrc)->value, no_elements); break; /* case(oldDBR_GR_INT): */ case(oldDBR_GR_SHORT): - status = dbPutField(paddr, DBR_SHORT, + status = dbChannelPutField(chan, DBR_SHORT, &((const struct dbr_gr_short *)psrc)->value, no_elements); break; case(oldDBR_GR_FLOAT): - status = dbPutField(paddr, DBR_FLOAT, + status = dbChannelPutField(chan, DBR_FLOAT, &((const struct dbr_gr_float *)psrc)->value, no_elements); break; case(oldDBR_GR_ENUM): - status = dbPutField(paddr, DBR_ENUM, + status = dbChannelPutField(chan, DBR_ENUM, &((const struct dbr_gr_enum *)psrc)->value, no_elements); break; case(oldDBR_GR_CHAR): - status = dbPutField(paddr, DBR_UCHAR, + status = dbChannelPutField(chan, DBR_UCHAR, &((const struct dbr_gr_char *)psrc)->value, no_elements); break; case(oldDBR_GR_LONG): - status = dbPutField(paddr, DBR_LONG, + status = dbChannelPutField(chan, DBR_LONG, &((const struct dbr_gr_long *)psrc)->value, no_elements); break; case(oldDBR_GR_DOUBLE): - status = dbPutField(paddr, DBR_DOUBLE, + status = dbChannelPutField(chan, DBR_DOUBLE, &((const struct dbr_gr_double *)psrc)->value, no_elements); break; case(oldDBR_CTRL_STRING): /* no struct dbr_ctrl_string - use dbr_sts_string instead */ - status = dbPutField(paddr, DBR_STRING, + status = dbChannelPutField(chan, DBR_STRING, ((const struct dbr_sts_string *)psrc)->value, no_elements); break; /* case(oldDBR_CTRL_INT): */ case(oldDBR_CTRL_SHORT): - status = dbPutField(paddr, DBR_SHORT, + status = dbChannelPutField(chan, DBR_SHORT, &((const struct dbr_ctrl_short *)psrc)->value, no_elements); break; case(oldDBR_CTRL_FLOAT): - status = dbPutField(paddr, DBR_FLOAT, + status = dbChannelPutField(chan, DBR_FLOAT, &((const struct dbr_ctrl_float *)psrc)->value, no_elements); break; case(oldDBR_CTRL_ENUM): - status = dbPutField(paddr, DBR_ENUM, + status = dbChannelPutField(chan, DBR_ENUM, &((const struct dbr_ctrl_enum *)psrc)->value, no_elements); break; case(oldDBR_CTRL_CHAR): - status = dbPutField(paddr, DBR_UCHAR, + status = dbChannelPutField(chan, DBR_UCHAR, &((const struct dbr_ctrl_char *)psrc)->value, no_elements); break; case(oldDBR_CTRL_LONG): - status = dbPutField(paddr, DBR_LONG, + status = dbChannelPutField(chan, DBR_LONG, &((const struct dbr_ctrl_long *)psrc)->value, no_elements); break; case(oldDBR_CTRL_DOUBLE): - status = dbPutField(paddr, DBR_DOUBLE, + status = dbChannelPutField(chan, DBR_DOUBLE, &((const struct dbr_ctrl_double *)psrc)->value, no_elements); break; case(oldDBR_PUT_ACKT): - status = dbPutField(paddr, DBR_PUT_ACKT, psrc, no_elements); + status = dbChannelPutField(chan, DBR_PUT_ACKT, psrc, no_elements); break; case(oldDBR_PUT_ACKS): - status = dbPutField(paddr, DBR_PUT_ACKS, psrc, no_elements); + status = dbChannelPutField(chan, DBR_PUT_ACKS, psrc, no_elements); break; default: diff --git a/src/ioc/db/db_test.c b/src/ioc/db/db_test.c index 57c325907..352d0b285 100644 --- a/src/ioc/db/db_test.c +++ b/src/ioc/db/db_test.c @@ -53,7 +53,7 @@ int gft(char *pname) printf(" Record Name: %s\n", precord->name); printf("Record Address: 0x%p\n", precord); printf(" Export Type: %d\n", type); - printf(" Field Address: 0x%p\n", dbChannelData(chan)); + printf(" Field Address: 0x%p\n", dbChannelField(chan)); printf(" Field Size: %d\n", dbChannelElementSize(chan)); printf(" No Elements: %ld\n", elements); @@ -107,7 +107,7 @@ int epicsShareAPI pft(char *pname, char *pvalue) printf(" Record Name: %s\n", precord->name); printf("Record Address: 0x%p\n", precord); printf(" Export Type: %d\n", type); - printf(" Field Address: 0x%p\n", dbChannelData(chan)); + printf(" Field Address: 0x%p\n", dbChannelField(chan)); printf(" Field Size: %d\n", dbChannelElementSize(chan)); printf(" No Elements: %ld\n", elements); From bcfaa63d8cf5e994d2c3855cf815dc0189625a64 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:21:44 -0400 Subject: [PATCH 17/81] complete rename --- src/ioc/db/db_access.c | 78 +++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/ioc/db/db_access.c b/src/ioc/db/db_access.c index a0e4fb7b7..f1ea31a01 100644 --- a/src/ioc/db/db_access.c +++ b/src/ioc/db/db_access.c @@ -172,22 +172,22 @@ int epicsShareAPI dbChannel_get_count( break; /* case(oldDBR_INT): */ case(oldDBR_SHORT): - status = dbGetField(paddr, DBR_SHORT, pbuffer, &zero, nRequest, pfl); + status = dbChannelGetField(chan, DBR_SHORT, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_FLOAT): - status = dbGetField(paddr, DBR_FLOAT, pbuffer, &zero, nRequest, pfl); + status = dbChannelGetField(chan, DBR_FLOAT, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_ENUM): - status = dbGetField(paddr, DBR_ENUM, pbuffer, &zero, nRequest, pfl); + status = dbChannelGetField(chan, DBR_ENUM, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_CHAR): - status = dbGetField(paddr, DBR_CHAR, pbuffer, &zero, nRequest, pfl); + status = dbChannelGetField(chan, DBR_CHAR, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_LONG): - status = dbGetField(paddr, DBR_LONG, pbuffer, &zero, nRequest, pfl); + status = dbChannelGetField(chan, DBR_LONG, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_DOUBLE): - status = dbGetField(paddr, DBR_DOUBLE, pbuffer, &zero, nRequest, pfl); + status = dbChannelGetField(chan, DBR_DOUBLE, pbuffer, &zero, nRequest, pfl); break; case(oldDBR_STS_STRING): @@ -200,10 +200,10 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS; - status = dbGetField(paddr, DBR_STRING, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; - status = dbGetField(paddr, DBR_STRING, pold->value, &zero, + status = dbChannelGetField(chan, DBR_STRING, pold->value, &zero, nRequest, pfl); } break; @@ -216,10 +216,10 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS; - status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; - status = dbGetField(paddr, DBR_SHORT, &pold->value, &zero, + status = dbChannelGetField(chan, DBR_SHORT, &pold->value, &zero, nRequest, pfl); } break; @@ -231,10 +231,10 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS; - status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; - status = dbGetField(paddr, DBR_FLOAT, &pold->value, &zero, + status = dbChannelGetField(chan, DBR_FLOAT, &pold->value, &zero, nRequest, pfl); } break; @@ -246,10 +246,10 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS; - status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; - status = dbGetField(paddr, DBR_ENUM, &pold->value, &zero, + status = dbChannelGetField(chan, DBR_ENUM, &pold->value, &zero, nRequest, pfl); } break; @@ -261,10 +261,10 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS; - status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; - status = dbGetField(paddr, DBR_UCHAR, &pold->value, &zero, + status = dbChannelGetField(chan, DBR_UCHAR, &pold->value, &zero, nRequest, pfl); } break; @@ -276,10 +276,10 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS; - status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; - status = dbGetField(paddr, DBR_LONG, &pold->value, &zero, + status = dbChannelGetField(chan, DBR_LONG, &pold->value, &zero, nRequest, pfl); } break; @@ -291,7 +291,7 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS; - status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; options = 0; @@ -309,12 +309,12 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS | DBR_TIME; - status = dbGetField(paddr, DBR_STRING, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ options = 0; - status = dbGetField(paddr, DBR_STRING, pold->value, &options, + status = dbChannelGetField(chan, DBR_STRING, pold->value, &options, nRequest, pfl); } break; @@ -328,7 +328,7 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS | DBR_TIME; - status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ @@ -346,7 +346,7 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS | DBR_TIME; - status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ @@ -364,7 +364,7 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS | DBR_TIME; - status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ @@ -382,7 +382,7 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS | DBR_TIME; - status = dbGetField(paddr, DBR_CHAR, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_CHAR, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ @@ -400,7 +400,7 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS | DBR_TIME; - status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ @@ -418,7 +418,7 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS | DBR_TIME; - status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->stamp = newSt.time; /* structure copy */ @@ -441,7 +441,7 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG; - status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); @@ -470,7 +470,7 @@ int epicsShareAPI dbChannel_get_count( options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE | DBR_AL_DOUBLE; - status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->precision = newSt.precision.dp; @@ -499,7 +499,7 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG; - status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); @@ -526,7 +526,7 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_AL_LONG; - status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); @@ -555,7 +555,7 @@ int epicsShareAPI dbChannel_get_count( options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE | DBR_AL_DOUBLE; - status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->precision = newSt.precision.dp; @@ -587,7 +587,7 @@ int epicsShareAPI dbChannel_get_count( options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG | DBR_AL_LONG; - status = dbGetField(paddr, DBR_SHORT, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_SHORT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); @@ -619,7 +619,7 @@ int epicsShareAPI dbChannel_get_count( options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE | DBR_CTRL_DOUBLE | DBR_AL_DOUBLE; - status = dbGetField(paddr, DBR_FLOAT, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_FLOAT, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->precision = newSt.precision.dp; @@ -651,7 +651,7 @@ int epicsShareAPI dbChannel_get_count( memset(pold, '\0', sizeof(struct dbr_ctrl_enum)); /* first get status and severity */ options = DBR_STATUS | DBR_ENUM_STRS; - status = dbGetField(paddr, DBR_ENUM, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_ENUM, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; no_str = newSt.no_str; @@ -678,7 +678,7 @@ int epicsShareAPI dbChannel_get_count( options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG | DBR_AL_LONG; - status = dbGetField(paddr, DBR_UCHAR, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_UCHAR, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); @@ -709,7 +709,7 @@ int epicsShareAPI dbChannel_get_count( options = DBR_STATUS | DBR_UNITS | DBR_GR_LONG | DBR_CTRL_LONG | DBR_AL_LONG; - status = dbGetField(paddr, DBR_LONG, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_LONG, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; strncpy(pold->units, newSt.units, MAX_UNITS_SIZE); @@ -741,7 +741,7 @@ int epicsShareAPI dbChannel_get_count( options = DBR_STATUS | DBR_UNITS | DBR_PRECISION | DBR_GR_DOUBLE | DBR_CTRL_DOUBLE | DBR_AL_DOUBLE; - status = dbGetField(paddr, DBR_DOUBLE, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_DOUBLE, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->precision = newSt.precision.dp; @@ -769,7 +769,7 @@ int epicsShareAPI dbChannel_get_count( } newSt; options = DBR_STATUS; - status = dbGetField(paddr, DBR_STRING, &newSt, &options, &zero, pfl); + status = dbChannelGetField(chan, DBR_STRING, &newSt, &options, &zero, pfl); pold->status = newSt.status; pold->severity = newSt.severity; pold->ackt = newSt.ackt; From e94fd29b9bfc3dae1d705708174b69c1a1ba4440 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:21:44 -0400 Subject: [PATCH 18/81] fix camessage --- src/ioc/rsrv/camessage.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ioc/rsrv/camessage.c b/src/ioc/rsrv/camessage.c index f51b81c5e..b3511ed2b 100644 --- a/src/ioc/rsrv/camessage.c +++ b/src/ioc/rsrv/camessage.c @@ -510,6 +510,7 @@ static void read_reply ( void *pArg, struct dbChannel *dbch, int autosize; long item_count; ca_uint32_t payload_size; + dbAddr *paddr=&dbch->addr; SEND_LOCK ( pClient ); @@ -546,7 +547,7 @@ static void read_reply ( void *pArg, struct dbChannel *dbch, send_err ( &pevext->msg, status, pClient, "server unable to load read (or subscription update) response " "into protocol buffer PV=\"%s\" max bytes=%u", - RECORD_NAME ( paddr ), rsrvSizeofLargeBufTCP ); + RECORD_NAME ( dbch ), rsrvSizeofLargeBufTCP ); if ( ! eventsRemaining ) cas_send_bs_msg ( pClient, FALSE ); SEND_UNLOCK ( pClient ); @@ -2322,7 +2323,6 @@ static int search_reply_udp ( caHdrLargeArray *mp, void *pPayload, struct client static int search_reply_tcp ( caHdrLargeArray *mp, void *pPayload, struct client *client ) { - struct dbAddr tmp_addr; char *pName = (char *) pPayload; int status; int spaceAvailOnFreeList; @@ -2340,8 +2340,7 @@ static int search_reply_tcp ( pName[mp->m_postsize-1] = '\0'; /* Exit quickly if channel not on this node */ - status = db_name_to_addr (pName, &tmp_addr); - if (status) { + if (dbChannelTest(pName)) { DLOG ( 2, ( "CAS: Lookup for channel \"%s\" failed\n", pPayLoad ) ); if (mp->m_dataType == DOREPLY) search_fail_reply ( mp, pPayload, client ); From 06bcdb0adea92bfde9f6302115524e052364fdd1 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:45 -0400 Subject: [PATCH 19/81] Tindying up. Fixed issues with dbChannelDelete() and dbChannelShow(). --- src/ioc/db/dbAccess.c | 94 ++++++++++++++++++--------------- src/ioc/db/dbChannel.c | 18 ++++--- src/ioc/db/test/dbChannelTest.c | 4 +- 3 files changed, 65 insertions(+), 51 deletions(-) diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index 280077149..da52ccb57 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -325,7 +325,8 @@ static void get_alarm(DBADDR *paddr, char **ppbuffer, return; } -static void getOptions(DBADDR *paddr,char **poriginal,long *options,void *pflin) +static void getOptions(DBADDR *paddr, char **poriginal, long *options, + void *pflin) { db_field_log *pfl= (db_field_log *)pflin; struct rset *prset; @@ -890,6 +891,51 @@ long epicsShareAPI dbPutLinkValue(struct link *plink, short dbrType, return status; } +static long dbGetFieldLink(DBADDR *paddr,short dbrType, + void *pbuffer, long *options, long *nRequest, void *pflin) +{ + dbCommon *precord = paddr->precord; + dbFldDes *pfldDes = paddr->pfldDes; + char *pbuf = (char *)pbuffer; + int maxlen; + DBENTRY dbEntry; + long status; + + if (options && (*options)) + getOptions(paddr, &pbuf, options, pflin); + if (nRequest && *nRequest == 0) + return 0; + + switch (dbrType) { + case DBR_STRING: + maxlen = MAX_STRING_SIZE - 1; + if (nRequest && *nRequest > 1) *nRequest = 1; + break; + + case DBR_CHAR: + case DBR_UCHAR: + if (nRequest && *nRequest > 0) { + maxlen = *nRequest - 1; + break; + } + /* else fall through ... */ + default: + return S_db_badDbrtype; + } + + dbInitEntry(pdbbase, &dbEntry); + status = dbFindRecord(&dbEntry, precord->name); + if (!status) status = dbFindField(&dbEntry, pfldDes->name); + if (!status) { + char *rtnString = dbGetString(&dbEntry); + + strncpy(pbuf, rtnString, --maxlen); + pbuf[maxlen] = 0; + } + dbFinishEntry(&dbEntry); + return status; +} + long epicsShareAPI dbGetField(DBADDR *paddr,short dbrType, void *pbuffer, long *options, long *nRequest, void *pflin) { @@ -899,47 +945,11 @@ long epicsShareAPI dbGetField(DBADDR *paddr,short dbrType, dbScanLock(precord); if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) { - DBENTRY dbEntry; - dbFldDes *pfldDes = paddr->pfldDes; - char *rtnString; - char *pbuf = (char *)pbuffer; - int maxlen; - - if (options && (*options)) - getOptions(paddr, &pbuf, options, pflin); - if (nRequest && *nRequest == 0) goto done; - - switch (dbrType) { - case DBR_STRING: - maxlen = MAX_STRING_SIZE - 1; - if (nRequest && *nRequest > 1) *nRequest = 1; - break; - - case DBR_CHAR: - case DBR_UCHAR: - if (nRequest && *nRequest > 0) { - maxlen = *nRequest - 1; - break; - } - /* else fall through ... */ - default: - status = S_db_badDbrtype; - goto done; - } - - dbInitEntry(pdbbase, &dbEntry); - status = dbFindRecord(&dbEntry, precord->name); - if (!status) status = dbFindField(&dbEntry, pfldDes->name); - if (!status) { - rtnString = dbGetString(&dbEntry); - strncpy(pbuf, rtnString, maxlen); - pbuf[maxlen] = 0; - } - dbFinishEntry(&dbEntry); + status = dbGetFieldLink(paddr, dbrType, pbuffer, options, nRequest, + pflin); } else { status = dbGet(paddr, dbrType, pbuffer, options, nRequest, pflin); } -done: dbScanUnlock(precord); return status; } @@ -985,7 +995,7 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, return S_db_badDbrtype; } - strncpy(pbuf, (char *)paddr->pfield, maxlen); + strncpy(pbuf, paddr->pfield, --maxlen); pbuf[maxlen] = 0; return 0; } @@ -1351,7 +1361,6 @@ long epicsShareAPI dbPut(DBADDR *paddr, short dbrType, short field_type = paddr->field_type; long no_elements = paddr->no_elements; long special = paddr->special; - long offset; long status = 0; dbFldDes *pfldDes; int isValueField; @@ -1380,6 +1389,7 @@ long epicsShareAPI dbPut(DBADDR *paddr, short dbrType, paddr->pfield, paddr); } else { struct rset *prset = dbGetRset(paddr); + long offset = 0; if (paddr->special == SPC_DBADDR && prset && prset->get_array_info) { @@ -1387,8 +1397,6 @@ long epicsShareAPI dbPut(DBADDR *paddr, short dbrType, status = prset->get_array_info(paddr, &dummy, &offset); } - else - offset = 0; if (no_elements < nRequest) nRequest = no_elements; status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer, nRequest, no_elements, offset); diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index 4010b7a9d..6a0b0468c 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -331,7 +331,7 @@ dbChannel * dbChannelCreate(const char *name) const char *pname = name; DBENTRY dbEntry; dbChannel *chan = NULL; - DBADDR *paddr; + dbAddr *paddr; dbFldDes *pflddes; long status; short dbfType; @@ -429,6 +429,9 @@ long dbChannelOpen(dbChannel *chan) return status; } +/* FIXME: For performance we should make these one-liners into macros, + * or try to make them inline if all our compilers can do that. + */ const char * dbChannelName(dbChannel *chan) { return chan->name; @@ -472,7 +475,11 @@ short dbChannelSpecial(dbChannel *chan) void * dbChannelField(dbChannel *chan) { void * pdata = chan->addr.pfield; - /* FIXME: offer to filters? */ + /* FIXME: offer to filters? + * This is probably *not* a good idea, since there are several places + * (e.g. db_post_events()) where this pointer is compared with the + * address of a specific record field. + */ return pdata; } @@ -483,6 +490,7 @@ long dbChannelGetField(dbChannel *chan, short type, void *pbuffer, return dbGetField(&chan->addr, type, pbuffer, options, nRequest, pfl); } +/* Only use this when the record is already scan-locked */ long dbChannelPut(dbChannel *chan, short type, const void *pbuffer, long nRequest) { @@ -501,7 +509,7 @@ void dbChannelShow(dbChannel *chan, int level) { printf(" dbChannel name: %s\n", chan->name); /* FIXME: show field_type name */ - printf(" field_type %d, %ld element(s), %d filter(s)", + printf(" field_type %d, %ld element(s), %d filter(s)\n", chan->addr.field_type, chan->addr.no_elements, ellCount(&chan->filters)); if (level > 0) @@ -512,7 +520,7 @@ void dbChannelFilterShow(dbChannel *chan, int level) { chFilter *filter = (chFilter *) ellFirst(&chan->filters); while (filter) { - filter->fif->channel_report(filter, level - 1); + filter->fif->channel_report(filter, level); filter = (chFilter *) ellNext(&filter->node); } } @@ -527,8 +535,6 @@ void dbChannelDelete(dbChannel *chan) } free((char *) chan->name); // FIXME: Use free-list free(chan); // FIXME: Use free-list - - return 0; } diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index 50543c038..1f45b5253 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -132,7 +132,7 @@ MAIN(dbChannelTest) { dbChannel *pch; - testPlan(67); + testPlan(68); testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.dbx", ".:..", NULL)); testOk(!!pdbbase, "pdbbase was set"); @@ -196,7 +196,7 @@ MAIN(dbChannelTest) testOk1(!!(pch = dbChannelCreate("x.{\"scalar\":null}"))); e = e_report; - dbChannelShow(pch, 0); + dbChannelShow(pch, 1); e = e_close; if (pch) dbChannelDelete(pch); From 9a02181084fecef20068283c5f2df48c837bf32f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:45 -0400 Subject: [PATCH 20/81] Add ellPop() function, removes and returns the last node on a list. --- src/libCom/ellLib/ellLib.c | 19 +++++++++++++++++-- src/libCom/ellLib/ellLib.h | 3 ++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/libCom/ellLib/ellLib.c b/src/libCom/ellLib/ellLib.c index 12a12ed4c..be1632079 100644 --- a/src/libCom/ellLib/ellLib.c +++ b/src/libCom/ellLib/ellLib.c @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* $Revision-Id$ * @@ -155,6 +155,21 @@ ELLNODE * ellGet (ELLLIST *pList) return pnode; } +/**************************************************************************** +* +* This function returns the last node in the specified list. The node is +* removed from the list. If the list is empty, NULL will be returned. +* +*****************************************************************************/ +ELLNODE * ellPop (ELLLIST *pList) +{ + ELLNODE *pnode = pList->node.previous; + + if (pnode != NULL) + ellDelete(pList, pnode); + + return pnode; +} /**************************************************************************** * * This function inserts the specified node pNode after pPrev in the list @@ -185,7 +200,7 @@ void ellInsert (ELLLIST *plist, ELLNODE *pPrev, ELLNODE *pNode) } /**************************************************************************** * - * This function returns the nodeNum'th element in pList. If there is no + * This function returns the nodeNum'th element in pList. If there is no * nodeNum'th node in the list, NULL will be returned. * *****************************************************************************/ diff --git a/src/libCom/ellLib/ellLib.h b/src/libCom/ellLib/ellLib.h index d855a552f..3bc167a41 100644 --- a/src/libCom/ellLib/ellLib.h +++ b/src/libCom/ellLib/ellLib.h @@ -4,7 +4,7 @@ * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* in file LICENSE that is included with this distribution. \*************************************************************************/ /* $Revision-Id$ * @@ -52,6 +52,7 @@ epicsShareFunc void ellConcat (ELLLIST *pDstList, ELLLIST *pAddList); epicsShareFunc void ellDelete (ELLLIST *pList, ELLNODE *pNode); epicsShareFunc void ellExtract (ELLLIST *pSrcList, ELLNODE *pStartNode, ELLNODE *pEndNode, ELLLIST *pDstList); epicsShareFunc ELLNODE * ellGet (ELLLIST *pList); +epicsShareFunc ELLNODE * ellPop (ELLLIST *pList); epicsShareFunc void ellInsert (ELLLIST *plist, ELLNODE *pPrev, ELLNODE *pNode); epicsShareFunc ELLNODE * ellNth (ELLLIST *pList, int nodeNum); epicsShareFunc ELLNODE * ellNStep (ELLNODE *pNode, int nStep); From 1047f42b15673932d60e36f7c5e1ba701527653c Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:45 -0400 Subject: [PATCH 21/81] Simplify implementation. * dbGet() now handles DBF_*LINK fields, * Simplify dbGetField(), * Split out getAttrValue(), * Rename dbGetFieldLink() to getLinkValue(), * Reduce number of casts. --- src/ioc/db/dbAccess.c | 120 ++++++++++++++++++++---------------------- 1 file changed, 58 insertions(+), 62 deletions(-) diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index da52ccb57..bca64397a 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -867,7 +867,7 @@ long epicsShareAPI dbPutLinkValue(struct link *plink, short dbrType, inherit_severity(ppv_link,pdest,psource->nsta,psource->nsev); if (status) return status; - if (paddr->pfield == (void *)&pdest->proc || + if (paddr->pfield == &pdest->proc || (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) { /*if dbPutField caused asyn record to process */ /* ask for reprocessing*/ @@ -891,21 +891,15 @@ long epicsShareAPI dbPutLinkValue(struct link *plink, short dbrType, return status; } -static long dbGetFieldLink(DBADDR *paddr,short dbrType, - void *pbuffer, long *options, long *nRequest, void *pflin) +static long getLinkValue(DBADDR *paddr, short dbrType, + char *pbuf, long *nRequest) { dbCommon *precord = paddr->precord; dbFldDes *pfldDes = paddr->pfldDes; - char *pbuf = (char *)pbuffer; int maxlen; DBENTRY dbEntry; long status; - if (options && (*options)) - getOptions(paddr, &pbuf, options, pflin); - if (nRequest && *nRequest == 0) - return 0; - switch (dbrType) { case DBR_STRING: maxlen = MAX_STRING_SIZE - 1; @@ -936,20 +930,43 @@ static long dbGetFieldLink(DBADDR *paddr,short dbrType, return status; } +static long getAttrValue(DBADDR *paddr, short dbrType, + char *pbuf, long *nRequest) +{ + int maxlen; + + if (!paddr->pfield) return S_db_badField; + + switch (dbrType) { + case DBR_STRING: + maxlen = MAX_STRING_SIZE - 1; + if (nRequest && *nRequest > 1) *nRequest = 1; + break; + + case DBR_CHAR: + case DBR_UCHAR: + if (nRequest && *nRequest > 0) { + maxlen = *nRequest - 1; + break; + } + /* else fall through ... */ + default: + return S_db_badDbrtype; + } + + strncpy(pbuf, paddr->pfield, --maxlen); + pbuf[maxlen] = 0; + return 0; +} + long epicsShareAPI dbGetField(DBADDR *paddr,short dbrType, void *pbuffer, long *options, long *nRequest, void *pflin) { - short dbfType = paddr->field_type; dbCommon *precord = paddr->precord; long status = 0; dbScanLock(precord); - if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) { - status = dbGetFieldLink(paddr, dbrType, pbuffer, options, nRequest, - pflin); - } else { - status = dbGet(paddr, dbrType, pbuffer, options, nRequest, pflin); - } + status = dbGet(paddr, dbrType, pbuffer, options, nRequest, pflin); dbScanUnlock(precord); return status; } @@ -957,6 +974,7 @@ long epicsShareAPI dbGetField(DBADDR *paddr,short dbrType, long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, void *pbuffer, long *options, long *nRequest, void *pflin) { + char *pbuf = pbuffer; db_field_log *pfl = (db_field_log *)pflin; short field_type = paddr->field_type; long no_elements = paddr->no_elements; @@ -964,41 +982,16 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, struct rset *prset; long status = 0; - if (options && *options) { - char *pbuf = pbuffer; - + if (options && *options) getOptions(paddr, &pbuf, options, pflin); - pbuffer = pbuf; - } - if (nRequest && *nRequest == 0) return 0; - - if (paddr->special == SPC_ATTRIBUTE) { - char *pbuf = pbuffer; - int maxlen; - - if (!paddr->pfield) return S_db_badField; - - switch (dbrType) { - case DBR_STRING: - maxlen = MAX_STRING_SIZE - 1; - if (nRequest && *nRequest > 1) *nRequest = 1; - break; - - case DBR_CHAR: - case DBR_UCHAR: - if (nRequest && *nRequest > 0) { - maxlen = *nRequest - 1; - break; - } - /* else fall through ... */ - default: - return S_db_badDbrtype; - } - - strncpy(pbuf, paddr->pfield, --maxlen); - pbuf[maxlen] = 0; + if (nRequest && *nRequest == 0) return 0; - } + + if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK) + return getLinkValue(paddr, dbrType, pbuf, nRequest); + + if (paddr->special == SPC_ATTRIBUTE) + return getAttrValue(paddr, dbrType, pbuf, nRequest); /* Check for valid request */ if (INVALID_DB_REQ(dbrType) || field_type > DBF_DEVICE) { @@ -1295,8 +1288,7 @@ long epicsShareAPI dbPutField(DBADDR *paddr, short dbrType, return S_db_noMod; /*check for putField disabled*/ - if (precord->disp && - (void *)(&precord->disp) != paddr->pfield) + if (precord->disp && paddr->pfield != &precord->disp) return S_db_putDisabled; if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) @@ -1305,7 +1297,7 @@ long epicsShareAPI dbPutField(DBADDR *paddr, short dbrType, dbScanLock(precord); status = dbPut(paddr, dbrType, pbuffer, nRequest); if (status == 0) { - if (paddr->pfield == (void *)&precord->proc || + if (paddr->pfield == &precord->proc || (pfldDes->process_passive && precord->scan == 0 && dbrType < DBR_PUT_ACKT)) { @@ -1325,15 +1317,17 @@ long epicsShareAPI dbPutField(DBADDR *paddr, short dbrType, return status; } -static long putAckt(DBADDR *paddr, const unsigned short *pbuffer, long nRequest, +static long putAckt(DBADDR *paddr, const void *pbuffer, long nRequest, long no_elements, long offset) { dbCommon *precord = paddr->precord; + const unsigned short *ptrans = pbuffer; - if (*pbuffer == precord->ackt) return 0; - precord->ackt = *pbuffer; + if (*ptrans == precord->ackt) return 0; + precord->ackt = *ptrans; db_post_events(precord, &precord->ackt, DBE_VALUE | DBE_ALARM); - if (!precord->ackt && precord->acks > precord->sevr) { + if (!precord->ackt && + precord->acks > precord->sevr) { precord->acks = precord->sevr; db_post_events(precord, &precord->acks, DBE_VALUE | DBE_ALARM); } @@ -1341,15 +1335,16 @@ static long putAckt(DBADDR *paddr, const unsigned short *pbuffer, long nRequest, return 0; } -static long putAcks(DBADDR *paddr, const unsigned short *pbuffer, long nRequest, +static long putAcks(DBADDR *paddr, const void *pbuffer, long nRequest, long no_elements, long offset) { dbCommon *precord = paddr->precord; + const unsigned short *psev = pbuffer; - if (*pbuffer >= precord->acks) { + if (*psev >= precord->acks) { precord->acks = 0; - db_post_events(precord, NULL, DBE_ALARM); db_post_events(precord, &precord->acks, DBE_VALUE | DBE_ALARM); + db_post_events(precord, NULL, DBE_ALARM); } return 0; } @@ -1365,12 +1360,13 @@ long epicsShareAPI dbPut(DBADDR *paddr, short dbrType, dbFldDes *pfldDes; int isValueField; - if (special == SPC_ATTRIBUTE) return S_db_noMod; + if (special == SPC_ATTRIBUTE) + return S_db_noMod; if (dbrType == DBR_PUT_ACKT && field_type <= DBF_DEVICE) { - return putAckt(paddr, (unsigned short *)pbuffer, 1, 1, 0); + return putAckt(paddr, pbuffer, 1, 1, 0); } else if (dbrType == DBR_PUT_ACKS && field_type <= DBF_DEVICE) { - return putAcks(paddr, (unsigned short *)pbuffer, 1, 1, 0); + return putAcks(paddr, pbuffer, 1, 1, 0); } else if (INVALID_DB_REQ(dbrType) || field_type > DBF_DEVICE) { char message[80]; From 19a166a60d9ddca94bb052f8a1889ec3699a4a86 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:46 -0400 Subject: [PATCH 22/81] Various updates. * Added dbChannelGet() * Implement dbChannelGetField() using dbChannelGet() * Close filters in reverse order of opening * Messages and comments improved. --- src/ioc/db/dbChannel.c | 56 ++++++++++++++++++++++++++++-------------- src/ioc/db/dbChannel.h | 2 ++ 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index 6a0b0468c..f88f55a83 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -14,6 +14,7 @@ #include "dbBase.h" #include "link.h" #include "dbAccessDefs.h" +#include "dbLock.h" #include "dbStaticLib.h" #include "epicsAssert.h" #include "errlog.h" @@ -143,7 +144,7 @@ static int chf_map_key(void * ctx, const unsigned char * key, assert(parser->depth == 0); fif = dbFindFilter((const char *) key, stringLen); if (!fif) { - printf("Channel filter '%.*s' not found\n", stringLen, key); + printf("dbChannelCreate: Channel filter '%.*s' not found\n", stringLen, key); return parse_stop; } @@ -474,44 +475,60 @@ short dbChannelSpecial(dbChannel *chan) void * dbChannelField(dbChannel *chan) { - void * pdata = chan->addr.pfield; - /* FIXME: offer to filters? - * This is probably *not* a good idea, since there are several places - * (e.g. db_post_events()) where this pointer is compared with the - * address of a specific record field. + /* Channel filters do not get to interpose here since there are many + * places where the field pointer is compared with the address of a + * specific record field, so they can't modify the pointer value. */ - return pdata; + return chan->addr.pfield; } -long dbChannelGetField(dbChannel *chan, short type, void *pbuffer, +/* Only use dbChannelGet() if the record is already locked. */ +long dbChannelGet(dbChannel *chan, short type, void *pbuffer, long *options, long *nRequest, void *pfl) { - /* FIXME: offer to filters? */ - return dbGetField(&chan->addr, type, pbuffer, options, nRequest, pfl); + /* FIXME: Vector through chan->get() ? */ + return dbGet(&chan->addr, type, pbuffer, options, nRequest, pfl); } -/* Only use this when the record is already scan-locked */ +long dbChannelGetField(dbChannel *chan, short dbrType, void *pbuffer, + long *options, long *nRequest, void *pfl) +{ + dbCommon *precord = chan->addr.precord; + long status = 0; + + dbScanLock(precord); + status = dbChannelGet(chan, dbrType, pbuffer, options, nRequest, pfl); + dbScanUnlock(precord); + return status; +} + +/* Only use dbChannelPut() if the record is already locked. + * This routine doesn't work on link fields, ignores DISP, and + * doesn't trigger record processing on PROC or pp(TRUE). + */ long dbChannelPut(dbChannel *chan, short type, const void *pbuffer, long nRequest) { - /* FIXME: offer to filters? */ + /* FIXME: Vector through chan->put() ? */ return dbPut(&chan->addr, type, pbuffer, nRequest); } long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer, long nRequest) { - /* FIXME: offer to filters? */ + /* FIXME: Vector through chan->putField() ? */ return dbPutField(&chan->addr, type, pbuffer, nRequest); } void dbChannelShow(dbChannel *chan, int level) { - printf(" dbChannel name: %s\n", chan->name); - /* FIXME: show field_type name */ - printf(" field_type %d, %ld element(s), %d filter(s)\n", - chan->addr.field_type, - chan->addr.no_elements, ellCount(&chan->filters)); + long elems = chan->addr.no_elements; + int count = ellCount(&chan->filters); + printf("dbChannel name: %s\n", chan->name); + /* FIXME: show field_type as text */ + printf(" field_type = %d, %ld element%s, %d filter%s\n", + chan->addr.field_type, elems, elems == 1 ? "" : "s", + count, count == 1 ? "" : "s"); if (level > 0) dbChannelFilterShow(chan, level - 1); } @@ -529,7 +546,8 @@ void dbChannelDelete(dbChannel *chan) { chFilter *filter; - while ((filter = (chFilter *) ellGet(&chan->filters))) { + /* Close filters in reverse order */ + while ((filter = (chFilter *) ellPop(&chan->filters))) { filter->fif->channel_close(filter); free(filter); } diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index 82c79abb2..dc51c83e5 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -96,6 +96,8 @@ epicsShareFunc short dbChannelExportType(dbChannel *chan); epicsShareFunc short dbChannelElementSize(dbChannel *chan); epicsShareFunc short dbChannelSpecial(dbChannel *chan); epicsShareFunc void * dbChannelField(dbChannel *chan); +epicsShareFunc long dbChannelGet(dbChannel *chan, short type, + void *pbuffer, long *options, long *nRequest, void *pfl); epicsShareFunc long dbChannelGetField(dbChannel *chan, short type, void *pbuffer, long *options, long *nRequest, void *pfl); epicsShareFunc long dbChannelPut(dbChannel *chan, short type, From 58d1e22e0f14349f8ce8b60f4783d4e26d34a77f Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:46 -0400 Subject: [PATCH 23/81] Improve output from Show routines. --- src/ioc/db/dbChannel.c | 14 +++++++------- src/ioc/db/dbChannel.h | 8 +++++--- src/ioc/db/dbChannelIO.cpp | 2 +- src/ioc/db/test/dbChannelTest.c | 6 +++--- src/ioc/rsrv/caservertask.c | 15 +++++---------- 5 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index f88f55a83..f87e0359b 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -520,24 +520,24 @@ long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer, return dbPutField(&chan->addr, type, pbuffer, nRequest); } -void dbChannelShow(dbChannel *chan, int level) +void dbChannelShow(dbChannel *chan, const char *intro, int level) { long elems = chan->addr.no_elements; int count = ellCount(&chan->filters); - printf("dbChannel name: %s\n", chan->name); + printf("%schannel name: %s\n", intro, chan->name); /* FIXME: show field_type as text */ - printf(" field_type = %d, %ld element%s, %d filter%s\n", - chan->addr.field_type, elems, elems == 1 ? "" : "s", + printf("%s field_type=%d, %ld element%s, %d filter%s\n", + intro, chan->addr.field_type, elems, elems == 1 ? "" : "s", count, count == 1 ? "" : "s"); if (level > 0) - dbChannelFilterShow(chan, level - 1); + dbChannelFilterShow(chan, intro, level - 1); } -void dbChannelFilterShow(dbChannel *chan, int level) +void dbChannelFilterShow(dbChannel *chan, const char *intro, int level) { chFilter *filter = (chFilter *) ellFirst(&chan->filters); while (filter) { - filter->fif->channel_report(filter, level); + filter->fif->channel_report(filter, intro, level); filter = (chFilter *) ellNext(&filter->node); } } diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index dc51c83e5..f7d7c651a 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -68,7 +68,7 @@ typedef struct chFilterIf { /* Channel operations: */ long (* channel_open)(chFilter *filter); - void (* channel_report)(chFilter *filter, int level); + void (* channel_report)(chFilter *filter, const char *intro, int level); /* FIXME: More filter routines here ... */ void (* channel_close)(chFilter *filter); } chFilterIf; @@ -104,8 +104,10 @@ epicsShareFunc long dbChannelPut(dbChannel *chan, short type, const void *pbuffer, long nRequest); epicsShareFunc long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer, long nRequest); -epicsShareFunc void dbChannelShow(dbChannel *chan, int level); -epicsShareFunc void dbChannelFilterShow(dbChannel *chan, int level); +epicsShareFunc void dbChannelShow(dbChannel *chan, const char *intro, + int level); +epicsShareFunc void dbChannelFilterShow(dbChannel *chan, const char *intro, + int level); epicsShareFunc void dbChannelDelete(dbChannel *chan); epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif); diff --git a/src/ioc/db/dbChannelIO.cpp b/src/ioc/db/dbChannelIO.cpp index e9afafb83..c1da6b920 100644 --- a/src/ioc/db/dbChannelIO.cpp +++ b/src/ioc/db/dbChannelIO.cpp @@ -160,7 +160,7 @@ void dbChannelIO::show ( dbChannelElements ( this->dbch ), dbChannelField ( this->dbch ) ); if ( level > 1u ) { - dbChannelFilterShow ( this->dbch, level - 2u ); + dbChannelFilterShow ( this->dbch, "\t", level - 2u ); this->serviceIO.show ( level - 2u ); this->serviceIO.showAllIO ( *this, level - 2u ); } diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index 1f45b5253..9c4ebc91b 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -114,9 +114,9 @@ long c_open(chFilter *filter) testOk(e & e_open, "channel_open called"); return 0; } -void c_report(chFilter *filter, int level) +void c_report(chFilter *filter, const char *intro, int level) { - testOk(e & e_report, "channel_report called, level = %d", level); + testOk(e & e_report, "%s channel_report called, level = %d", level); } void c_close(chFilter *filter) { @@ -196,7 +196,7 @@ MAIN(dbChannelTest) testOk1(!!(pch = dbChannelCreate("x.{\"scalar\":null}"))); e = e_report; - dbChannelShow(pch, 1); + dbChannelShow(pch, "# ", 1); e = e_close; if (pch) dbChannelDelete(pch); diff --git a/src/ioc/rsrv/caservertask.c b/src/ioc/rsrv/caservertask.c index febd74f49..1d0b5f3e7 100644 --- a/src/ioc/rsrv/caservertask.c +++ b/src/ioc/rsrv/caservertask.c @@ -349,22 +349,18 @@ static unsigned countChanListBytes ( } static void showChanList ( - struct client * client, ELLLIST * pList ) + struct client * client, unsigned level, ELLLIST * pList ) { - unsigned i = 0u; struct channel_in_use * pciu; epicsMutexMustLock ( client->chanListLock ); pciu = (struct channel_in_use *) pList->node.next; while ( pciu ){ - printf( "\t%s (%d%c%c)", - dbChannelName(pciu->dbch), + dbChannelShow ( pciu->dbch, "\t", level ); + printf( "\t # on eventq=%d, access=%c%c\n", ellCount ( &pciu->eventq ), asCheckGet ( pciu->asClientPVT ) ? 'r': '-', rsrvCheckPut ( pciu ) ? 'w': '-' ); pciu = ( struct channel_in_use * ) ellNext ( &pciu->node ); - if( ++i % 3u == 0u ) { - printf ( "\n" ); - } } epicsMutexUnlock ( client->chanListLock ); } @@ -432,9 +428,8 @@ static void log_one_client (struct client *client, unsigned level) bytes_reserved += countChanListBytes ( client, & client->chanPendingUpdateARList ); printf( "\t%d bytes allocated\n", bytes_reserved); - showChanList ( client, & client->chanList ); - showChanList ( client, & client->chanPendingUpdateARList ); - printf("\n"); + showChanList ( client, level - 2u, & client->chanList ); + showChanList ( client, level - 2u, & client->chanPendingUpdateARList ); } if ( level >= 3u ) { From 06d3397342ffba73b2ff17332c936a674db8a0e0 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:47 -0400 Subject: [PATCH 24/81] Fix compiler warnings. --- src/ioc/db/test/dbChannelTest.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index 9c4ebc91b..af3b25ee5 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -77,7 +77,7 @@ parse_result p_double(chFilter *filter, double doubleVal) } parse_result p_string(chFilter *filter, const char *stringVal, size_t stringLen) { - testOk(e & e_string, "parse_string called, val = '%.*s'", stringLen, + testOk(e & e_string, "parse_string called, val = '%.*s'", (int) stringLen, stringVal); p_ret(e_string); } @@ -89,7 +89,7 @@ parse_result p_start_map(chFilter *filter) } parse_result p_map_key(chFilter *filter, const char *key, size_t stringLen) { - testOk(e & e_map_key, "parse_map_key called, key = '%.*s'", stringLen, key); + testOk(e & e_map_key, "parse_map_key called, key = '%.*s'", (int) stringLen, key); p_ret(e_map_key); } parse_result p_end_map(chFilter *filter) @@ -116,7 +116,7 @@ long c_open(chFilter *filter) } void c_report(chFilter *filter, const char *intro, int level) { - testOk(e & e_report, "%s channel_report called, level = %d", level); + testOk(e & e_report, "channel_report called, level = %d", level); } void c_close(chFilter *filter) { From 74b24bbe0fd1015f13af0d70e39289efd32fd7c9 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:21:47 -0400 Subject: [PATCH 25/81] Refactor link handling code (incomplete). * Moved routines into dbLink.h and dbLink.c * Created separate routines for DB_LINK processing * Added dbinitLink() and dbAddLink() * Started a Link Support Entry Table, not used yet * Everything should still work as before... --- src/ioc/db/Makefile | 2 + src/ioc/db/dbAccess.c | 616 ++++++++----------------------------- src/ioc/db/dbAccess.h | 4 +- src/ioc/db/dbAccessDefs.h | 49 +-- src/ioc/db/dbBkpt.c | 1 + src/ioc/db/dbCa.c | 9 +- src/ioc/db/dbCa.h | 1 + src/ioc/db/dbLink.c | 623 ++++++++++++++++++++++++++++++++++++++ src/ioc/db/dbLink.h | 87 ++++++ src/ioc/db/recGbl.c | 1 + src/ioc/dbStatic/link.h | 49 ++- src/ioc/misc/iocInit.c | 41 +-- 12 files changed, 874 insertions(+), 609 deletions(-) create mode 100644 src/ioc/db/dbLink.c create mode 100644 src/ioc/db/dbLink.h diff --git a/src/ioc/db/Makefile b/src/ioc/db/Makefile index 324edecb5..132861fda 100644 --- a/src/ioc/db/Makefile +++ b/src/ioc/db/Makefile @@ -21,6 +21,7 @@ INC += dbChannel.h INC += dbConvert.h INC += dbConvertFast.h INC += dbEvent.h +INC += dbLink.h INC += dbLock.h INC += dbNotify.h INC += dbScan.h @@ -57,6 +58,7 @@ dbCore_SRCS += dbBkpt.c dbCore_SRCS += dbChannel.c dbCore_SRCS += dbConvert.c dbCore_SRCS += dbFastLinkConv.c +dbCore_SRCS += dbLink.c dbCore_SRCS += dbNotify.c dbCore_SRCS += dbScan.c dbCore_SRCS += dbEvent.c diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index bca64397a..10dc7f468 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -46,6 +46,7 @@ #include "dbAddr.h" #include "callback.h" #include "dbScan.h" +#include "dbLink.h" #include "dbLock.h" #include "dbEvent.h" #include "dbConvert.h" @@ -90,17 +91,6 @@ static short mapDBFToDBR[DBF_NTYPES] = { /* The following is to handle SPC_AS */ static SPC_ASCALLBACK spcAsCallback = 0; -static void inherit_severity(const struct pv_link *ppv_link, - dbCommon *pdest, epicsEnum16 stat, epicsEnum16 sevr) -{ - switch(ppv_link->pvlMask&pvlOptMsMode) { - case pvlOptNMS: break; - case pvlOptMSI: if (sevr < INVALID_ALARM) break; - case pvlOptMS: recGblSetSevr(pdest,LINK_ALARM,sevr); break; - case pvlOptMSS: recGblSetSevr(pdest,stat,sevr); break; - } -} - void epicsShareAPI dbSpcAsRegisterCallback(SPC_ASCALLBACK func) { spcAsCallback = func; @@ -420,10 +410,10 @@ long epicsShareAPI dbPutAttribute( int epicsShareAPI dbIsValueField(const struct dbFldDes *pdbFldDes) { - if(pdbFldDes->pdbRecordType->indvalFlddes == pdbFldDes->indRecordType) - return(TRUE); + if (pdbFldDes->pdbRecordType->indvalFlddes == pdbFldDes->indRecordType) + return TRUE; else - return(FALSE); + return FALSE; } int epicsShareAPI dbGetFieldIndex(const struct dbAddr *paddr) @@ -431,50 +421,6 @@ int epicsShareAPI dbGetFieldIndex(const struct dbAddr *paddr) return paddr->pfldDes->indRecordType; } -long epicsShareAPI dbGetNelements(const struct link *plink,long *nelements) -{ - switch(plink->type) { - case CONSTANT: - *nelements = 0; - return(0); - case DB_LINK: { - DBADDR *paddr = (DBADDR *)plink->value.pv_link.pvt; - *nelements = paddr->no_elements; - return(0); - } - case CA_LINK: - return(dbCaGetNelements(plink,nelements)); - default: - break; - } - return(S_db_badField); -} - -int epicsShareAPI dbIsLinkConnected(const struct link *plink) -{ - switch(plink->type) { - case DB_LINK: return(TRUE); - case CA_LINK: return(dbCaIsLinkConnected(plink)); - default: break; - } - return(FALSE); -} - -int epicsShareAPI dbGetLinkDBFtype(const struct link *plink) -{ - switch(plink->type) { - case DB_LINK: - { - DBADDR *paddr = (DBADDR *)plink->value.pv_link.pvt; - - return((int)paddr->field_type); - } - case CA_LINK: return(dbCaGetLinkDBFtype(plink)); - default: break; - } - return(-1); -} - /* * Process a record if its scan field is passive. * Will notify if processing is complete by callback. @@ -482,48 +428,13 @@ int epicsShareAPI dbGetLinkDBFtype(const struct link *plink) */ long epicsShareAPI dbScanPassive(dbCommon *pfrom, dbCommon *pto) { - long status; - /* if not passive just return success */ - if(pto->scan != 0) return(0); + if (pto->scan != 0) + return 0; - if(pfrom && pfrom->ppn) dbNotifyAdd(pfrom,pto); - status = dbProcess(pto); - return(status); -} - -/*KLUDGE: Following needed so that dbPutLink to PROC field works correctly*/ -long epicsShareAPI dbScanLink(dbCommon *pfrom, dbCommon *pto) -{ - long status; - unsigned char pact; - - if(pfrom && pfrom->ppn) dbNotifyAdd(pfrom,pto); - pact = pfrom->pact; - pfrom->pact = TRUE; - status = dbProcess(pto); - pfrom->pact = pact; - return(status); -} - -void epicsShareAPI dbScanFwdLink(struct link *plink) -{ - dbCommon *precord; - struct pv_link *pvlink; - short fwdLinkValue; - - if(plink->type!=DB_LINK && plink->type!=CA_LINK) return; - pvlink = &plink->value.pv_link; - precord = pvlink->precord; - if(plink->type==DB_LINK) { - dbAddr *paddr = (dbAddr *)plink->value.pv_link.pvt; - dbScanPassive(precord,paddr->precord); - return; - } - if(!(pvlink->pvlMask & pvlOptFWD)) return; - fwdLinkValue = 1; - dbCaPutLink(plink,DBR_SHORT,&fwdLinkValue,1); - return; + if (pfrom && pfrom->ppn) + dbNotifyAdd(pfrom,pto); + return dbProcess(pto); } /* @@ -537,119 +448,125 @@ void epicsShareAPI dbScanFwdLink(struct link *plink) */ long epicsShareAPI dbProcess(dbCommon *precord) { - struct rset *prset = precord->rset; - dbRecordType *pdbRecordType = precord->rdes; - unsigned char tpro=precord->tpro; - long status = 0; - int *ptrace; - int set_trace=FALSE; - dbFldDes *pdbFldDes; - int callNotifyCompletion = FALSE; + struct rset *prset = precord->rset; + dbRecordType *pdbRecordType = precord->rdes; + unsigned char tpro = precord->tpro; + long status = 0; + int *ptrace; + int set_trace = FALSE; + dbFldDes *pdbFldDes; + int callNotifyCompletion = FALSE; - ptrace = dbLockSetAddrTrace(precord); + ptrace = dbLockSetAddrTrace(precord); + /* + * Note that it is likely that if any changes are made + * to dbProcess() corresponding changes will have to + * be made in the breakpoint handler. + */ + + /* see if there are any stopped records or breakpoints */ + if (lset_stack_count != 0) { /* - * Note that it is likely that if any changes are made - * to dbProcess() corresponding changes will have to - * be made in the breakpoint handler. + * Check to see if the record should be processed + * and activate breakpoint accordingly. If this + * function call returns non-zero, skip record + * support and fall out of dbProcess(). This is + * done so that a dbContTask() can be spawned to + * take over record processing for the lock set + * containing a breakpoint. */ + if (dbBkpt(precord)) + goto all_done; + } - /* see if there are any stopped records or breakpoints */ - if (lset_stack_count != 0) { - /* - * Check to see if the record should be processed - * and activate breakpoint accordingly. If this - * function call returns non-zero, skip record - * support and fall out of dbProcess(). This is - * done so that a dbContTask() can be spawned to - * take over record processing for the lock set - * containing a breakpoint. - */ - if (dbBkpt(precord)) - goto all_done; + /* check for trace processing*/ + if (tpro) { + if(*ptrace==0) { + *ptrace = 1; + set_trace = TRUE; } + } - /* check for trace processing*/ - if (tpro) { - if(*ptrace==0) { - *ptrace = 1; - set_trace = TRUE; - } - } + /* If already active dont process */ + if (precord->pact) { + unsigned short monitor_mask; - /* If already active dont process */ - if (precord->pact) { - unsigned short monitor_mask; + if (*ptrace) + printf("%s: Active %s\n", + epicsThreadGetNameSelf(), precord->name); + /* raise scan alarm after MAX_LOCK times */ + if (precord->stat==SCAN_ALARM) goto all_done; + if (precord->lcnt++ !=MAX_LOCK) goto all_done; + if (precord->sevr>=INVALID_ALARM) goto all_done; + recGblSetSevr(precord, SCAN_ALARM, INVALID_ALARM); + monitor_mask = recGblResetAlarms(precord); + monitor_mask |= DBE_VALUE|DBE_LOG; + pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes]; + db_post_events(precord, + (void *)(((char *)precord) + pdbFldDes->offset), + monitor_mask); + goto all_done; + } + else precord->lcnt = 0; - if (*ptrace) printf("%s: Active %s\n", - epicsThreadGetNameSelf(), precord->name); - /* raise scan alarm after MAX_LOCK times */ - if (precord->stat==SCAN_ALARM) goto all_done; - if (precord->lcnt++ !=MAX_LOCK) goto all_done; - if (precord->sevr>=INVALID_ALARM) goto all_done; - recGblSetSevr(precord, SCAN_ALARM, INVALID_ALARM); - monitor_mask = recGblResetAlarms(precord); - monitor_mask |= DBE_VALUE|DBE_LOG; - pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes]; - db_post_events(precord, - (void *)(((char *)precord) + pdbFldDes->offset), - monitor_mask); - goto all_done; - } - else precord->lcnt = 0; + /* + * Check the record disable link. A record will not be + * processed if the value retrieved through this link + * is equal to constant set in the record's disv field. + */ + status = dbGetLink(&precord->sdis, DBR_SHORT, &precord->disa, 0, 0); - /* - * Check the record disable link. A record will not be - * processed if the value retrieved through this link - * is equal to constant set in the record's disv field. - */ - status = dbGetLink(&(precord->sdis),DBR_SHORT,&(precord->disa),0,0); + /* if disabled check disable alarm severity and return success */ + if (precord->disa == precord->disv) { + if(*ptrace) + printf("%s: Disabled %s\n", + epicsThreadGetNameSelf(), precord->name); - /* if disabled check disable alarm severity and return success */ - if (precord->disa == precord->disv) { - if(*ptrace) printf("%s: Disabled %s\n", - epicsThreadGetNameSelf(), precord->name); - /*take care of caching and notifyCompletion*/ - precord->rpro = FALSE; - precord->putf = FALSE; - callNotifyCompletion = TRUE; - /* raise disable alarm */ - if (precord->stat==DISABLE_ALARM) goto all_done; - precord->sevr = precord->diss; - precord->stat = DISABLE_ALARM; - precord->nsev = 0; - precord->nsta = 0; - db_post_events(precord, &precord->stat, DBE_VALUE); - db_post_events(precord, &precord->sevr, DBE_VALUE); - pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes]; - db_post_events(precord, - (void *)(((char *)precord) + pdbFldDes->offset), - DBE_VALUE|DBE_ALARM); - goto all_done; - } + /*take care of caching and notifyCompletion*/ + precord->rpro = FALSE; + precord->putf = FALSE; + callNotifyCompletion = TRUE; - /* locate record processing routine */ - /* put this in iocInit() !!! */ - if (!(prset=precord->rset) || !(prset->process)) { - callNotifyCompletion = TRUE; - precord->pact=1;/*set pact TRUE so error is issued only once*/ - recGblRecordError(S_db_noRSET, (void *)precord, "dbProcess"); - status = S_db_noRSET; - if (*ptrace) printf("%s: No RSET for %s\n", - epicsThreadGetNameSelf(), precord->name); - goto all_done; - } - if(*ptrace) printf("%s: Process %s\n", - epicsThreadGetNameSelf(), precord->name); - /* process record */ - status = (*prset->process)(precord); - /* Print record's fields if PRINT_MASK set in breakpoint field */ - if (lset_stack_count != 0) { - dbPrint(precord); - } + /* raise disable alarm */ + if (precord->stat==DISABLE_ALARM) goto all_done; + precord->sevr = precord->diss; + precord->stat = DISABLE_ALARM; + precord->nsev = 0; + precord->nsta = 0; + db_post_events(precord, &precord->stat, DBE_VALUE); + db_post_events(precord, &precord->sevr, DBE_VALUE); + pdbFldDes = pdbRecordType->papFldDes[pdbRecordType->indvalFlddes]; + db_post_events(precord, + (void *)(((char *)precord) + pdbFldDes->offset), + DBE_VALUE|DBE_ALARM); + goto all_done; + } + + /* locate record processing routine */ + /* FIXME: put this in iocInit() !!! */ + if (!(prset=precord->rset) || !(prset->process)) { + callNotifyCompletion = TRUE; + precord->pact=1;/*set pact TRUE so error is issued only once*/ + recGblRecordError(S_db_noRSET, (void *)precord, "dbProcess"); + status = S_db_noRSET; + if (*ptrace) + printf("%s: No RSET for %s\n", + epicsThreadGetNameSelf(), precord->name); + goto all_done; + } + if(*ptrace) + printf("%s: Process %s\n", + epicsThreadGetNameSelf(), precord->name); + /* process record */ + status = (*prset->process)(precord); + /* Print record's fields if PRINT_MASK set in breakpoint field */ + if (lset_stack_count != 0) { + dbPrint(precord); + } all_done: - if (set_trace) *ptrace = 0; - if(callNotifyCompletion && precord->ppn) dbNotifyCompletion(precord); - return(status); + if (set_trace) *ptrace = 0; + if(callNotifyCompletion && precord->ppn) dbNotifyCompletion(precord); + return(status); } /* @@ -776,121 +693,6 @@ int epicsShareAPI dbLoadRecords(const char* file, const char* subs) } -long epicsShareAPI dbGetLinkValue(struct link *plink, short dbrType, - void *pbuffer, long *poptions, long *pnRequest) -{ - long status = 0; - - if (plink->type == CONSTANT) { - if (poptions) *poptions = 0; - if (pnRequest) *pnRequest = 0; - } else if (plink->type == DB_LINK) { - struct pv_link *ppv_link = &(plink->value.pv_link); - DBADDR *paddr = ppv_link->pvt; - dbCommon *precord = plink->value.pv_link.precord; - - /* scan passive records with links that are process passive */ - if (ppv_link->pvlMask&pvlOptPP) { - dbCommon *pfrom = paddr->precord; - unsigned char pact; - - pact = precord->pact; - precord->pact = TRUE; - status = dbScanPassive(precord,pfrom); - precord->pact = pact; - if (status) return status; - } - if(precord!= paddr->precord) { - inherit_severity(ppv_link,precord,paddr->precord->stat,paddr->precord->sevr); - } - - if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) { - status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr); - } else { - unsigned short dbfType = paddr->field_type; - long no_elements = paddr->no_elements; - - if (dbrType < 0 || dbrType > DBR_ENUM || - dbfType > DBF_DEVICE) { - status = S_db_badDbrtype; - recGblRecordError(status, precord, "GetLinkValue Failed"); - recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); - return status; - } - /* attempt to make a fast link */ - if ((!poptions || *poptions == 0) && - no_elements == 1 && - (!pnRequest || *pnRequest == 1) && - paddr->special != SPC_ATTRIBUTE) { - ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType]; - status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr); - } else { - ppv_link->getCvt = 0; - status = dbGet(paddr, dbrType, pbuffer, poptions, pnRequest, NULL); - } - } - ppv_link->lastGetdbrType = dbrType; - if (status) { - recGblRecordError(status, precord, "dbGetLinkValue"); - recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); - } - } else if (plink->type == CA_LINK) { - struct dbCommon *precord = plink->value.pv_link.precord; - const struct pv_link *pcalink = &plink->value.pv_link; - epicsEnum16 sevr, stat; - - status=dbCaGetLink(plink,dbrType,pbuffer,&stat,&sevr,pnRequest); - if (status) { - recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); - } else { - inherit_severity(pcalink,precord,stat,sevr); - } - if (poptions) *poptions = 0; - } else { - cantProceed("dbGetLinkValue: Illegal link type"); - } - return status; -} - -long epicsShareAPI dbPutLinkValue(struct link *plink, short dbrType, - const void *pbuffer, long nRequest) -{ - long status = 0; - - if (plink->type == DB_LINK) { - struct dbCommon *psource = plink->value.pv_link.precord; - struct pv_link *ppv_link = &plink->value.pv_link; - DBADDR *paddr = (DBADDR *)ppv_link->pvt; - dbCommon *pdest = paddr->precord; - - status = dbPut(paddr, dbrType, pbuffer, nRequest); - inherit_severity(ppv_link,pdest,psource->nsta,psource->nsev); - if (status) return status; - - if (paddr->pfield == &pdest->proc || - (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) { - /*if dbPutField caused asyn record to process */ - /* ask for reprocessing*/ - if (pdest->putf) { - pdest->rpro = TRUE; - } else { /* otherwise ask for the record to be processed*/ - status = dbScanLink(psource, pdest); - } - } - if (status) - recGblSetSevr(psource, LINK_ALARM, INVALID_ALARM); - } else if (plink->type == CA_LINK) { - struct dbCommon *psource = plink->value.pv_link.precord; - - status = dbCaPutLink(plink, dbrType, pbuffer, nRequest); - if (status < 0) - recGblSetSevr(psource, LINK_ALARM, INVALID_ALARM); - } else { - cantProceed("dbPutLinkValue: Illegal link type"); - } - return status; -} - static long getLinkValue(DBADDR *paddr, short dbrType, char *pbuf, long *nRequest) { @@ -1076,10 +878,9 @@ static long dbPutFieldLink(DBADDR *paddr, dbCommon *precord = paddr->precord; dbFldDes *pfldDes = paddr->pfldDes; long special = paddr->special; - DBLINK *plink = (DBLINK *)paddr->pfield; + struct link *plink = (struct link *)paddr->pfield; const char *pstring = (const char *)pbuffer; DBENTRY dbEntry; - DBADDR dbaddr; struct dsxt *old_dsxt = NULL; struct dset *new_dset = NULL; struct dsxt *new_dsxt = NULL; @@ -1150,21 +951,8 @@ static long dbPutFieldLink(DBADDR *paddr, switch (plink->type) { /* Old link type */ case DB_LINK: - free(plink->value.pv_link.pvt); - plink->value.pv_link.pvt = 0; - plink->type = PV_LINK; - plink->value.pv_link.getCvt = 0; - plink->value.pv_link.pvlMask = 0; - plink->value.pv_link.lastGetdbrType = 0; - dbLockSetSplit(precord); - break; - case CA_LINK: - dbCaRemoveLink(plink); - plink->type = PV_LINK; - plink->value.pv_link.getCvt = 0; - plink->value.pv_link.pvlMask = 0; - plink->value.pv_link.lastGetdbrType = 0; + dbRemoveLink(plink); break; case CONSTANT: @@ -1211,35 +999,7 @@ static long dbPutFieldLink(DBADDR *paddr, switch (plink->type) { /* New link type */ case PV_LINK: - if (plink == &precord->tsel) - recGblTSELwasModified(plink); - plink->value.pv_link.precord = precord; - - if (!(plink->value.pv_link.pvlMask & (pvlOptCA|pvlOptCP|pvlOptCPP)) && - (dbNameToAddr(plink->value.pv_link.pvname, &dbaddr) == 0)) { - /* It's a DB link */ - DBADDR *pdbAddr; - - plink->type = DB_LINK; - pdbAddr = dbMalloc(sizeof(struct dbAddr)); - *pdbAddr = dbaddr; /* NB: structure copy */; - plink->value.pv_link.pvt = pdbAddr; - dbLockSetRecordLock(pdbAddr->precord); - dbLockSetMerge(precord, pdbAddr->precord); - } else { /* Make it a CA link */ - char *pperiod; - - plink->type = CA_LINK; - if (pfldDes->field_type == DBF_INLINK) { - plink->value.pv_link.pvlMask |= pvlOptInpNative; - } - dbCaAddLink(plink); - if (pfldDes->field_type == DBF_FWDLINK) { - pperiod = strrchr(plink->value.pv_link.pvname, '.'); - if (pperiod && strstr(pperiod, "PROC")) - plink->value.pv_link.pvlMask |= pvlOptFWD; - } - } + dbAddLink(precord, plink, pfldDes->field_type); break; case CONSTANT: @@ -1267,7 +1027,7 @@ restoreScan: } postScanEvent: if (scan != precord->scan) - db_post_events(precord, &precord->scan, DBE_VALUE|DBE_LOG); + db_post_events(precord, &precord->scan, DBE_VALUE | DBE_LOG); unlock: dbLockSetGblUnlock(); finish: @@ -1424,139 +1184,3 @@ long epicsShareAPI dbPut(DBADDR *paddr, short dbrType, return status; } -/* various utility routines */ -long epicsShareAPI dbGetControlLimits( - const struct link *plink,double *low, double *high) -{ - struct buffer { - DBRctrlDouble - double value; - } buffer; - DBADDR *paddr; - long options = DBR_CTRL_DOUBLE; - long number_elements = 0; - long status; - - if(plink->type == CA_LINK) return(dbCaGetControlLimits(plink,low,high)); - if(plink->type !=DB_LINK) return(S_db_notFound); - paddr = (DBADDR *)plink->value.pv_link.pvt; - status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0); - if(status) return(status); - *low = buffer.lower_ctrl_limit; - *high = buffer.upper_ctrl_limit; - return(0); -} - -long epicsShareAPI dbGetGraphicLimits( - const struct link *plink,double *low, double *high) -{ - struct buffer { - DBRgrDouble - double value; - } buffer; - DBADDR *paddr; - long options = DBR_GR_DOUBLE; - long number_elements = 0; - long status; - - if(plink->type == CA_LINK) return(dbCaGetGraphicLimits(plink,low,high)); - if(plink->type !=DB_LINK) return(S_db_notFound); - paddr = (DBADDR *)plink->value.pv_link.pvt; - status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0); - if(status) return(status); - *low = buffer.lower_disp_limit; - *high = buffer.upper_disp_limit; - return(0); -} - -long epicsShareAPI dbGetAlarmLimits(const struct link *plink, - double *lolo, double *low, double *high, double *hihi) -{ - struct buffer { - DBRalDouble - double value; - } buffer; - DBADDR *paddr; - long options = DBR_AL_DOUBLE; - long number_elements = 0; - long status; - - if(plink->type == CA_LINK) - return(dbCaGetAlarmLimits(plink,lolo,low,high,hihi)); - if(plink->type !=DB_LINK) return(S_db_notFound); - paddr = (DBADDR *)plink->value.pv_link.pvt; - status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0); - if(status) return(status); - *lolo = buffer.lower_alarm_limit; - *low = buffer.lower_warning_limit; - *high = buffer.upper_warning_limit; - *hihi = buffer.upper_alarm_limit; - return(0); -} - -long epicsShareAPI dbGetPrecision(const struct link *plink,short *precision) -{ - struct buffer { - DBRprecision - double value; - } buffer; - DBADDR *paddr; - long options = DBR_PRECISION; - long number_elements = 0; - long status; - - if(plink->type == CA_LINK) return(dbCaGetPrecision(plink,precision)); - if(plink->type !=DB_LINK) return(S_db_notFound); - paddr = (DBADDR *)plink->value.pv_link.pvt; - status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0); - if(status) return(status); - *precision = buffer.precision.dp; - return(0); -} - -long epicsShareAPI dbGetUnits( - const struct link *plink,char *units,int unitsSize) -{ - struct buffer { - DBRunits - double value; - } buffer; - DBADDR *paddr; - long options = DBR_UNITS; - long number_elements = 0; - long status; - - if(plink->type == CA_LINK) return(dbCaGetUnits(plink,units,unitsSize)); - if(plink->type !=DB_LINK) return(S_db_notFound); - paddr = (DBADDR *)plink->value.pv_link.pvt; - status = dbGet(paddr,DBR_DOUBLE,&buffer,&options,&number_elements,0); - if(status) return(status); - strncpy(units,buffer.units,unitsSize); - return(0); -} - -long epicsShareAPI dbGetAlarm(const struct link *plink, - epicsEnum16 *status,epicsEnum16 *severity) -{ - DBADDR *paddr; - - if(plink->type == CA_LINK) return(dbCaGetAlarm(plink,status,severity)); - if(plink->type !=DB_LINK) return(S_db_notFound); - paddr = (DBADDR *)plink->value.pv_link.pvt; - if (status) *status = paddr->precord->stat; - if (severity) *severity = paddr->precord->sevr; - return(0); -} - -long epicsShareAPI dbGetTimeStamp(const struct link *plink,epicsTimeStamp *pstamp) -{ - DBADDR *paddr; - - if (plink->type == CA_LINK) - return dbCaGetTimeStamp(plink,pstamp); - if (plink->type != DB_LINK) - return S_db_notFound; - paddr = (DBADDR *)plink->value.pv_link.pvt; - *pstamp = paddr->precord->time; - return 0; -} diff --git a/src/ioc/db/dbAccess.h b/src/ioc/db/dbAccess.h index 909774323..96246b302 100644 --- a/src/ioc/db/dbAccess.h +++ b/src/ioc/db/dbAccess.h @@ -3,8 +3,7 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbAccess.h */ @@ -22,6 +21,7 @@ #include "dbAddr.h" #include "dbLock.h" #include "dbAccessDefs.h" +#include "dbLink.h" #include "dbCa.h" #include "dbCommon.h" #include "db_field_log.h" diff --git a/src/ioc/db/dbAccessDefs.h b/src/ioc/db/dbAccessDefs.h index 6c12d3df5..81fe939a4 100644 --- a/src/ioc/db/dbAccessDefs.h +++ b/src/ioc/db/dbAccessDefs.h @@ -3,8 +3,7 @@ * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* dbAccessDefs.h */ @@ -199,76 +198,30 @@ struct dbr_alDouble {DBRalDouble}; #define S_db_cntCont (M_dbAccess|65) /*Cannot resume dbContTask*/ #define S_db_noMemory (M_dbAccess|66) /*unable to allocate data structure from pool*/ -/* Global Database Access Routines*/ -#define dbGetLink(PLNK, DBRTYPE, PBUFFER, OPTIONS, NREQUEST) \ - ( ( ( (PLNK)->type == CONSTANT ) && \ - ( (NREQUEST) == 0) &&\ - ( (OPTIONS) == 0) ) \ - ? 0 \ - : dbGetLinkValue((PLNK),(DBRTYPE), \ - (void *)(PBUFFER), (OPTIONS), (NREQUEST) ) ) -#define dbPutLink(PLNK, DBRTYPE, PBUFFER, NREQUEST) \ - ( ( (PLNK)->type == CONSTANT) \ - ? 0 \ - : dbPutLinkValue( (PLNK), (DBRTYPE), (void *)(PBUFFER), (NREQUEST) ) ) -#define dbGetPdbAddrFromLink(PLNK) \ - ( ( (PLNK)->type != DB_LINK ) \ - ? 0 \ - : ( ( (struct dbAddr *)( (PLNK)->value.pv_link.pvt) ) ) ) -#define dbGetSevr(PLINK,PSEVERITY) \ - dbGetAlarm((PLINK),NULL,(PSEVERITY)) - epicsShareFunc long epicsShareAPI dbPutSpecial(struct dbAddr *paddr,int pass); epicsShareFunc struct rset * epicsShareAPI dbGetRset(const struct dbAddr *paddr); epicsShareFunc long epicsShareAPI dbPutAttribute( const char *recordTypename,const char *name,const char*value); epicsShareFunc int epicsShareAPI dbIsValueField(const struct dbFldDes *pdbFldDes); epicsShareFunc int epicsShareAPI dbGetFieldIndex(const struct dbAddr *paddr); -epicsShareFunc long epicsShareAPI dbGetNelements( - const struct link *plink,long *nelements); -epicsShareFunc int epicsShareAPI dbIsLinkConnected(const struct link *plink); -epicsShareFunc int epicsShareAPI dbGetLinkDBFtype(const struct link *plink); -epicsShareFunc long epicsShareAPI dbScanLink( - struct dbCommon *pfrom, struct dbCommon *pto); epicsShareFunc long epicsShareAPI dbScanPassive( struct dbCommon *pfrom,struct dbCommon *pto); -epicsShareFunc void epicsShareAPI dbScanFwdLink(struct link *plink); epicsShareFunc long epicsShareAPI dbProcess(struct dbCommon *precord); epicsShareFunc long epicsShareAPI dbNameToAddr( const char *pname,struct dbAddr *); epicsShareFunc devSup* epicsShareAPI dbDTYPtoDevSup(dbRecordType *prdes, int dtyp); epicsShareFunc devSup* epicsShareAPI dbDSETtoDevSup(dbRecordType *prdes, struct dset *pdset); -epicsShareFunc long epicsShareAPI dbGetLinkValue( - struct link *,short dbrType,void *pbuffer,long *options,long *nRequest); epicsShareFunc long epicsShareAPI dbGetField( struct dbAddr *,short dbrType,void *pbuffer,long *options, long *nRequest,void *pfl); epicsShareFunc long epicsShareAPI dbGet( struct dbAddr *,short dbrType,void *pbuffer,long *options, long *nRequest,void *pfl); -epicsShareFunc long epicsShareAPI dbPutLinkValue( - struct link *,short dbrType,const void *pbuffer,long nRequest); epicsShareFunc long epicsShareAPI dbPutField( struct dbAddr *,short dbrType,const void *pbuffer,long nRequest); epicsShareFunc long epicsShareAPI dbPut( struct dbAddr *,short dbrType,const void *pbuffer,long nRequest); -/* various utility routines */ -epicsShareFunc long epicsShareAPI dbGetControlLimits( - const struct link *plink,double *low, double *high); -epicsShareFunc long epicsShareAPI dbGetGraphicLimits( - const struct link *plink,double *low, double *high); -epicsShareFunc long epicsShareAPI dbGetAlarmLimits( - const struct link *plink,double *lolo, double *low, double *high, double *hihi); -epicsShareFunc long epicsShareAPI dbGetPrecision( - const struct link *plink,short *precision); -epicsShareFunc long epicsShareAPI dbGetUnits( - const struct link *plink,char *units,int unitsSize); -epicsShareFunc long epicsShareAPI dbGetAlarm( - const struct link *plink, epicsEnum16 *status,epicsEnum16 *severity); -epicsShareFunc long epicsShareAPI dbGetTimeStamp( - const struct link *plink,epicsTimeStamp *pstamp); - typedef void(*SPC_ASCALLBACK)(struct dbCommon *); /*dbSpcAsRegisterCallback called by access security */ epicsShareFunc void epicsShareAPI dbSpcAsRegisterCallback(SPC_ASCALLBACK func); diff --git a/src/ioc/db/dbBkpt.c b/src/ioc/db/dbBkpt.c index f865ba80c..681886676 100644 --- a/src/ioc/db/dbBkpt.c +++ b/src/ioc/db/dbBkpt.c @@ -65,6 +65,7 @@ #include "dbAddr.h" #include "dbAccessDefs.h" #include "dbScan.h" +#include "dbLink.h" #include "dbLock.h" #include "recGbl.h" #include "dbTest.h" diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 9f90dabc0..5db15d323 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -411,6 +411,13 @@ int dbCaIsLinkConnected(const struct link *plink) return pca->isConnected; } +void dbCaScanFwdLink(struct link *plink) { + short fwdLinkValue = 1; + + if (plink->value.pv_link.pvlMask & pvlOptFWD) + dbCaPutLink(plink, DBR_SHORT, &fwdLinkValue, 1); +} + #define pcaGetCheck \ assert(plink); \ if (plink->type != CA_LINK) return -1; \ @@ -639,7 +646,7 @@ done: static void eventCallback(struct event_handler_args arg) { caLink *pca = (caLink *)arg.usr; - DBLINK *plink; + struct link *plink; size_t size; dbCommon *precord = 0; struct dbr_time_double *pdbr_time_double; diff --git a/src/ioc/db/dbCa.h b/src/ioc/db/dbCa.h index ee0fee1ca..bf6f2edd9 100644 --- a/src/ioc/db/dbCa.h +++ b/src/ioc/db/dbCa.h @@ -40,6 +40,7 @@ epicsShareFunc long dbCaPutLinkCallback(struct link *plink, #define dbCaPutLink(plink, dbrType, pbuffer, nRequest) \ dbCaPutLinkCallback((plink), (dbrType), (pbuffer), (nRequest), 0, 0) epicsShareFunc int dbCaIsLinkConnected(const struct link *plink); +epicsShareFunc void dbCaScanFwdLink(struct link *plink); /* The following are available after the link is connected*/ epicsShareFunc long dbCaGetNelements(const struct link *plink, diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c new file mode 100644 index 000000000..870183136 --- /dev/null +++ b/src/ioc/db/dbLink.c @@ -0,0 +1,623 @@ +/*************************************************************************\ +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne + * National Laboratory. + * Copyright (c) 2002 The Regents of the University of California, as + * Operator of Los Alamos National Laboratory. + * EPICS BASE is distributed subject to a Software License Agreement found + * in file LICENSE that is included with this distribution. + \*************************************************************************/ +/* dbLink.c */ +/* $Id$ */ +/* + * Original Authors: Bob Dalesio, Marty Kraimer + * Current Author: Andrew Johnson + */ + +#include +#include +#include +#include +#include + +#include "dbDefs.h" +#include "epicsThread.h" +#include "errlog.h" +#include "cantProceed.h" +#include "cvtFast.h" +#include "epicsTime.h" +#include "alarm.h" +#include "ellLib.h" +#include "dbStaticLib.h" +#include "dbBase.h" +#include "link.h" +#include "dbFldTypes.h" +#include "recSup.h" +#include "devSup.h" +#include "caeventmask.h" +#include "db_field_log.h" +#include "dbCommon.h" +#include "dbFldTypes.h" +#include "special.h" +#include "errMdef.h" +#define epicsExportSharedSymbols +#include "dbAddr.h" +#include "dbLink.h" +#include "callback.h" +#include "dbScan.h" +#include "dbLock.h" +#include "dbEvent.h" +#include "dbConvert.h" +#include "dbConvertFast.h" +#include "epicsEvent.h" +#include "dbCa.h" +#include "dbBkpt.h" +#include "dbNotify.h" +#include "dbAccessDefs.h" +#include "recGbl.h" + +static void inherit_severity(const struct pv_link *ppv_link, dbCommon *pdest, + epicsEnum16 stat, epicsEnum16 sevr) +{ + switch (ppv_link->pvlMask & pvlOptMsMode) { + case pvlOptNMS: + break; + case pvlOptMSI: + if (sevr < INVALID_ALARM) + break; + /* Fall through */ + case pvlOptMS: + recGblSetSevr(pdest, LINK_ALARM, sevr); + break; + case pvlOptMSS: + recGblSetSevr(pdest, stat, sevr); + break; + } +} + +/***************************** Constant Links *****************************/ + +static long dbConstGetNelements(const struct link *plink, long *nelements) +{ + *nelements = 0; + return 0; +} + +static long dbConstGetLink(struct link *plink, short dbrType, void *pbuffer, + epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest) +{ + if (pnRequest) + *pnRequest = 0; + return 0; +} + +/***************************** Database Links *****************************/ + +static long dbDbInitLink(struct link *plink, short dbfType) +{ + DBADDR dbaddr; + long status; + DBADDR *pdbAddr; + + status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr); + if (status) + return status; + + plink->type = DB_LINK; + pdbAddr = dbCalloc(1, sizeof(struct dbAddr)); + *pdbAddr = dbaddr; /* structure copy */ + plink->value.pv_link.pvt = pdbAddr; + return 0; +} + +static long dbDbAddLink(struct link *plink, short dbfType) +{ + long status = dbDbInitLink(plink, dbfType); + + if (!status) { + DBADDR *pdbAddr = (DBADDR *) plink->value.pv_link.pvt; + dbCommon *precord = plink->value.pv_link.precord; + + dbLockSetRecordLock(pdbAddr->precord); + dbLockSetMerge(precord, pdbAddr->precord); + } + return status; +} + +static void dbDbRemoveLink(struct link *plink) +{ + free(plink->value.pv_link.pvt); + plink->value.pv_link.pvt = 0; + plink->value.pv_link.getCvt = 0; + plink->value.pv_link.lastGetdbrType = 0; + dbLockSetSplit(plink->value.pv_link.precord); +} + +static int dbDbIsLinkConnected(const struct link *plink) +{ + return TRUE; +} + +static int dbDbGetDBFtype(const struct link *plink) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + + return paddr->field_type; +} + +static long dbDbGetElements(const struct link *plink, long *nelements) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + + *nelements = paddr->no_elements; + return 0; +} + +static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer, + epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest) +{ + struct pv_link *ppv_link = &plink->value.pv_link; + DBADDR *paddr = ppv_link->pvt; + dbCommon *precord = plink->value.pv_link.precord; + long status; + + /* scan passive records if link is process passive */ + if (ppv_link->pvlMask & pvlOptPP) { + unsigned char pact = precord->pact; + + precord->pact = TRUE; + status = dbScanPassive(precord, paddr->precord); + precord->pact = pact; + if (status) + return status; + } + *pstat = paddr->precord->stat; + *psevr = paddr->precord->sevr; + + if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) { + status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr); + } else { + unsigned short dbfType = paddr->field_type; + + if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE) + return S_db_badDbrtype; + + if (paddr->no_elements == 1 && (!pnRequest || *pnRequest == 1) + && paddr->special != SPC_ATTRIBUTE) { + ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType]; + status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr); + } else { + ppv_link->getCvt = NULL; + status = dbGet(paddr, dbrType, pbuffer, NULL, pnRequest, NULL); + } + ppv_link->lastGetdbrType = dbrType; + } + return status; +} + +static long dbDbGetControlLimits(const struct link *plink, double *low, + double *high) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + struct buffer { + DBRctrlDouble + double value; + } buffer; + long options = DBR_CTRL_DOUBLE; + long number_elements = 0; + long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, + NULL); + + if (status) + return status; + + *low = buffer.lower_ctrl_limit; + *high = buffer.upper_ctrl_limit; + return 0; +} + +static long dbDbGetGraphicLimits(const struct link *plink, double *low, + double *high) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + struct buffer { + DBRgrDouble + double value; + } buffer; + long options = DBR_GR_DOUBLE; + long number_elements = 0; + long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, + NULL); + + if (status) + return status; + + *low = buffer.lower_disp_limit; + *high = buffer.upper_disp_limit; + return 0; +} + +static long dbDbGetAlarmLimits(const struct link *plink, double *lolo, + double *low, double *high, double *hihi) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + struct buffer { + DBRalDouble + double value; + } buffer; + long options = DBR_AL_DOUBLE; + long number_elements = 0; + long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, + 0); + + if (status) + return status; + + *lolo = buffer.lower_alarm_limit; + *low = buffer.lower_warning_limit; + *high = buffer.upper_warning_limit; + *hihi = buffer.upper_alarm_limit; + return 0; +} + +static long dbDbGetPrecision(const struct link *plink, short *precision) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + struct buffer { + DBRprecision + double value; + } buffer; + long options = DBR_PRECISION; + long number_elements = 0; + long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, + 0); + + if (status) + return status; + + *precision = buffer.precision.dp; + return 0; +} + +static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + struct buffer { + DBRunits + double value; + } buffer; + long options = DBR_UNITS; + long number_elements = 0; + long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements, + 0); + + if (status) + return status; + + strncpy(units, buffer.units, unitsSize); + return 0; +} + +static long dbDbGetAlarm(const struct link *plink, epicsEnum16 *status, + epicsEnum16 *severity) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + + if (status) + *status = paddr->precord->stat; + if (severity) + *severity = paddr->precord->sevr; + return 0; +} + +static long dbDbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp) +{ + DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt; + + *pstamp = paddr->precord->time; + return 0; +} + +static long dbDbPutValue(struct link *plink, short dbrType, + const void *pbuffer, long nRequest) +{ + struct pv_link *ppv_link = &plink->value.pv_link; + struct dbCommon *psrce = ppv_link->precord; + DBADDR *paddr = (DBADDR *) ppv_link->pvt; + dbCommon *pdest = paddr->precord; + long status = dbPut(paddr, dbrType, pbuffer, nRequest); + + inherit_severity(ppv_link, pdest, psrce->nsta, psrce->nsev); + if (status) + return status; + + if (paddr->pfield == (void *) &pdest->proc || + (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) { + /* if dbPutField caused asyn record to process */ + /* ask for reprocessing*/ + if (pdest->putf) { + pdest->rpro = TRUE; + } else { /* process dest record with source's PACT true */ + unsigned char pact; + + if (psrce && psrce->ppn) + dbNotifyAdd(psrce, pdest); + pact = psrce->pact; + psrce->pact = TRUE; + status = dbProcess(pdest); + psrce->pact = pact; + } + } + return status; +} + +static void dbDbScanFwdLink(struct link *plink) +{ + dbCommon *precord = plink->value.pv_link.precord; + dbAddr *paddr = (dbAddr *) plink->value.pv_link.pvt; + + dbScanPassive(precord, paddr->precord); +} + +lset dbDb_lset = { dbDbInitLink, dbDbAddLink, dbDbRemoveLink, + dbDbIsLinkConnected, dbDbGetDBFtype, dbDbGetElements, dbDbGetValue, + dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits, + dbDbGetPrecision, dbDbGetUnits, dbDbGetAlarm, dbDbGetTimeStamp, + dbDbPutValue, dbDbScanFwdLink }; + +/***************************** Generic Link API *****************************/ + +void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType) +{ + plink->value.pv_link.precord = precord; + + if (plink == &precord->tsel) + recGblTSELwasModified(plink); + + if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) { + /* Make it a DB link if possible */ + if (!dbDbInitLink(plink, dbfType)) + return; + } + + /* Make it a CA link */ + if (dbfType == DBF_INLINK) + plink->value.pv_link.pvlMask |= pvlOptInpNative; + + dbCaAddLink(plink); + if (dbfType == DBF_FWDLINK) { + char *pperiod = strrchr(plink->value.pv_link.pvname, '.'); + + if (pperiod && strstr(pperiod, "PROC")) { + plink->value.pv_link.pvlMask |= pvlOptFWD; + } else { + errlogPrintf("%s.FLNK is a Channel Access Link " + " but does not link to a PROC field\n", precord->name); + } + } +} + +void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType) +{ + plink->value.pv_link.precord = precord; + + if (plink == &precord->tsel) + recGblTSELwasModified(plink); + + if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) { + /* Make it a DB link if possible */ + if (!dbDbAddLink(plink, dbfType)) + return; + } + + /* Make it a CA link */ + if (dbfType == DBF_INLINK) + plink->value.pv_link.pvlMask |= pvlOptInpNative; + + dbCaAddLink(plink); + if (dbfType == DBF_FWDLINK) { + char *pperiod = strrchr(plink->value.pv_link.pvname, '.'); + + if (pperiod && strstr(pperiod, "PROC")) + plink->value.pv_link.pvlMask |= pvlOptFWD; + } +} + +void dbRemoveLink(struct link *plink) +{ + switch (plink->type) { + case DB_LINK: + dbDbRemoveLink(plink); + break; + case CA_LINK: + dbCaRemoveLink(plink); + break; + default: + cantProceed("dbRemoveLink: Unexpected link type %d\n", plink->type); + } + plink->type = PV_LINK; + plink->value.pv_link.pvlMask = 0; +} + +int dbIsLinkConnected(const struct link *plink) +{ + switch (plink->type) { + case DB_LINK: + return dbDbIsLinkConnected(plink); + case CA_LINK: + return dbCaIsLinkConnected(plink); + } + return FALSE; +} + +int dbGetLinkDBFtype(const struct link *plink) +{ + switch (plink->type) { + case DB_LINK: + return dbDbGetDBFtype(plink); + case CA_LINK: + return dbCaGetLinkDBFtype(plink); + } + return -1; +} + +long dbGetNelements(const struct link *plink, long *nelements) +{ + switch (plink->type) { + case CONSTANT: + return dbConstGetNelements(plink, nelements); + case DB_LINK: + return dbDbGetElements(plink, nelements); + case CA_LINK: + return dbCaGetNelements(plink, nelements); + } + return S_db_badField; +} + +long dbGetLinkValue(struct link *plink, short dbrType, void *pbuffer, + long *poptions, long *pnRequest) +{ + struct dbCommon *precord = plink->value.pv_link.precord; + epicsEnum16 sevr = 0, stat = 0; + long status; + + if (poptions && *poptions) { + printf("dbGetLinkValue: Use of poptions no longer supported\n"); + *poptions = 0; + } + + switch (plink->type) { + case CONSTANT: + status = dbConstGetLink(plink, dbrType, pbuffer, &stat, &sevr, + pnRequest); + break; + case DB_LINK: + status = dbDbGetValue(plink, dbrType, pbuffer, &stat, &sevr, pnRequest); + break; + case CA_LINK: + status = dbCaGetLink(plink, dbrType, pbuffer, &stat, &sevr, pnRequest); + break; + default: + cantProceed("dbGetLinkValue: Illegal link type"); + } + if (status) { + recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); + } else { + inherit_severity(&plink->value.pv_link, precord, stat, sevr); + } + return status; +} + +long dbGetControlLimits(const struct link *plink, double *low, double *high) +{ + switch (plink->type) { + case DB_LINK: + return dbDbGetControlLimits(plink, low, high); + case CA_LINK: + return dbCaGetControlLimits(plink, low, high); + } + return S_db_notFound; +} + +long dbGetGraphicLimits(const struct link *plink, double *low, double *high) +{ + switch (plink->type) { + case DB_LINK: + return dbDbGetGraphicLimits(plink, low, high); + case CA_LINK: + return dbCaGetGraphicLimits(plink, low, high); + } + return S_db_notFound; +} + +long dbGetAlarmLimits(const struct link *plink, double *lolo, double *low, + double *high, double *hihi) +{ + switch (plink->type) { + case DB_LINK: + return dbDbGetAlarmLimits(plink, lolo, low, high, hihi); + case CA_LINK: + return dbCaGetAlarmLimits(plink, lolo, low, high, hihi); + } + return S_db_notFound; +} + +long dbGetPrecision(const struct link *plink, short *precision) +{ + switch (plink->type) { + case DB_LINK: + return dbDbGetPrecision(plink, precision); + case CA_LINK: + return dbCaGetPrecision(plink, precision); + } + return S_db_notFound; +} + +long dbGetUnits(const struct link *plink, char *units, int unitsSize) +{ + switch (plink->type) { + case DB_LINK: + return dbDbGetUnits(plink, units, unitsSize); + case CA_LINK: + return dbCaGetUnits(plink, units, unitsSize); + } + return S_db_notFound; +} + +long dbGetAlarm(const struct link *plink, epicsEnum16 *status, + epicsEnum16 *severity) +{ + switch (plink->type) { + case DB_LINK: + return dbDbGetAlarm(plink, status, severity); + case CA_LINK: + return dbCaGetAlarm(plink, status, severity); + } + return S_db_notFound; +} + +long dbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp) +{ + switch (plink->type) { + case DB_LINK: + return dbDbGetTimeStamp(plink, pstamp); + case CA_LINK: + return dbCaGetTimeStamp(plink, pstamp); + } + return S_db_notFound; +} + +long dbPutLinkValue(struct link *plink, short dbrType, const void *pbuffer, + long nRequest) +{ + long status; + + switch (plink->type) { + case DB_LINK: + status = dbDbPutValue(plink, dbrType, pbuffer, nRequest); + break; + case CA_LINK: + status = dbCaPutLink(plink, dbrType, pbuffer, nRequest); + break; + default: + cantProceed("dbPutLinkValue: Illegal link type %d", plink->type); + } + if (status) { + struct dbCommon *precord = plink->value.pv_link.precord; + + recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); + } + return status; +} + +void dbScanFwdLink(struct link *plink) +{ + switch (plink->type) { + case DB_LINK: + dbDbScanFwdLink(plink); + break; + case CA_LINK: + dbCaScanFwdLink(plink); + break; + } +} + diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h new file mode 100644 index 000000000..0f2e14da8 --- /dev/null +++ b/src/ioc/db/dbLink.h @@ -0,0 +1,87 @@ +/*************************************************************************\ +* Copyright (c) 2010 The UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ +/* + * dbLink.h + * + * Created on: Mar 21, 2010 + * Author: Andrew Johnson + */ + +#ifndef INC_dbLink_H +#define INC_dbLink_H + +#include "link.h" +#include "shareLib.h" + +typedef struct lset { + long (*initLink)(struct link *plink, short dbfType); + long (*addLink)(struct link *plink, short dbfType); + void (*removeLink)(struct link *plink); + int (*isLinkConnected)(const struct link *plink); + int (*getDBFtype)(const struct link *plink); + long (*getElements)(const struct link *plink, long *nelements); + long (*getValue)(struct link *plink, short dbrType, void *pbuffer, + epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest); + long (*getControlLimits)(const struct link *plink, double *lo, double *hi); + long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi); + long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo, + double *hi, double *hihi); + long (*getPrecision)(const struct link *plink, short *precision); + long (*getUnits)(const struct link *plink, char *units, int unitsSize); + long (*getAlarm)(const struct link *plink, epicsEnum16 *status, + epicsEnum16 *severity); + long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp); + long (*putValue)(struct link *plink, short dbrType, + const void *pbuffer, long nRequest); + void (*scanFwdLink)(struct link *plink); +} lset; + +#define dbGetLink(PLNK, DBRTYPE, PBUFFER, OPTIONS, NREQUEST) \ + ( ( ( (PLNK)->type == CONSTANT ) && \ + ( (NREQUEST) == 0) && \ + ( (OPTIONS) == 0) ) \ + ? 0 \ + : dbGetLinkValue((PLNK),(DBRTYPE), \ + (void *)(PBUFFER), (OPTIONS), (NREQUEST) ) ) + +#define dbPutLink(PLNK, DBRTYPE, PBUFFER, NREQUEST) \ + ( ( (PLNK)->type == CONSTANT) \ + ? 0 \ + : dbPutLinkValue( (PLNK), (DBRTYPE), (void *)(PBUFFER), (NREQUEST) ) ) + +#define dbGetSevr(PLINK, PSEVERITY) \ + dbGetAlarm((PLINK), NULL, (PSEVERITY)); + +epicsShareFunc void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType); +epicsShareFunc void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType); +epicsShareFunc void dbRemoveLink(struct link *plink); +epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements); +epicsShareFunc int dbIsLinkConnected(const struct link *plink); +epicsShareFunc int dbGetLinkDBFtype(const struct link *plink); +epicsShareFunc long dbGetLinkValue(struct link *, short dbrType, void *pbuffer, + long *options, long *nRequest); +epicsShareFunc long dbGetControlLimits(const struct link *plink, double *low, + double *high); +epicsShareFunc long dbGetGraphicLimits(const struct link *plink, double *low, + double *high); +epicsShareFunc long dbGetAlarmLimits(const struct link *plink, double *lolo, + double *low, double *high, double *hihi); +epicsShareFunc long dbGetPrecision(const struct link *plink, short *precision); +epicsShareFunc long dbGetUnits(const struct link *plink, char *units, + int unitsSize); +epicsShareFunc long dbGetAlarm(const struct link *plink, epicsEnum16 *status, + epicsEnum16 *severity); +epicsShareFunc long dbGetTimeStamp(const struct link *plink, + epicsTimeStamp *pstamp); +epicsShareFunc long dbPutLinkValue(struct link *, short dbrType, + const void *pbuffer, long nRequest); +epicsShareFunc void dbScanFwdLink(struct link *plink); + + +#endif /* INC_dbLink_H */ diff --git a/src/ioc/db/recGbl.c b/src/ioc/db/recGbl.c index 7cdf69a19..828126e63 100644 --- a/src/ioc/db/recGbl.c +++ b/src/ioc/db/recGbl.c @@ -34,6 +34,7 @@ #include "caeventmask.h" #define epicsExportSharedSymbols #include "dbAccessDefs.h" +#include "dbLink.h" #include "dbNotify.h" #include "dbCa.h" #include "dbEvent.h" diff --git a/src/ioc/dbStatic/link.h b/src/ioc/dbStatic/link.h index c9818f6f0..ef3cfc69d 100644 --- a/src/ioc/dbStatic/link.h +++ b/src/ioc/dbStatic/link.h @@ -1,27 +1,24 @@ /*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* link.h */ -/* base/include $Revision-Id$ */ +/* $Id$ */ /* - * Original Author: Bob Dalesio - * Current Author: Marty Kraimer - * Date: 6-1-90 + * Original Authors: Bob Dalesio, Marty Kraimer */ +#ifndef INC_link_H +#define INC_link_H + #include "dbDefs.h" #include "shareLib.h" -#ifndef INClinkh -#define INClinkh 1 - #ifdef __cplusplus extern "C" { #endif @@ -55,8 +52,8 @@ epicsShareExtern maplinkType pamaplinkType[]; /* structure of a PV_LINK DB_LINK and a CA_LINK */ /*Options defined by pvlMask */ #define pvlOptMsMode 0x3 /*Maximize Severity mode selection*/ -#define pvlOptNMS 0 /*Dont Maximize Severity*/ -#define pvlOptMS 1 /*Maximize Severity allways*/ +#define pvlOptNMS 0 /*Don't Maximize Severity*/ +#define pvlOptMS 1 /*Maximize Severity always*/ #define pvlOptMSI 2 /*Maximize Severity if INVALID*/ #define pvlOptMSS 3 /*Maximize Severity and copy Status*/ #define pvlOptPP 0x4 /*Process Passive*/ @@ -77,14 +74,15 @@ struct macro_link { }; struct dbCommon; +struct pvlet; struct pv_link { - char *pvname; /*pvname to link to*/ - struct dbCommon *precord; /*Address of record containing link*/ - void *pvt; /*CA or DB private*/ - LINKCVT getCvt; /*input conversion function*/ - short pvlMask; /*Options mask*/ - short lastGetdbrType; /*last dbrType for DB or CA get*/ + char *pvname; /* pvname link points to */ + struct dbCommon *precord; /* Address of record owning link */ + void *pvt; /* CA or DB private */ + LINKCVT getCvt; /* input conversion function */ + short pvlMask; /* Options mask */ + short lastGetdbrType; /* last dbrType for DB or CA get */ }; /* structure of a VME io channel */ @@ -150,13 +148,12 @@ struct bbgpibio { /* structure of an instrument io link */ struct instio { - char *string; /* the cat of location. - signal.parameter */ + char *string; }; /* structure of a vxi link */ struct vxiio{ - short flag; /* 0 = frame/slot, 1 = SA */ + short flag; /* 0 = frame/slot, 1 = SA */ short frame; short slot; short la; /* logical address if flag =1 */ @@ -165,7 +162,7 @@ struct vxiio{ }; /* union of possible address structures */ -union value{ +union value { char *constantStr; /*constant string*/ struct macro_link macro_link; /* link containing macro substitution*/ struct pv_link pv_link; /* link to process variable*/ @@ -180,9 +177,9 @@ union value{ struct vxiio vxiio; /* vxi io */ }; -struct link{ - union value value; - short type; +struct link { + union value value; + short type; char *text; /* original INP/OUT link text */ }; @@ -191,4 +188,4 @@ typedef struct link DBLINK; #ifdef __cplusplus } #endif -#endif /*INClinkh*/ +#endif /* INC_link_H */ diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index 9f9c1a385..f4e3b7e9e 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -41,7 +41,6 @@ #include "taskwd.h" #include "callback.h" #include "dbCommon.h" -#include "dbLock.h" #include "devSup.h" #include "drvSup.h" #include "menuConvert.h" @@ -426,48 +425,18 @@ static void doInitRecord0(dbRecordType *pdbRecordType, dbCommon *precord, static void doResolveLinks(dbRecordType *pdbRecordType, dbCommon *precord, void *user) { + dbFldDes **papFldDes = pdbRecordType->papFldDes; + short *link_ind = pdbRecordType->link_ind; devSup *pdevSup; int j; - /* Convert all PV_LINKs to DB_LINKs or CA_LINKs */ /* For all the links in the record type... */ for (j = 0; j < pdbRecordType->no_links; j++) { - dbFldDes *pdbFldDes = - pdbRecordType->papFldDes[pdbRecordType->link_ind[j]]; + dbFldDes *pdbFldDes = papFldDes[link_ind[j]]; DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset); - if (plink->type == PV_LINK) { - DBADDR dbaddr; - - if (plink == &precord->tsel) recGblTSELwasModified(plink); - if (!(plink->value.pv_link.pvlMask&(pvlOptCA|pvlOptCP|pvlOptCPP)) - && (dbNameToAddr(plink->value.pv_link.pvname,&dbaddr)==0)) { - DBADDR *pdbAddr; - - plink->type = DB_LINK; - pdbAddr = dbCalloc(1,sizeof(struct dbAddr)); - *pdbAddr = dbaddr; /*structure copy*/; - plink->value.pv_link.pvt = pdbAddr; - } else {/*It is a CA link*/ - - if (pdbFldDes->field_type == DBF_INLINK) { - plink->value.pv_link.pvlMask |= pvlOptInpNative; - } - dbCaAddLink(plink); - if (pdbFldDes->field_type == DBF_FWDLINK) { - char *pperiod = - strrchr(plink->value.pv_link.pvname,'.'); - - if (pperiod && strstr(pperiod,"PROC")) { - plink->value.pv_link.pvlMask |= pvlOptFWD; - } else { - errlogPrintf("%s.FLNK is a Channel Access Link " - " but does not link to a PROC field\n", - precord->name); - } - } - } - } + if (plink->type == PV_LINK) + dbInitLink(precord, plink, pdbFldDes->field_type); } pdevSup = dbDTYPtoDevSup(pdbRecordType, precord->dtyp); if (pdevSup) { From 4ef35a5f5c4276da990550895eabcfa527700339 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:48 -0400 Subject: [PATCH 26/81] Move recGblInitConstantLink() functionality into dbLink.c Using dbFastPutConvert for string to number conversion. --- src/ioc/db/dbLink.c | 32 ++++++++++++++++++++++---- src/ioc/db/dbLink.h | 9 ++++++-- src/ioc/db/recGbl.c | 55 +++++---------------------------------------- 3 files changed, 41 insertions(+), 55 deletions(-) diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index 870183136..72929f878 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -76,6 +76,19 @@ static void inherit_severity(const struct pv_link *ppv_link, dbCommon *pdest, /***************************** Constant Links *****************************/ +static long dbConstLoadLink(struct link *plink, short dbrType, void *pbuffer) +{ + if (!plink->value.constantStr) + return S_db_badField; + + /* Constant strings are always numeric */ + if (dbrType== DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE) + dbrType = DBF_USHORT; + + return dbFastPutConvertRoutine[DBR_STRING][dbrType] + (plink->value.constantStr, pbuffer, NULL); +} + static long dbConstGetNelements(const struct link *plink, long *nelements) { *nelements = 0; @@ -358,7 +371,7 @@ static void dbDbScanFwdLink(struct link *plink) dbScanPassive(precord, paddr->precord); } -lset dbDb_lset = { dbDbInitLink, dbDbAddLink, dbDbRemoveLink, +lset dbDb_lset = { dbDbInitLink, dbDbAddLink, NULL, dbDbRemoveLink, dbDbIsLinkConnected, dbDbGetDBFtype, dbDbGetElements, dbDbGetValue, dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits, dbDbGetPrecision, dbDbGetUnits, dbDbGetAlarm, dbDbGetTimeStamp, @@ -404,7 +417,7 @@ void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType) recGblTSELwasModified(plink); if (!(plink->value.pv_link.pvlMask & (pvlOptCA | pvlOptCP | pvlOptCPP))) { - /* Make it a DB link if possible */ + /* Can we make it a DB link? */ if (!dbDbAddLink(plink, dbfType)) return; } @@ -422,6 +435,15 @@ void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType) } } +long dbLoadLink(struct link *plink, short dbrType, void *pbuffer) +{ + switch (plink->type) { + case CONSTANT: + return dbConstLoadLink(plink, dbrType, pbuffer); + } + return S_db_notFound; +} + void dbRemoveLink(struct link *plink) { switch (plink->type) { @@ -497,7 +519,8 @@ long dbGetLinkValue(struct link *plink, short dbrType, void *pbuffer, status = dbCaGetLink(plink, dbrType, pbuffer, &stat, &sevr, pnRequest); break; default: - cantProceed("dbGetLinkValue: Illegal link type"); + cantProceed("dbGetLinkValue: Illegal link type %d\n", plink->type); + status = -1; } if (status) { recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM); @@ -599,7 +622,8 @@ long dbPutLinkValue(struct link *plink, short dbrType, const void *pbuffer, status = dbCaPutLink(plink, dbrType, pbuffer, nRequest); break; default: - cantProceed("dbPutLinkValue: Illegal link type %d", plink->type); + cantProceed("dbPutLinkValue: Illegal link type %d\n", plink->type); + status = -1; } if (status) { struct dbCommon *precord = plink->value.pv_link.precord; diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h index 0f2e14da8..55aa0098f 100644 --- a/src/ioc/db/dbLink.h +++ b/src/ioc/db/dbLink.h @@ -22,6 +22,7 @@ typedef struct lset { long (*initLink)(struct link *plink, short dbfType); long (*addLink)(struct link *plink, short dbfType); + long (*loadLink)(struct link *plink, short dbrType, void *pbuffer); void (*removeLink)(struct link *plink); int (*isLinkConnected)(const struct link *plink); int (*getDBFtype)(const struct link *plink); @@ -58,8 +59,12 @@ typedef struct lset { #define dbGetSevr(PLINK, PSEVERITY) \ dbGetAlarm((PLINK), NULL, (PSEVERITY)); -epicsShareFunc void dbInitLink(struct dbCommon *precord, struct link *plink, short dbfType); -epicsShareFunc void dbAddLink(struct dbCommon *precord, struct link *plink, short dbfType); +epicsShareFunc void dbInitLink(struct dbCommon *precord, struct link *plink, + short dbfType); +epicsShareFunc void dbAddLink(struct dbCommon *precord, struct link *plink, + short dbfType); +epicsShareFunc long dbLoadLink(struct link *plink, short dbrType, + void *pbuffer); epicsShareFunc void dbRemoveLink(struct link *plink); epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements); epicsShareFunc int dbIsLinkConnected(const struct link *plink); diff --git a/src/ioc/db/recGbl.c b/src/ioc/db/recGbl.c index 828126e63..2ccac3a07 100644 --- a/src/ioc/db/recGbl.c +++ b/src/ioc/db/recGbl.c @@ -167,60 +167,17 @@ void epicsShareAPI recGblGetControlDouble( return; } - + int epicsShareAPI recGblInitConstantLink( struct link *plink,short dbftype,void *pdest) { - if(plink->type != CONSTANT) return(FALSE); - if(!plink->value.constantStr) return(FALSE); - switch(dbftype) { - case DBF_STRING: - strcpy((char *)pdest,plink->value.constantStr); - break; - case DBF_CHAR : { - epicsInt16 value; - epicsInt8 *pvalue = (epicsInt8 *)pdest; + long status = dbLoadLink(plink, dbftype, pdest); - sscanf(plink->value.constantStr,"%hi",&value); - *pvalue = value; - } - break; - case DBF_UCHAR : { - epicsUInt16 value; - epicsUInt8 *pvalue = (epicsUInt8 *)pdest; - - sscanf(plink->value.constantStr,"%hu",&value); - *pvalue = value; - } - break; - case DBF_SHORT : - sscanf(plink->value.constantStr,"%hi",(epicsInt16 *)pdest); - break; - case DBF_USHORT : - case DBF_ENUM : - case DBF_MENU : - case DBF_DEVICE : - sscanf(plink->value.constantStr,"%hu",(epicsUInt16 *)pdest); - break; - case DBF_LONG : - *(epicsInt32 *)pdest = strtol(plink->value.constantStr, NULL, 0); - break; - case DBF_ULONG : - *(epicsUInt32 *)pdest = strtoul(plink->value.constantStr, NULL, 10); - break; - case DBF_FLOAT : - epicsScanFloat(plink->value.constantStr, (epicsFloat32 *)pdest); - break; - case DBF_DOUBLE : - epicsScanDouble(plink->value.constantStr, (epicsFloat64 *)pdest); - break; - default: - epicsPrintf("Error in recGblInitConstantLink: Illegal DBF type\n"); - return(FALSE); - } - return(TRUE); + if (status) + return FALSE; + return TRUE; } - + unsigned short epicsShareAPI recGblResetAlarms(void *precord) { dbCommon *pdbc = precord; From a2fadc15a7da717a00c422edc4603f013a03fce2 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Fri, 27 Apr 2012 13:21:48 -0400 Subject: [PATCH 27/81] Must clear link type before calling dbLockSetSplit() --- src/ioc/db/dbLink.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ioc/db/dbLink.c b/src/ioc/db/dbLink.c index 72929f878..f4f442ba8 100644 --- a/src/ioc/db/dbLink.c +++ b/src/ioc/db/dbLink.c @@ -142,6 +142,7 @@ static void dbDbRemoveLink(struct link *plink) plink->value.pv_link.pvt = 0; plink->value.pv_link.getCvt = 0; plink->value.pv_link.lastGetdbrType = 0; + plink->type = PV_LINK; dbLockSetSplit(plink->value.pv_link.precord); } From f65ec61dc3e5325446f4edd31a5ebc42c1dbfb9d Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:49 -0400 Subject: [PATCH 28/81] Fix: Call dbChannelDelete() when client connection is lost. [rsrv/caservertask.c] --- src/ioc/db/dbChannel.c | 8 ++++---- src/ioc/db/dbChannel.h | 8 ++++---- src/ioc/rsrv/caservertask.c | 1 + 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index f87e0359b..8698b5d03 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -1,9 +1,9 @@ /*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne - * National Laboratory. - * EPICS BASE is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - \*************************************************************************/ +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ #include #include diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index f7d7c651a..34c9d073b 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -1,9 +1,9 @@ /*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne - * National Laboratory. - * EPICS BASE is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - \*************************************************************************/ +* National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ #ifndef INC_dbChannel_H #define INC_dbChannel_H diff --git a/src/ioc/rsrv/caservertask.c b/src/ioc/rsrv/caservertask.c index 1d0b5f3e7..ce7fef94e 100644 --- a/src/ioc/rsrv/caservertask.c +++ b/src/ioc/rsrv/caservertask.c @@ -654,6 +654,7 @@ static void destroyAllChannels ( errPrintf ( status, __FILE__, __LINE__, "asRemoveClient" ); } + dbChannelDelete(pciu->dbch); freeListFree ( rsrvChanFreeList, pciu ); } } From d22b719e3230c98ade0c265e277dbd00cc7583ec Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:50 -0400 Subject: [PATCH 29/81] Changed the structure for channel filters and channel filter plugins. - Made chFilterPlugin public (moved from dbChannel.c to dbChannel.h) - Made the interface jump table chFilterIf part of the chFilterPlugin (i.e. once per plugin) instead of chFilter (once per instance) - Added a pointer from chFilter (instance) to chFilterPlugin (plugin) - Added a private user pointer in chFilterPlugin - Changed dbFindFilter() to return a chFilterPlugin* (instead of the jump table) - Changed dbRegisterFilter() to accept a user private pointer as 3rd arg --- src/ioc/db/dbChannel.c | 59 +++++++++++++++------------------ src/ioc/db/dbChannel.h | 16 ++++++--- src/ioc/db/test/dbChannelTest.c | 4 +-- 3 files changed, 40 insertions(+), 39 deletions(-) diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index 8698b5d03..ee8ac34f6 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -29,12 +29,6 @@ typedef struct parseContext { int depth; } parseContext; -typedef struct filterPlugin { - ELLNODE node; - const char *name; - const chFilterIf *fif; -} filterPlugin; - #define CALLIF(rtn) !rtn ? parse_stop : rtn static void chf_value(parseContext *parser, parse_result *presult) @@ -45,7 +39,7 @@ static void chf_value(parseContext *parser, parse_result *presult) return; parser->filter = NULL; - if (filter->fif->parse_end(filter) == parse_continue) { + if (filter->plug->fif->parse_end(filter) == parse_continue) { ellAdd(&parser->chan->filters, &filter->node); } else { free(filter); // FIXME: Use free-list @@ -60,7 +54,7 @@ static int chf_null(void * ctx) parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_null)(filter ); + result = CALLIF(filter->plug->fif->parse_null)(filter ); chf_value(parser, &result); return result; } @@ -72,7 +66,7 @@ static int chf_boolean(void * ctx, int boolVal) parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_boolean)(filter , boolVal); + result = CALLIF(filter->plug->fif->parse_boolean)(filter , boolVal); chf_value(parser, &result); return result; } @@ -84,7 +78,7 @@ static int chf_integer(void * ctx, long integerVal) parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_integer)(filter , integerVal); + result = CALLIF(filter->plug->fif->parse_integer)(filter , integerVal); chf_value(parser, &result); return result; } @@ -96,7 +90,7 @@ static int chf_double(void * ctx, double doubleVal) parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_double)(filter , doubleVal); + result = CALLIF(filter->plug->fif->parse_double)(filter , doubleVal); chf_value(parser, &result); return result; } @@ -109,7 +103,7 @@ static int chf_string(void * ctx, const unsigned char * stringVal, parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_string)(filter , (const char *) stringVal, stringLen); + result = CALLIF(filter->plug->fif->parse_string)(filter , (const char *) stringVal, stringLen); chf_value(parser, &result); return result; } @@ -125,7 +119,7 @@ static int chf_start_map(void * ctx) } ++parser->depth; - return CALLIF(filter->fif->parse_start_map)(filter ); + return CALLIF(filter->plug->fif->parse_start_map)(filter ); } static int chf_map_key(void * ctx, const unsigned char * key, @@ -133,17 +127,17 @@ static int chf_map_key(void * ctx, const unsigned char * key, { parseContext *parser = (parseContext *) ctx; chFilter *filter = parser->filter; - const chFilterIf *fif; + const chFilterPlugin *plug; parse_result result; if (filter) { assert(parser->depth > 0); - return CALLIF(filter->fif->parse_map_key)(filter , (const char *) key, stringLen); + return CALLIF(filter->plug->fif->parse_map_key)(filter , (const char *) key, stringLen); } assert(parser->depth == 0); - fif = dbFindFilter((const char *) key, stringLen); - if (!fif) { + plug = dbFindFilter((const char *) key, stringLen); + if (!plug) { printf("dbChannelCreate: Channel filter '%.*s' not found\n", stringLen, key); return parse_stop; } @@ -151,10 +145,10 @@ static int chf_map_key(void * ctx, const unsigned char * key, /* FIXME: Use a free-list */ filter = (chFilter *) callocMustSucceed(1, sizeof(*filter), "Creating dbChannel filter"); filter->chan = parser->chan; - filter->fif = fif; + filter->plug = plug; filter->puser = NULL; - result = fif->parse_start(filter); + result = plug->fif->parse_start(filter); if (result == parse_continue) { parser->filter = filter; } else { @@ -175,7 +169,7 @@ static int chf_end_map(void * ctx) } assert(parser->depth > 0); - result = CALLIF(filter->fif->parse_end_map)(filter ); + result = CALLIF(filter->plug->fif->parse_end_map)(filter ); --parser->depth; chf_value(parser, &result); @@ -189,7 +183,7 @@ static int chf_start_array(void * ctx) assert(filter); ++parser->depth; - return CALLIF(filter->fif->parse_start_array)(filter ); + return CALLIF(filter->plug->fif->parse_start_array)(filter ); } static int chf_end_array(void * ctx) @@ -199,7 +193,7 @@ static int chf_end_array(void * ctx) parse_result result; assert(filter); - result = CALLIF(filter->fif->parse_end_array)(filter ); + result = CALLIF(filter->plug->fif->parse_end_array)(filter ); --parser->depth; chf_value(parser, &result); return result; @@ -266,7 +260,7 @@ static long chf_parse(dbChannel *chan, const char **pjson) if (parser.filter) { assert(status); - parser.filter->fif->parse_abort(parser.filter); + parser.filter->plug->fif->parse_abort(parser.filter); free(parser.filter); /* FIXME: free-list */ } yajl_free(yh); @@ -422,7 +416,7 @@ long dbChannelOpen(dbChannel *chan) filter = (chFilter *) ellFirst(&chan->filters); while (filter) { - status = filter->fif->channel_open(filter); + status = filter->plug->fif->channel_open(filter); if (status) break; filter = (chFilter *) ellNext(&filter->node); @@ -537,7 +531,7 @@ void dbChannelFilterShow(dbChannel *chan, const char *intro, int level) { chFilter *filter = (chFilter *) ellFirst(&chan->filters); while (filter) { - filter->fif->channel_report(filter, intro, level); + filter->plug->fif->channel_report(filter, intro, level); filter = (chFilter *) ellNext(&filter->node); } } @@ -548,7 +542,7 @@ void dbChannelDelete(dbChannel *chan) /* Close filters in reverse order */ while ((filter = (chFilter *) ellPop(&chan->filters))) { - filter->fif->channel_close(filter); + filter->plug->fif->channel_close(filter); free(filter); } free((char *) chan->name); // FIXME: Use free-list @@ -558,10 +552,10 @@ void dbChannelDelete(dbChannel *chan) /* FIXME: Do these belong in a different file? */ -void dbRegisterFilter(const char *name, const chFilterIf *fif) +void dbRegisterFilter(const char *name, const chFilterIf *fif, void *puser) { GPHENTRY *pgph; - filterPlugin *pfilt; + chFilterPlugin *pfilt; if (!pdbbase) { printf("dbRegisterFilter: pdbbase not set!\n"); @@ -572,9 +566,10 @@ void dbRegisterFilter(const char *name, const chFilterIf *fif) if (pgph) return; - pfilt = dbCalloc(1, sizeof(filterPlugin)); + pfilt = dbCalloc(1, sizeof(chFilterPlugin)); pfilt->name = strdup(name); pfilt->fif = fif; + pfilt->puser = puser; ellAdd(&pdbbase->filterList, &pfilt->node); pgph = gphAdd(pdbbase->pgpHash, pfilt->name, &pdbbase->filterList); @@ -587,14 +582,12 @@ void dbRegisterFilter(const char *name, const chFilterIf *fif) pgph->userPvt = pfilt; } -const chFilterIf * dbFindFilter(const char *name, size_t len) +const chFilterPlugin * dbFindFilter(const char *name, size_t len) { GPHENTRY *pgph = gphFindParse(pdbbase->pgpHash, name, len, &pdbbase->filterList); - filterPlugin *pfilt; if (!pgph) return NULL; - pfilt = (filterPlugin *) pgph->userPvt; - return pfilt->fif; + return (chFilterPlugin *) pgph->userPvt; } diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index 34c9d073b..1539ee038 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -73,11 +73,19 @@ typedef struct chFilterIf { void (* channel_close)(chFilter *filter); } chFilterIf; -/* A chFilter holds instance data for a single filter */ +/* A chFilterPlugin holds data for a filter plugin */ +typedef struct chFilterPlugin { + ELLNODE node; + const char *name; + const chFilterIf *fif; + void *puser; +} chFilterPlugin; + +/* A chFilter holds data for a single filter instance */ struct chFilter { ELLNODE node; dbChannel *chan; - const chFilterIf *fif; + const chFilterPlugin *plug; void *puser; }; @@ -110,8 +118,8 @@ epicsShareFunc void dbChannelFilterShow(dbChannel *chan, const char *intro, int level); epicsShareFunc void dbChannelDelete(dbChannel *chan); -epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif); -epicsShareFunc const chFilterIf * dbFindFilter(const char *key, size_t len); +epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif, void *puser); +epicsShareFunc const chFilterPlugin * dbFindFilter(const char *key, size_t len); #ifdef __cplusplus } diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index af3b25ee5..68195a288 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -169,7 +169,7 @@ MAIN(dbChannelTest) testOk(!dbChannelCreate("x.{not-json}"), "Create, bad JSON"); testOk(!dbChannelCreate("x.{\"none\":null}"), "Create, bad filter"); - dbRegisterFilter("any", &testIf); + dbRegisterFilter("any", &testIf, NULL); /* Parser event rejection by filter */ e = e_start; @@ -190,7 +190,7 @@ MAIN(dbChannelTest) e = e_close; if (pch) dbChannelDelete(pch); - dbRegisterFilter("scalar", &testIf); + dbRegisterFilter("scalar", &testIf, NULL); e = e_start | e_null | e_end; testOk1(!!(pch = dbChannelCreate("x.{\"scalar\":null}"))); From de134ea72db3192c3bd7d22667a097fe912653fc Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:21:51 -0400 Subject: [PATCH 30/81] Added chfPlugin, a simple interface for channel filter plugins. - chfPlugin.h contains the simple jump table and doxygen documentation - chfPlugin.c is a wrapper library around the yajl callbacks that parses any client-supplied configuration into the user's structure - test/chfPluginTest.c tests the library and the data conversion --- src/ioc/db/Makefile | 2 + src/ioc/db/chfPlugin.c | 613 ++++++++++++++++++++++++++++++++ src/ioc/db/chfPlugin.h | 223 ++++++++++++ src/ioc/db/test/Makefile | 5 + src/ioc/db/test/chfPluginTest.c | 388 ++++++++++++++++++++ 5 files changed, 1231 insertions(+) create mode 100644 src/ioc/db/chfPlugin.c create mode 100644 src/ioc/db/chfPlugin.h create mode 100644 src/ioc/db/test/chfPluginTest.c diff --git a/src/ioc/db/Makefile b/src/ioc/db/Makefile index 132861fda..3a47dae3e 100644 --- a/src/ioc/db/Makefile +++ b/src/ioc/db/Makefile @@ -32,6 +32,7 @@ INC += db_field_log.h INC += initHooks.h INC += recGbl.h INC += dbIocRegister.h +INC += chfPlugin.h INC += db_access_routines.h INC += db_convert.h @@ -78,4 +79,5 @@ dbCore_SRCS += dbPutNotifyBlocker.cpp dbCore_SRCS += dbContextReadNotifyCache.cpp dbCore_SRCS += templateInstances.cpp dbCore_SRCS += dbIocRegister.c +dbCore_SRCS += chfPlugin.c diff --git a/src/ioc/db/chfPlugin.c b/src/ioc/db/chfPlugin.c new file mode 100644 index 000000000..107c4a622 --- /dev/null +++ b/src/ioc/db/chfPlugin.c @@ -0,0 +1,613 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* Based on the linkoptions utility by Michael Davidsaver (BNL) */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define epicsExportSharedSymbols +#include "chfPlugin.h" + +#ifndef HUGE_VALF +# define HUGE_VALF HUGE_VAL +#endif +#ifndef HUGE_VALL +# define HUGE_VALL (-(HUGE_VAL)) +#endif + +/* + * Data for a chfPlugin + */ +typedef struct chfPluginCtx { + const chfPluginArgDef *opts; + size_t nopts; + const chfPluginIf *pif; +} chfPluginCtx; + +/* + * Parser state data for a chfFilter (chfPlugin instance) + */ +typedef struct chfFilterCtx { + const chfPluginCtx *plugin; + epicsUInt32 *found; + void *puser; + epicsInt16 nextParam; +} chfFilterCtx; + +/* Data types we get from the parser */ +typedef enum chfPluginType { + chfPluginTypeBool, + chfPluginTypeInt, + chfPluginTypeDouble, + chfPluginTypeString +} chfPluginType; + +/* + * Convert the (long) integer value 'val' to the type named in 'opt->optType' + * and store the result at 'user + opt->offset'. + */ +static +int +store_integer_value(const chfPluginArgDef *opt, void *user, long val) +{ + long *ival; + int *eval; + const chfPluginEnumType *emap; + double *dval; + char *sval; + char buff[22]; /* 2^64 = 1.8e+19, so 20 digits plus sign max */ + +/* printf("Got an integer for %s (type %d): %ld\n", opt->name, opt->optType, val); */ + + if (!opt->convert && opt->optType != chfPluginArgInt32) { + return -1; + } + + switch (opt->optType) { + case chfPluginArgInt32: + ival = (long*) ((char*)user + opt->offset); + *ival = val; + break; + case chfPluginArgBoolean: + eval = (int*) ((char*)user + opt->offset); + *eval = !!val; + break; + case chfPluginArgDouble: + dval = (double*) ((char*)user + opt->offset); + *dval = (double) val; + break; + case chfPluginArgString: + sval = ((char*)user + opt->offset); + sprintf(buff, "%ld", val); + if (strlen(buff) > opt->size-1) { + return -1; + } + strncpy(sval, buff, opt->size-1); + sval[opt->size-1]='\0'; + break; + case chfPluginArgEnum: + eval = (int*) ((char*)user + opt->offset); + for (emap = opt->enums; emap && emap->name; emap++) { + if (val == emap->value) { + *eval = val; + break; + } + } + if (!emap || !emap->name) { + return -1; + } + break; + case chfPluginArgInvalid: + return -1; + } + return 0; +} + +/* + * Convert the (int) boolean value 'val' to the type named in 'opt->optType' + * and store the result at 'user + opt->offset'. + */ +static +int +store_boolean_value(const chfPluginArgDef *opt, void *user, int val) +{ + long *ival; + int *eval; + double *dval; + char *sval; + +/* printf("Got a boolean for %s (type %d): %d\n", opt->name, opt->optType, val); */ + + if (!opt->convert && opt->optType != chfPluginArgBoolean) { + return -1; + } + + switch (opt->optType) { + case chfPluginArgInt32: + ival = (long*) ((char*)user + opt->offset); + *ival = val; + break; + case chfPluginArgBoolean: + eval = (int*) ((char*)user + opt->offset); + *eval = val; + break; + case chfPluginArgDouble: + dval = (double*) ((char*)user + opt->offset); + *dval = val ? 1. : 0.; + break; + case chfPluginArgString: + sval = ((char*)user + opt->offset); + if ((val ? 4 : 5) > opt->size-1) { + return -1; + } + strncpy(sval, (val ? "true" : "false"), opt->size-1); + sval[opt->size-1]='\0'; + break; + case chfPluginArgEnum: + case chfPluginArgInvalid: + return -1; + } + return 0; +} + +/* + * Convert the double value 'val' to the type named in 'opt->optType' + * and store the result at 'user + opt->offset'. + */ +static +int +store_double_value(const chfPluginArgDef *opt, void *user, double val) +{ + long *ival; + int *eval; + double *dval; + char *sval; + int i; + +/* + printf("Got a double for %s (type %d, convert: %s): %g\n", + opt->name, opt->optType, opt->convert?"yes":"no", val); +*/ + if (!opt->convert && opt->optType != chfPluginArgDouble) { + return -1; + } + + switch (opt->optType) { + case chfPluginArgInt32: + if (val < LONG_MIN || val > LONG_MAX) { + return -1; + } + ival = (long*) ((char*)user + opt->offset); + *ival = (long) val; + break; + case chfPluginArgBoolean: + eval = (int*) ((char*)user + opt->offset); + *eval = (val != 0.) ? 1 : 0; + break; + case chfPluginArgDouble: + dval = (double*) ((char*)user + opt->offset); + *dval = val; + break; + case chfPluginArgString: + sval = ((char*)user + opt->offset); + if (opt->size <= 8) { /* Play it safe: 3 exp + 2 sign + 'e' + '.' */ + return -1; + } + i = snprintf(sval, opt->size, "%.*g", opt->size - 7, val); + if (i >= opt->size) { + return -1; + } + break; + case chfPluginArgEnum: + case chfPluginArgInvalid: + return -1; + } + return 0; +} + +/* + * Convert the (char*) string value 'val' to the type named in 'opt->optType' + * and store the result at 'user + opt->offset'. + */ +static +int +store_string_value(const chfPluginArgDef *opt, void *user, const char *val, size_t len) +{ + long *ival; + int *eval; + const chfPluginEnumType *emap; + double *dval; + char *sval; + char *end; + int i; + +/* printf("Got a string for %s (type %d): %.*s\n", opt->name, opt->optType, len, val); */ + + if (!opt->convert && opt->optType != chfPluginArgString && opt->optType != chfPluginArgEnum) { + return -1; + } + + switch (opt->optType) { + case chfPluginArgInt32: + ival = (long*) ((char*)user + opt->offset); + *ival = strtol(val, &end, 0); + /* test for the myriad error conditions which strtol may use */ + if (*ival == LONG_MAX || *ival == LONG_MIN || end == val) { + return -1; + } + break; + case chfPluginArgBoolean: + eval = (int*) ((char*)user + opt->offset); + if (strncasecmp(val, "true", len) == 0) { + *eval = 1; + } else if (strncasecmp(val, "false", len) == 0) { + *eval = 0; + } else { + i = strtol(val, &end, 0); + if (i > INT_MAX || i < INT_MIN || end == val) { + return -1; + } + *eval = !!i; + } + break; + case chfPluginArgDouble: + dval = (double*) ((char*)user + opt->offset); + *dval = strtod(val, &end); + /* Indicates errors in the same manner as strtol */ + if (*dval==HUGE_VALF||*dval==HUGE_VALL||end==val ) + { + return -1; + } + break; + case chfPluginArgString: + i = opt->size-1 < len ? opt->size-1 : len; + sval = ((char*)user + opt->offset); + strncpy(sval, val, i); + sval[i] = '\0'; + break; + case chfPluginArgEnum: + eval = (int*) ((char*)user + opt->offset); + for (emap = opt->enums; emap && emap->name; emap++) { + if (strncmp(emap->name, val, len) == 0) { + *eval = emap->value; + break; + } + } + if( !emap || !emap->name ) { + return -1; + } + break; + case chfPluginArgInvalid: + return -1; + } + return 0; +} + +static void freeInstanceData(chfFilterCtx *fctx) +{ + free(fctx->found); /* FIXME: Use a free-list */ + free(fctx); /* FIXME: Use a free-list */ +} + +/* + * chFilterIf callbacks + */ + +/* First entry point when a new filter instance is created. + * All per-instance allocations happen here. + */ +static parse_result parse_start(chFilter *filter) +{ + chfPluginCtx *pctx = (chfPluginCtx*) filter->plug->puser; + chfFilterCtx *fctx; + + /* Filter context */ + /* FIXME: Use a free-list */ + fctx = calloc(1, sizeof(chfFilterCtx)); + if (!fctx) { + fprintf(stderr,"chfFilterCtx calloc failed\n"); + goto errfctx; + } + fctx->nextParam = -1; + + /* Bit array to find missing required keys */ + /* FIXME: Use a free-list */ + fctx->found = calloc( (pctx->nopts/32)+1, sizeof(epicsUInt32) ); + if (!fctx->found) { + fprintf(stderr,"chfConfigParseStart: bit array calloc failed\n"); + goto errbitarray; + } + + /* Call the plugin to allocate its structure, it returns NULL on error */ + if (pctx->pif->allocPvt) { + if ((fctx->puser = pctx->pif->allocPvt()) == NULL) + goto errplugin; + } + + filter->puser = (void*) fctx; + + return parse_continue; + + errplugin: + free(fctx->found); /* FIXME: Use a free-list */ + errbitarray: + free(fctx); /* FIXME: Use a free-list */ + errfctx: + return parse_stop; +} + +static void parse_abort(chFilter *filter) { + chfPluginCtx *pctx = (chfPluginCtx*) filter->plug->puser; + chfFilterCtx *fctx = (chfFilterCtx*) filter->puser; + + /* Call the plugin to tell it we're aborting */ + if (pctx->pif->parse_error) pctx->pif->parse_error(); + if (pctx->pif->freePvt) pctx->pif->freePvt(fctx->puser); + freeInstanceData(fctx); +} + +static parse_result parse_end(chFilter *filter) +{ + chfPluginCtx *pctx = (chfPluginCtx*) filter->plug->puser; + chfFilterCtx *fctx = (chfFilterCtx*) filter->puser; + const chfPluginArgDef *cur; + int i; + + for(cur = pctx->opts, i = 0; cur && cur->name; cur++, i++) { + if ( !(fctx->found[i/32] & (1 << (i%32))) && cur->required ) { + if (pctx->pif->parse_error) pctx->pif->parse_error(); + if (pctx->pif->freePvt) pctx->pif->freePvt(fctx->puser); + freeInstanceData(fctx); + return parse_stop; + } + } + + /* Call the plugin to tell it we're done */ + if (pctx->pif->parse_ok) { + if (pctx->pif->parse_ok(fctx->puser)) { + if (pctx->pif->freePvt) pctx->pif->freePvt(fctx->puser); + freeInstanceData(fctx); + return parse_stop; + } + } + + return parse_continue; +} + +static parse_result parse_boolean(chFilter *filter, int boolVal) +{ + const chfPluginArgDef *opts = ((chfPluginCtx*)filter->plug->puser)->opts; + chfFilterCtx *ctx = (chfFilterCtx*)filter->puser; + + if (ctx->nextParam < 0 || store_boolean_value(&opts[ctx->nextParam], ctx->puser, boolVal)) { + return parse_stop; + } else { + return parse_continue; + } +} + +static parse_result parse_integer(chFilter *filter, long integerVal) +{ + const chfPluginArgDef *opts = ((chfPluginCtx*)filter->plug->puser)->opts; + chfFilterCtx *ctx = (chfFilterCtx*)filter->puser; + + if (ctx->nextParam < 0 || store_integer_value(&opts[ctx->nextParam], ctx->puser, integerVal)) { + return parse_stop; + } else { + return parse_continue; + } +} + +static parse_result parse_double(chFilter *filter, double doubleVal) +{ + const chfPluginArgDef *opts = ((chfPluginCtx*)filter->plug->puser)->opts; + chfFilterCtx *ctx = (chfFilterCtx*)filter->puser; + + if (ctx->nextParam < 0 || store_double_value(&opts[ctx->nextParam], ctx->puser, doubleVal)) { + return parse_stop; + } else { + return parse_continue; + } +} + +static parse_result parse_string(chFilter *filter, const char *stringVal, size_t stringLen) +{ + const chfPluginArgDef *opts = ((chfPluginCtx*)filter->plug->puser)->opts; + chfFilterCtx *ctx = (chfFilterCtx*)filter->puser; + + if (ctx->nextParam < 0 || store_string_value(&opts[ctx->nextParam], ctx->puser, stringVal, stringLen)) { + return parse_stop; + } else { + return parse_continue; + } +} + +static parse_result parse_start_map(chFilter *filter) +{ + return parse_continue; +} + +static parse_result parse_map_key(chFilter *filter, const char *key, size_t stringLen) +{ + const chfPluginArgDef *cur; + const chfPluginArgDef *opts = ((chfPluginCtx*)filter->plug->puser)->opts; + chfFilterCtx *ctx = (chfFilterCtx*)filter->puser; + int i; + + ctx->nextParam = -1; + for(cur = opts, i = 0; cur && cur->name; cur++, i++) { + if (strncmp(key, cur->name, stringLen) == 0) { + ctx->nextParam = i; + break; + } + } + if (ctx->nextParam == -1) { + return parse_stop; + } + + ctx->found[i/32] |= 1<<(i%32); + return parse_continue; +} + +static parse_result parse_end_map(chFilter *filter) +{ + return parse_continue; +} + +static long channel_open(chFilter *filter) +{ + chfPluginCtx *pctx = (chfPluginCtx*) filter->plug->puser; + chfFilterCtx *fctx = (chfFilterCtx*) filter->puser; + + if (pctx->pif->channel_open) return pctx->pif->channel_open(filter->chan, fctx->puser); + else return 0; +} + +static void channel_report(chFilter *filter, const char *intro, int level) +{ + chfPluginCtx *pctx = (chfPluginCtx*) filter->plug->puser; + chfFilterCtx *fctx = (chfFilterCtx*) filter->puser; + + if (pctx->pif->channel_report) pctx->pif->channel_report(filter->chan, fctx->puser, intro, level); +} + +static void channel_close(chFilter *filter) +{ + chfPluginCtx *pctx = (chfPluginCtx*) filter->plug->puser; + chfFilterCtx *fctx = (chfFilterCtx*) filter->puser; + + if (pctx->pif->channel_close) pctx->pif->channel_close(filter->chan, fctx->puser); + if (pctx->pif->freePvt) pctx->pif->freePvt(fctx->puser); + free(fctx->found); /* FIXME: Use a free-list */ + free(fctx); /* FIXME: Use a free-list */ +} + +/* + * chFilterIf for the wrapper + * we just support a simple one-level map, and no arrays + */ +static chFilterIf wrapper_fif = { + parse_start, + parse_abort, + parse_end, + + NULL, /* parse_null, */ + parse_boolean, + parse_integer, + parse_double, + parse_string, + + parse_start_map, + parse_map_key, + parse_end_map, + + NULL, /* parse_start_array, */ + NULL, /* parse_end_array, */ + + channel_open, + channel_report, + channel_close +}; + +epicsShareFunc +const char* +epicsShareAPI +chfPluginEnumString(const chfPluginEnumType *emap, int i, const char* def) +{ + for(; emap && emap->name; emap++) { + if ( i == emap->value ) { + return emap->name; + } + } + return def; +} + +epicsShareFunc +int +epicsShareAPI +chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef* opts) +{ + chfPluginCtx *pctx; + size_t i; + const chfPluginArgDef *cur; + + /* Check and count options */ + for (i = 0, cur = opts; cur && cur->name; i++, cur++) { + switch(cur->optType) { + case chfPluginArgInt32: + if (cur->size < sizeof(long)) { + errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for long (%lu)\n", + key, cur->size, cur->name, + (unsigned long) sizeof(long)); + return -1; + } + break; + case chfPluginArgBoolean: + if (cur->size < 1) { + errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for boolean (%lu)\n", + key, cur->size, cur->name, + (unsigned long) sizeof(char)); + return -1; + } + break; + case chfPluginArgDouble: + if (cur->size < sizeof(double)) { + errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for double (%lu)\n", + key, cur->size, cur->name, + (unsigned long) sizeof(double)); + return -1; + } + break; + case chfPluginArgString: + if (cur->size < sizeof(char*)) { + /* Catch if someone has given us a char* instead of a char[] + * Also means that char buffers must be >=4. + */ + errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for string (>= %lu)\n", + key, cur->size, cur->name, + (unsigned long) sizeof(char*)); + return -1; + } + break; + case chfPluginArgEnum: + if (cur->size < sizeof(int)) { + errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for enum (%lu)\n", + key, cur->size, cur->name, + (unsigned long) sizeof(int)); + return -1; + } + break; + case chfPluginArgInvalid: + errlogPrintf("Plugin %s: storage type for %s is not defined\n", + key, cur->name); + return -1; + break; + } + } + + /* Plugin context */ + pctx = dbCalloc(1, sizeof(chfPluginCtx)); + pctx->pif = pif; + pctx->opts = opts; + pctx->nopts = i; + + dbRegisterFilter(key, &wrapper_fif, pctx); + + return 0; +} diff --git a/src/ioc/db/chfPlugin.h b/src/ioc/db/chfPlugin.h new file mode 100644 index 000000000..43669c4a5 --- /dev/null +++ b/src/ioc/db/chfPlugin.h @@ -0,0 +1,223 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef CHFPLUGIN_H +#define CHFPLUGIN_H + +#include +#include +#include +#include + +/* Based on the linkoptions utility by Michael Davidsaver (BNL) */ + +/** @file chfPlugin.h + * @brief Channel filter simplified plugins. + * + * Utility layer to allow an easier (reduced) interface for + * channel filter plugins. + * + * Parsing the configuration arguments of a channel filter plugin + * is done according to an argument description table provided by the plugin. + * The parser stores the results directly into a user supplied structure + * after appropriate type conversion. + * + * To specify the arguments, a chfPluginArgDef table must be defined + * for the user structure. This table has to be specified when the plugin registers. + * + * The plugin is responsible to register an init function using + * epicsExportRegistrar() and the accompanying registrar() directive in the dbd, + * and call chfPluginRegister() from within the init function. + * + * For example: + * + * typedef struct myStruct { + * ... other stuff + * epicsUInt32 ival; + * double dval; + * epicsUInt32 ival2; + * int enumval; + * char strval[20]; + * } myStruct; + * + * static const + * chfPluginEnumType colorEnum[] = { {"Red",1}, {"Green",2}, {"Blue",3}, {NULL,0} }; + * + * static const + * chfPluginDef myStructDef[] = { + * chfInt32 (myStruct, ival, "Integer" , 0, 0), + * chfInt32 (myStruct, ival2, "Second" , 1, 0), + * chfDouble(myStruct, dval, "Double" , 1, 0), + * chfString(myStruct, strval , "String" , 1, 0), + * chfEnum (myStruct, enumval, "Color" , 1, 0, colorEnum), + * chfPluginEnd + * }; + * + * Note: The 4th argument specifies the parameter to be required (1) or optional (0), + * the 5th whether converting to the required type is allowed (1), or + * type mismatches are an error (0). + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Channel filter simplified plugin interface. + * + * The routines in this structure must be implemented by each filter plugin. + */ +typedef struct chfPluginIf { + + /* Memory management */ + /** @brief Allocate private resources. + * + * Called before parsing starts. + * The plugin should allocate its per-instance structures, + * returning a pointer to them or NULL requesting an abort of the operation. + * + * allocPvt may be set to NULL, if no resource allocation is needed. + * + * @return Pointer to private structure, NULL if operation is to be aborted. + */ + void * (* allocPvt) (void); + + /** @brief Free private resources. + * + * Called as part of abort or shutdown. + * The plugin should release any resources allocated for this filter; + * no further calls through this interface will be made. + * + * freePvt may be set to NULL, if no resources need to be released. + * + * @param pvt Pointer to private structure. + */ + void (* freePvt) (void *pvt); + + /* Parameter parsing results */ + /** @brief A parsing error occurred. + * + * Called after parsing failed with an error. + */ + void (* parse_error) (void); + + /** @brief Configuration has been parsed successfully. + * + * Called after parsing has finished ok. + * The plugin may check the validity of the parsed data, + * returning -1 to request an abort of the operation. + * + * @param pvt Pointer to private structure. + * @return 0 for success, -1 if operation is to be aborted. + */ + int (* parse_ok) (void *pvt); + + /* Channel operations */ + /** @brief Open channel. + * + * Called as part of the channel connection setup. + * @param chan dbChannel for which the connection is being made. + * @param pvt Pointer to private structure. + * @return 0 for success, -1 if operation is to be aborted. + */ + long (* channel_open) (dbChannel *chan, void *pvt); + + /** @brief Channel report request. + * + * Called as part of show... routines. + * @param chan dbChannel for which the report is requested. + * @param pvt Pointer to private structure. + * @param intro Line header string to be printed at the beginning of each line. + * @param level Interest level. + */ + void (* channel_report) (dbChannel *chan, void *pvt, const char *intro, int level); + + /* FIXME: More filter routines here ... */ + + /** @brief Channel close request. + * + * Called as part of connection shutdown. + * @param chan dbChannel for which the connection is being shut down. + * @param pvt Pointer to private structure. + */ + void (* channel_close) (dbChannel *chan, void *pvt); + +} chfPluginIf; + +typedef enum chfPluginArg { + chfPluginArgInvalid=0, + chfPluginArgBoolean, + chfPluginArgInt32, + chfPluginArgDouble, + chfPluginArgString, + chfPluginArgEnum +} chfPluginArg; + +typedef struct chfPluginEnumType { + const char *name; + const int value; +} chfPluginEnumType; + +typedef struct chfPluginArgDef { + const char * name; + chfPluginArg optType; + int required:1; + int convert:1; + epicsUInt32 offset; + epicsUInt32 size; + const chfPluginEnumType *enums; +} chfPluginArgDef; + +#define chfInt32(Struct, Member, Name, Req, Conv) \ +{Name, chfPluginArgInt32, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} + +#define chfBoolean(Struct, Member, Name, Req, Conv) \ +{Name, chfPluginArgBoolean, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} + +#define chfDouble(Struct, Member, Name, Req, Conv) \ +{Name, chfPluginArgDouble, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} + +#define chfString(Struct, Member, Name, Req, Conv) \ +{Name, chfPluginArgString, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), NULL} + +#define chfEnum(Struct, Member, Name, Req, Conv, Enums) \ +{Name, chfPluginArgEnum, Req, Conv, OFFSET(Struct, Member), sizeof( ((Struct*)0)->Member ), Enums} + +#define chfPluginArgEnd {0} + +/* Extra output when parsing and converting */ +#define CHFPLUGINDEBUG 1 + +/** @brief Return the string associated with Enum index 'i'. + * + * @param Enums A null-terminated array of string/integer pairs. + * @param i An Enum index. + * @param def String to be returned when 'i' isn't a valid Enum index. + * @return The string associated with 'i'. + */ +epicsShareFunc +const char* +epicsShareAPI +chfPluginEnumString(const chfPluginEnumType *Enums, int i, const char* def); + +/** @brief Register a plugin. + * + * @param key The plugin name key that clients will use. + * @param pif Pointer to the plugin's interface. + * @param opts Pointer to the configuration argument description table. + */ +epicsShareFunc +int +epicsShareAPI +chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef* opts); + +#ifdef __cplusplus +} +#endif + +#endif // CHFPLUGIN_H diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index a1bc70d5e..bed566e824 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -25,6 +25,11 @@ dbChannelTest_SRCS += dbChannelTest.c OBJS_IOC_vxWorks += dbChannelTest TESTS += dbChannelTest +TESTPROD_HOST += chfPluginTest +chfPluginTest_SRCS += chfPluginTest.c +OBJS_IOC_vxWorks += chfPluginTest +TESTS += chfPluginTest + TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c new file mode 100644 index 000000000..44609aee6 --- /dev/null +++ b/src/ioc/db/test/chfPluginTest.c @@ -0,0 +1,388 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#include + +#include "chfPlugin.h" +#include "dbStaticLib.h" +#include "dbAccessDefs.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#define PATTERN 0x55555555 + +/* Expected call bit definitions */ +#define e_alloc 0x00000001 +#define e_free 0x00000002 +#define e_error 0x00000004 +#define e_ok 0x00000008 +#define e_open 0x00000010 +#define e_report 0x00000020 +#define e_close 0x00000040 + +unsigned int e, c; + +#define e_any (e_alloc | e_free | e_error | e_ok | e_open | e_report | e_close) + +typedef struct myStruct { + int sent1; + char flag; + int sent2; + epicsUInt32 ival; + int sent3; + double dval; + int sent4; + int enumval; + int sent5; + char str[20]; + int sent6; + char c; + char c1[2]; +} myStruct; + +static const +chfPluginEnumType colorEnum[] = { {"R", 1}, {"G", 2}, {"B", 4}, {NULL,0} }; + +static const +chfPluginArgDef strictOpts[] = { + chfInt32 (myStruct, ival, "i" , 1, 0), + chfBoolean(myStruct, flag, "f" , 1, 0), + chfDouble (myStruct, dval, "d" , 1, 0), + chfString (myStruct, str, "s" , 1, 0), + chfEnum (myStruct, enumval, "c" , 1, 0, colorEnum), + chfPluginArgEnd +}; + +static const +chfPluginArgDef noconvOpts[] = { + chfInt32 (myStruct, ival, "i" , 0, 0), + chfBoolean(myStruct, flag, "f" , 0, 0), + chfDouble (myStruct, dval, "d" , 0, 0), + chfString (myStruct, str, "s" , 0, 0), + chfEnum (myStruct, enumval, "c" , 0, 0, colorEnum), + chfPluginArgEnd +}; + +static const +chfPluginArgDef sloppyOpts[] = { + chfInt32 (myStruct, ival, "i" , 0, 1), + chfBoolean(myStruct, flag, "f" , 0, 1), + chfDouble (myStruct, dval, "d" , 0, 1), + chfString (myStruct, str, "s" , 0, 1), + chfEnum (myStruct, enumval, "c" , 0, 1, colorEnum), + chfPluginArgEnd +}; + +/* Options defs with not enough room provided */ +static const +chfPluginArgDef brokenOpts1[] = { + chfInt32 (myStruct, c, "i" , 0, 1), + chfPluginArgEnd +}; + +static const +chfPluginArgDef brokenOpts2[] = { + chfDouble(myStruct, c, "d" , 0, 1), + chfPluginArgEnd +}; + +static const +chfPluginArgDef brokenOpts3[] = { + chfString(myStruct, c1, "s" , 0, 1), + chfPluginArgEnd +}; + +static const +chfPluginArgDef brokenOpts4[] = { + chfEnum (myStruct, c, "c" , 0, 1, colorEnum), + chfPluginArgEnd +}; + +int p_ok_return = 0; +int c_open_return = 0; +void *puser; + +static void clearStruct(void *p) { + myStruct *my = (myStruct*) p; + + if (!my) return; + memset(my, 0, sizeof(myStruct)); + my->sent1 = my->sent2 = my->sent3 = my->sent4 = my->sent5 = my->sent6 = PATTERN; + my->ival = 12; + my->flag = 1; + my->dval = 1.234e5; + strcpy(my->str, "hello"); + my->enumval = 4; +} + +static void * allocPvt(void) +{ + testOk(e & e_alloc, "allocPvt called"); + c |= e_alloc; + + myStruct *my = puser = (myStruct*) calloc(1, sizeof(myStruct)); + clearStruct (my); + return my; +} + +static void freePvt(void *user) +{ + testOk(e & e_free, "freePvt called"); + c |= e_free; + testOk(user == puser, "user pointer correct"); + + free(user); + puser = NULL; +} + +static void parse_error(void) +{ + testOk(e & e_error, "parse_error called"); + c |= e_error; +} + +static int parse_ok(void *user) +{ + testOk(e & e_ok, "parse_ok called"); + c |= e_ok; + testOk(user == puser, "user pointer correct"); + + return p_ok_return; +} + +static long channel_open(dbChannel *chan, void *user) +{ + testOk(e & e_open, "channel_open called"); + c |= e_open; + testOk(user == puser, "user pointer correct"); + + return c_open_return; +} + +static void channel_report(dbChannel *chan, void *user, const char *intro, int level) +{ + testOk(e & e_report, "channel_report called"); + c |= e_report; + testOk(user == puser, "user pointer correct"); +} + +static void channel_close(dbChannel *chan, void *user) +{ + testOk(e & e_close, "channel_close called"); + c |= e_close; + testOk(user == puser, "user pointer correct"); +} + +static chfPluginIf myPif = { + allocPvt, + freePvt, + + parse_error, + parse_ok, + + channel_open, + channel_report, + channel_close +}; + +static int checkValues(myStruct *my, epicsUInt32 i, int f, double d, char *s, int c) { + if (!my) return 0; + if (my->sent1 == PATTERN && my->sent2 == PATTERN && my->sent3 == PATTERN + && my->sent4 == PATTERN && my->sent5 == PATTERN && my->sent6 == PATTERN + && my->ival == i && my->flag == f && my->dval == d && my->enumval == c + && (strcmp(s, my->str) == 0)) { + return 1; + } else { + return 0; + } +} + +MAIN(chfPluginTest) +{ + dbChannel *pch; + + testPlan(667); + + /* Enum to string conversion */ + testOk(strcmp(chfPluginEnumString(colorEnum, 1, "-"), "R") == 0, "Enum to string: R"); + testOk(strcmp(chfPluginEnumString(colorEnum, 2, "-"), "G") == 0, "Enum to string: G"); + testOk(strcmp(chfPluginEnumString(colorEnum, 4, "-"), "B") == 0, "Enum to string: B"); + testOk(strcmp(chfPluginEnumString(colorEnum, 3, "-"), "-") == 0, "Enum to string: invalid index"); + + testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.dbx", ".:..", NULL)); + testOk(!!pdbbase, "pdbbase was set"); + + testOk(chfPluginRegister("buggy", &myPif, brokenOpts1), "not enough storage for integer"); + testOk(chfPluginRegister("buggy", &myPif, brokenOpts2), "not enough storage for double"); + testOk(chfPluginRegister("buggy", &myPif, brokenOpts3), "not enough storage for string"); + testOk(chfPluginRegister("buggy", &myPif, brokenOpts4), "not enough storage for enum"); + + testOk(!chfPluginRegister("strict", &myPif, strictOpts), "register plugin strict"); + testOk(!chfPluginRegister("noconv", &myPif, noconvOpts), "register plugin noconv"); + testOk(!chfPluginRegister("sloppy", &myPif, sloppyOpts), "register plugin sloppy"); + + /* STRICT parsing: mandatory, no conversion */ + + /* All perfect */ + e = e_alloc | e_ok; c = 0; + testOk(!!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "strict parsing: JSON correct"); + testOk(checkValues(puser, 1, 0, 1.2e15, "bar", 1), "guards intact, values correct"); + if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + e = e_close | e_free; c = 0; + if (pch) dbChannelDelete(pch); + testOk(!puser, "user part cleaned up"); + if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + + /* Any one missing must fail */ + e = e_alloc | e_error | e_free; c = 0; + testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), "strict parsing: c missing"); + testOk(!puser, "user part cleaned up"); + if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + e = e_alloc | e_error | e_free; c = 0; + testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"f\":false,\"i\":1,\"d\":1.2e15,\"c\":\"R\"}}")), "strict parsing: s missing"); + testOk(!puser, "user part cleaned up"); + if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + e = e_alloc | e_error | e_free; c = 0; + testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"c\":\"R\",\"f\":false,\"s\":\"bar\"}}")), "strict parsing: d missing"); + testOk(!puser, "user part cleaned up"); + if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + e = e_alloc | e_error | e_free; c = 0; + testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"d\":1.2e15,\"c\":\"R\",\"i\":1,\"s\":\"bar\"}}")), "strict parsing: f missing"); + testOk(!puser, "user part cleaned up"); + if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + e = e_alloc | e_error | e_free; c = 0; + testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"c\":\"R\",\"s\":\"bar\",\"f\":false,\"d\":1.2e15}}")), "strict parsing: i missing"); + testOk(!puser, "user part cleaned up"); + if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + + /* NOCONV parsing: optional, no conversion */ + + /* Any one missing must leave the default intact */ + e = e_alloc | e_ok; c = 0; + testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), "noconv parsing: c missing"); + testOk(checkValues(puser, 1, 0, 1.2e15, "bar", 4), "guards intact, values correct"); + if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + e = e_close | e_free; c = 0; + if (pch) dbChannelDelete(pch); + testOk(!puser, "user part cleaned up"); + if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + + e = e_any; + testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"c\":\"R\"}}")), "noconv parsing: s missing"); + testOk(checkValues(puser, 1, 0, 1.2e15, "hello", 1), "guards intact, values correct"); + if (pch) dbChannelDelete(pch); + + testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"f\":false,\"s\":\"bar\",\"c\":\"R\"}}")), "noconv parsing: d missing"); + testOk(checkValues(puser, 1, 0, 1.234e5, "bar", 1), "guards intact, values correct"); + if (pch) dbChannelDelete(pch); + + testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "noconv parsing: f missing"); + testOk(checkValues(puser, 1, 1, 1.2e15, "bar", 1), "guards intact, values correct"); + if (pch) dbChannelDelete(pch); + + testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "noconv parsing: i missing"); + testOk(checkValues(puser, 12, 0, 1.2e15, "bar", 1), "guards intact, values correct"); + if (pch) dbChannelDelete(pch); + + /* Reject wrong types */ +#define WRONGTYPETEST(Var, Val, Typ) \ + e = e_alloc | e_error | e_free; c = 0; \ + testOk(!(pch = dbChannelCreate("x.{\"noconv\":{\""#Var"\":"#Val"}}")), "noconv parsing: wrong type "#Typ" for "#Var); \ + testOk(!puser, "user part cleaned up"); \ + if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + + WRONGTYPETEST(i, 123.0, double); + WRONGTYPETEST(i, true, boolean); + WRONGTYPETEST(i, "1", string); + WRONGTYPETEST(f, "false", string); + WRONGTYPETEST(f, 0.0, double); + WRONGTYPETEST(f, 1, integer); + WRONGTYPETEST(d, "1.2", string); + WRONGTYPETEST(d, true, boolean); + WRONGTYPETEST(d, 123, integer); + WRONGTYPETEST(s, 1.23, double); + WRONGTYPETEST(s, true, boolean); + WRONGTYPETEST(s, 123, integer); + WRONGTYPETEST(c, 1.23, double); + WRONGTYPETEST(c, true, boolean); + WRONGTYPETEST(c, 2, integer); + + /* SLOPPY parsing: optional, with conversion */ + +#define CONVTESTGOOD(Var, Val, Typ, Ival, Fval, Dval, Sval, Cval) \ + e = e_alloc | e_ok; c = 0; \ + testOk(!!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), "sloppy parsing: "#Typ" (good) for "#Var); \ + testOk(checkValues(puser, Ival, Fval, Dval, Sval, Cval), "guards intact, values correct"); \ + if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); \ + e = e_close | e_free; c = 0; \ + if (pch) dbChannelDelete(pch); \ + testOk(!puser, "user part cleaned up"); \ + if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + +#define CONVTESTBAD(Var, Val, Typ) \ + e = e_alloc | e_error | e_free; c = 0; \ + testOk(!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), "sloppy parsing: "#Typ" (bad) for "#Var); \ + testOk(!puser, "user part cleaned up"); \ + if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + + /* To integer */ + CONVTESTGOOD(i, "123e4", positive string, 123, 1, 1.234e5, "hello", 4); + CONVTESTGOOD(i, "-12345", negative string, -12345, 1, 1.234e5, "hello", 4); + CONVTESTBAD(i, "9234567890", out-of-range string); + CONVTESTBAD(i, ".4", invalid string); + CONVTESTGOOD(i, false, valid boolean, 0, 1, 1.234e5, "hello", 4); + CONVTESTGOOD(i, 3456.789, valid double, 3456, 1, 1.234e5, "hello", 4); + CONVTESTBAD(i, 34.7e14, out-of-range double); + + /* To boolean */ + CONVTESTGOOD(f, "false", valid string, 12, 0, 1.234e5, "hello", 4); + CONVTESTGOOD(f, "False", capital valid string, 12, 0, 1.234e5, "hello", 4); + CONVTESTGOOD(f, "0", 0 string, 12, 0, 1.234e5, "hello", 4); + CONVTESTGOOD(f, "15", 15 string, 12, 1, 1.234e5, "hello", 4); + CONVTESTBAD(f, ".4", invalid .4 string); + CONVTESTBAD(f, "Flase", misspelled invalid string); + CONVTESTGOOD(f, 0, zero integer, 12, 0, 1.234e5, "hello", 4); + CONVTESTGOOD(f, 12, positive integer, 12, 1, 1.234e5, "hello", 4); + CONVTESTGOOD(f, -1234, negative integer, 12, 1, 1.234e5, "hello", 4); + CONVTESTGOOD(f, 0.4, positive non-zero double, 12, 1, 1.234e5, "hello", 4); + CONVTESTGOOD(f, 0.0, zero double, 12, 0, 1.234e5, "hello", 4); + CONVTESTGOOD(f, -0.0, minus-zero double, 12, 0, 1.234e5, "hello", 4); + CONVTESTGOOD(f, -1.24e14, negative double, 12, 1, 1.234e5, "hello", 4); + + /* To double */ + CONVTESTGOOD(d, "123e4", positive double string, 12, 1, 1.23e6, "hello", 4); + CONVTESTGOOD(d, "-7.89e-14", negative double string, 12, 1, -7.89e-14, "hello", 4); + CONVTESTGOOD(d, "123", positive integer string, 12, 1, 123.0, "hello", 4); + CONVTESTGOOD(d, "-1234567", negative integer string, 12, 1, -1.234567e6, "hello", 4); + CONVTESTBAD(d, "1.67e407", out-of-range double string); + CONVTESTBAD(d, "blubb", invalid blubb string); + CONVTESTGOOD(d, 123, positive integer, 12, 1, 123.0, "hello", 4); + CONVTESTGOOD(d, -12345, negative integer, 12, 1, -1.2345e4, "hello", 4); + CONVTESTGOOD(d, true, true boolean, 12, 1, 1.0, "hello", 4); + CONVTESTGOOD(d, false, false boolean, 12, 1, 0.0, "hello", 4); + + /* To string */ + CONVTESTGOOD(s, 12345, positive integer, 12, 1, 1.234e5, "12345", 4); + CONVTESTGOOD(s, -1234567891, negative integer, 12, 1, 1.234e5, "-1234567891", 4); + CONVTESTGOOD(s, true, true boolean, 12, 1, 1.234e5, "true", 4); + CONVTESTGOOD(s, false, false boolean, 12, 1, 1.234e5, "false", 4); + CONVTESTGOOD(s, 123e4, small positive double, 12, 1, 1.234e5, "1230000", 4); + CONVTESTGOOD(s, -123e24, negative double, 12, 1, 1.234e5, "-1.23e+26", 4); + CONVTESTGOOD(s, -1.23456789123456789e26, large rounded negative double, 12, 1, 1.234e5, "-1.234567891235e+26", 4); + + /* To Enum */ + CONVTESTGOOD(c, 2, valid integer choice, 12, 1, 1.234e5, "hello", 2); + CONVTESTBAD(c, 3, invalid integer choice); + CONVTESTBAD(c, 3.2, double); + CONVTESTGOOD(c, "R", valid string choice, 12, 1, 1.234e5, "hello", 1); + CONVTESTBAD(c, "blubb", invalid string choice); + + dbFreeBase(pdbbase); + + return testDone(); +} From 52c6eaa84892383769acaee37d6d46402a7aebf3 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:52 -0400 Subject: [PATCH 31/81] Refactor: get rid of ...Ctx (as Ctx is often used for user handles) [src/db/chfPlugin.c] --- src/ioc/db/chfPlugin.c | 146 ++++++++++++++++++++--------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/src/ioc/db/chfPlugin.c b/src/ioc/db/chfPlugin.c index 107c4a622..815a056b9 100644 --- a/src/ioc/db/chfPlugin.c +++ b/src/ioc/db/chfPlugin.c @@ -34,21 +34,21 @@ /* * Data for a chfPlugin */ -typedef struct chfPluginCtx { +typedef struct chfPlugin { const chfPluginArgDef *opts; size_t nopts; const chfPluginIf *pif; -} chfPluginCtx; +} chfPlugin; /* * Parser state data for a chfFilter (chfPlugin instance) */ -typedef struct chfFilterCtx { - const chfPluginCtx *plugin; +typedef struct chfFilter { + const chfPlugin *plugin; epicsUInt32 *found; void *puser; epicsInt16 nextParam; -} chfFilterCtx; +} chfFilter; /* Data types we get from the parser */ typedef enum chfPluginType { @@ -299,10 +299,10 @@ store_string_value(const chfPluginArgDef *opt, void *user, const char *val, size return 0; } -static void freeInstanceData(chfFilterCtx *fctx) +static void freeInstanceData(chfFilter *f) { - free(fctx->found); /* FIXME: Use a free-list */ - free(fctx); /* FIXME: Use a free-list */ + free(f->found); /* FIXME: Use a free-list */ + free(f); /* FIXME: Use a free-list */ } /* @@ -314,75 +314,75 @@ static void freeInstanceData(chfFilterCtx *fctx) */ static parse_result parse_start(chFilter *filter) { - chfPluginCtx *pctx = (chfPluginCtx*) filter->plug->puser; - chfFilterCtx *fctx; + chfPlugin *p = (chfPlugin*) filter->plug->puser; + chfFilter *f; /* Filter context */ /* FIXME: Use a free-list */ - fctx = calloc(1, sizeof(chfFilterCtx)); - if (!fctx) { + f = calloc(1, sizeof(chfFilter)); + if (!f) { fprintf(stderr,"chfFilterCtx calloc failed\n"); goto errfctx; } - fctx->nextParam = -1; + f->nextParam = -1; /* Bit array to find missing required keys */ /* FIXME: Use a free-list */ - fctx->found = calloc( (pctx->nopts/32)+1, sizeof(epicsUInt32) ); - if (!fctx->found) { + f->found = calloc( (p->nopts/32)+1, sizeof(epicsUInt32) ); + if (!f->found) { fprintf(stderr,"chfConfigParseStart: bit array calloc failed\n"); goto errbitarray; } /* Call the plugin to allocate its structure, it returns NULL on error */ - if (pctx->pif->allocPvt) { - if ((fctx->puser = pctx->pif->allocPvt()) == NULL) + if (p->pif->allocPvt) { + if ((f->puser = p->pif->allocPvt()) == NULL) goto errplugin; } - filter->puser = (void*) fctx; + filter->puser = (void*) f; return parse_continue; errplugin: - free(fctx->found); /* FIXME: Use a free-list */ + free(f->found); /* FIXME: Use a free-list */ errbitarray: - free(fctx); /* FIXME: Use a free-list */ + free(f); /* FIXME: Use a free-list */ errfctx: return parse_stop; } static void parse_abort(chFilter *filter) { - chfPluginCtx *pctx = (chfPluginCtx*) filter->plug->puser; - chfFilterCtx *fctx = (chfFilterCtx*) filter->puser; + chfPlugin *p = (chfPlugin*) filter->plug->puser; + chfFilter *f = (chfFilter*) filter->puser; /* Call the plugin to tell it we're aborting */ - if (pctx->pif->parse_error) pctx->pif->parse_error(); - if (pctx->pif->freePvt) pctx->pif->freePvt(fctx->puser); - freeInstanceData(fctx); + if (p->pif->parse_error) p->pif->parse_error(); + if (p->pif->freePvt) p->pif->freePvt(f->puser); + freeInstanceData(f); } static parse_result parse_end(chFilter *filter) { - chfPluginCtx *pctx = (chfPluginCtx*) filter->plug->puser; - chfFilterCtx *fctx = (chfFilterCtx*) filter->puser; + chfPlugin *p = (chfPlugin*) filter->plug->puser; + chfFilter *f = (chfFilter*) filter->puser; const chfPluginArgDef *cur; int i; - for(cur = pctx->opts, i = 0; cur && cur->name; cur++, i++) { - if ( !(fctx->found[i/32] & (1 << (i%32))) && cur->required ) { - if (pctx->pif->parse_error) pctx->pif->parse_error(); - if (pctx->pif->freePvt) pctx->pif->freePvt(fctx->puser); - freeInstanceData(fctx); + for(cur = p->opts, i = 0; cur && cur->name; cur++, i++) { + if ( !(f->found[i/32] & (1 << (i%32))) && cur->required ) { + if (p->pif->parse_error) p->pif->parse_error(); + if (p->pif->freePvt) p->pif->freePvt(f->puser); + freeInstanceData(f); return parse_stop; } } /* Call the plugin to tell it we're done */ - if (pctx->pif->parse_ok) { - if (pctx->pif->parse_ok(fctx->puser)) { - if (pctx->pif->freePvt) pctx->pif->freePvt(fctx->puser); - freeInstanceData(fctx); + if (p->pif->parse_ok) { + if (p->pif->parse_ok(f->puser)) { + if (p->pif->freePvt) p->pif->freePvt(f->puser); + freeInstanceData(f); return parse_stop; } } @@ -392,10 +392,10 @@ static parse_result parse_end(chFilter *filter) static parse_result parse_boolean(chFilter *filter, int boolVal) { - const chfPluginArgDef *opts = ((chfPluginCtx*)filter->plug->puser)->opts; - chfFilterCtx *ctx = (chfFilterCtx*)filter->puser; + const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; + chfFilter *f = (chfFilter*)filter->puser; - if (ctx->nextParam < 0 || store_boolean_value(&opts[ctx->nextParam], ctx->puser, boolVal)) { + if (f->nextParam < 0 || store_boolean_value(&opts[f->nextParam], f->puser, boolVal)) { return parse_stop; } else { return parse_continue; @@ -404,10 +404,10 @@ static parse_result parse_boolean(chFilter *filter, int boolVal) static parse_result parse_integer(chFilter *filter, long integerVal) { - const chfPluginArgDef *opts = ((chfPluginCtx*)filter->plug->puser)->opts; - chfFilterCtx *ctx = (chfFilterCtx*)filter->puser; + const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; + chfFilter *f = (chfFilter*)filter->puser; - if (ctx->nextParam < 0 || store_integer_value(&opts[ctx->nextParam], ctx->puser, integerVal)) { + if (f->nextParam < 0 || store_integer_value(&opts[f->nextParam], f->puser, integerVal)) { return parse_stop; } else { return parse_continue; @@ -416,10 +416,10 @@ static parse_result parse_integer(chFilter *filter, long integerVal) static parse_result parse_double(chFilter *filter, double doubleVal) { - const chfPluginArgDef *opts = ((chfPluginCtx*)filter->plug->puser)->opts; - chfFilterCtx *ctx = (chfFilterCtx*)filter->puser; + const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; + chfFilter *f = (chfFilter*)filter->puser; - if (ctx->nextParam < 0 || store_double_value(&opts[ctx->nextParam], ctx->puser, doubleVal)) { + if (f->nextParam < 0 || store_double_value(&opts[f->nextParam], f->puser, doubleVal)) { return parse_stop; } else { return parse_continue; @@ -428,10 +428,10 @@ static parse_result parse_double(chFilter *filter, double doubleVal) static parse_result parse_string(chFilter *filter, const char *stringVal, size_t stringLen) { - const chfPluginArgDef *opts = ((chfPluginCtx*)filter->plug->puser)->opts; - chfFilterCtx *ctx = (chfFilterCtx*)filter->puser; + const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; + chfFilter *f = (chfFilter*)filter->puser; - if (ctx->nextParam < 0 || store_string_value(&opts[ctx->nextParam], ctx->puser, stringVal, stringLen)) { + if (f->nextParam < 0 || store_string_value(&opts[f->nextParam], f->puser, stringVal, stringLen)) { return parse_stop; } else { return parse_continue; @@ -446,22 +446,22 @@ static parse_result parse_start_map(chFilter *filter) static parse_result parse_map_key(chFilter *filter, const char *key, size_t stringLen) { const chfPluginArgDef *cur; - const chfPluginArgDef *opts = ((chfPluginCtx*)filter->plug->puser)->opts; - chfFilterCtx *ctx = (chfFilterCtx*)filter->puser; + const chfPluginArgDef *opts = ((chfPlugin*)filter->plug->puser)->opts; + chfFilter *f = (chfFilter*)filter->puser; int i; - ctx->nextParam = -1; + f->nextParam = -1; for(cur = opts, i = 0; cur && cur->name; cur++, i++) { if (strncmp(key, cur->name, stringLen) == 0) { - ctx->nextParam = i; + f->nextParam = i; break; } } - if (ctx->nextParam == -1) { + if (f->nextParam == -1) { return parse_stop; } - ctx->found[i/32] |= 1<<(i%32); + f->found[i/32] |= 1<<(i%32); return parse_continue; } @@ -472,30 +472,30 @@ static parse_result parse_end_map(chFilter *filter) static long channel_open(chFilter *filter) { - chfPluginCtx *pctx = (chfPluginCtx*) filter->plug->puser; - chfFilterCtx *fctx = (chfFilterCtx*) filter->puser; + chfPlugin *p = (chfPlugin*) filter->plug->puser; + chfFilter *f = (chfFilter*) filter->puser; - if (pctx->pif->channel_open) return pctx->pif->channel_open(filter->chan, fctx->puser); + if (p->pif->channel_open) return p->pif->channel_open(filter->chan, f->puser); else return 0; } static void channel_report(chFilter *filter, const char *intro, int level) { - chfPluginCtx *pctx = (chfPluginCtx*) filter->plug->puser; - chfFilterCtx *fctx = (chfFilterCtx*) filter->puser; + chfPlugin *p = (chfPlugin*) filter->plug->puser; + chfFilter *f = (chfFilter*) filter->puser; - if (pctx->pif->channel_report) pctx->pif->channel_report(filter->chan, fctx->puser, intro, level); + if (p->pif->channel_report) p->pif->channel_report(filter->chan, f->puser, intro, level); } static void channel_close(chFilter *filter) { - chfPluginCtx *pctx = (chfPluginCtx*) filter->plug->puser; - chfFilterCtx *fctx = (chfFilterCtx*) filter->puser; + chfPlugin *p = (chfPlugin*) filter->plug->puser; + chfFilter *f = (chfFilter*) filter->puser; - if (pctx->pif->channel_close) pctx->pif->channel_close(filter->chan, fctx->puser); - if (pctx->pif->freePvt) pctx->pif->freePvt(fctx->puser); - free(fctx->found); /* FIXME: Use a free-list */ - free(fctx); /* FIXME: Use a free-list */ + if (p->pif->channel_close) p->pif->channel_close(filter->chan, f->puser); + if (p->pif->freePvt) p->pif->freePvt(f->puser); + free(f->found); /* FIXME: Use a free-list */ + free(f); /* FIXME: Use a free-list */ } /* @@ -543,7 +543,7 @@ int epicsShareAPI chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef* opts) { - chfPluginCtx *pctx; + chfPlugin *p; size_t i; const chfPluginArgDef *cur; @@ -601,13 +601,13 @@ chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef } } - /* Plugin context */ - pctx = dbCalloc(1, sizeof(chfPluginCtx)); - pctx->pif = pif; - pctx->opts = opts; - pctx->nopts = i; + /* Plugin data */ + p = dbCalloc(1, sizeof(chfPlugin)); + p->pif = pif; + p->opts = opts; + p->nopts = i; - dbRegisterFilter(key, &wrapper_fif, pctx); + dbRegisterFilter(key, &wrapper_fif, p); return 0; } From 9f5f5246aaee9f3e675ff254c03e38b2e19723fd Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:52 -0400 Subject: [PATCH 32/81] Added required mask per plugin to shorten check loop. Trivial test change. [src/db/chfPlugin.c src/db/test/chfPluginTest.c] --- src/ioc/db/chfPlugin.c | 19 +++++++++++++++++-- src/ioc/db/test/chfPluginTest.c | 8 ++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/ioc/db/chfPlugin.c b/src/ioc/db/chfPlugin.c index 815a056b9..f32eb846e 100644 --- a/src/ioc/db/chfPlugin.c +++ b/src/ioc/db/chfPlugin.c @@ -37,6 +37,7 @@ typedef struct chfPlugin { const chfPluginArgDef *opts; size_t nopts; + epicsUInt32 *required; const chfPluginIf *pif; } chfPlugin; @@ -369,8 +370,9 @@ static parse_result parse_end(chFilter *filter) const chfPluginArgDef *cur; int i; - for(cur = p->opts, i = 0; cur && cur->name; cur++, i++) { - if ( !(f->found[i/32] & (1 << (i%32))) && cur->required ) { + /* Check if all required arguments were supplied */ + for(i = 0; i < (p->nopts/32)+1; i++) { + if ((f->found[i] & p->required[i]) != p->required[i]) { if (p->pif->parse_error) p->pif->parse_error(); if (p->pif->freePvt) p->pif->freePvt(f->puser); freeInstanceData(f); @@ -546,6 +548,7 @@ chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef chfPlugin *p; size_t i; const chfPluginArgDef *cur; + epicsUInt32 *reqd; /* Check and count options */ for (i = 0, cur = opts; cur && cur->name; i++, cur++) { @@ -601,11 +604,23 @@ chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef } } + /* Bit array used to find missing required keys */ + reqd = dbCalloc((i/32)+1, sizeof(epicsUInt32)); + if (!reqd) { + fprintf(stderr,"Plugin %s: bit array calloc failed\n", key); + return -1; + } + + for (i = 0, cur = opts; cur && cur->name; i++, cur++) { + if (cur->required) reqd[i/32] |= 1 << (i%32); + } + /* Plugin data */ p = dbCalloc(1, sizeof(chfPlugin)); p->pif = pif; p->opts = opts; p->nopts = i; + p->required = reqd; dbRegisterFilter(key, &wrapper_fif, p); diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index 44609aee6..95f0a3c79 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -217,10 +217,10 @@ MAIN(chfPluginTest) testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.dbx", ".:..", NULL)); testOk(!!pdbbase, "pdbbase was set"); - testOk(chfPluginRegister("buggy", &myPif, brokenOpts1), "not enough storage for integer"); - testOk(chfPluginRegister("buggy", &myPif, brokenOpts2), "not enough storage for double"); - testOk(chfPluginRegister("buggy", &myPif, brokenOpts3), "not enough storage for string"); - testOk(chfPluginRegister("buggy", &myPif, brokenOpts4), "not enough storage for enum"); + testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts1), "not enough storage for integer"); + testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts2), "not enough storage for double"); + testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts3), "not enough storage for string"); + testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts4), "not enough storage for enum"); testOk(!chfPluginRegister("strict", &myPif, strictOpts), "register plugin strict"); testOk(!chfPluginRegister("noconv", &myPif, noconvOpts), "register plugin noconv"); From d92665c5a339b4ad6097f030c1f45e363c71b7c8 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:52 -0400 Subject: [PATCH 33/81] Refactoring: enforce consistent use of names in dbEvent.c - NO EFFECTIVE CODE CHANGES --- src/ioc/db/dbEvent.c | 186 ++++++++++++++++++++----------------------- 1 file changed, 88 insertions(+), 98 deletions(-) diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index 2280309a8..9c45da977 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -62,7 +62,7 @@ struct evSubscrip { unsigned long npend; /* n times this event is on the queue */ unsigned long nreplace; /* n times replacing event on the queue */ unsigned char select; - char valque; + char useValque; char callBackInProgress; char enabled; }; @@ -115,21 +115,14 @@ struct event_user { #define RNGINC(OLD)\ ( (unsigned short) ( (OLD) >= (EVENTQUESIZE-1) ? 0 : (OLD)+1 ) ) -#define LOCKEVQUE(EV_QUE)\ -epicsMutexMustLock((EV_QUE)->writelock); - -#define UNLOCKEVQUE(EV_QUE)\ -epicsMutexUnlock((EV_QUE)->writelock); - -#define LOCKREC(RECPTR)\ -epicsMutexMustLock((RECPTR)->mlok); - -#define UNLOCKREC(RECPTR)\ -epicsMutexUnlock((RECPTR)->mlok); +#define LOCKEVQUE(EV_QUE) epicsMutexMustLock((EV_QUE)->writelock) +#define UNLOCKEVQUE(EV_QUE) {epicsMutexUnlock((EV_QUE)->writelock);} +#define LOCKREC(RECPTR) epicsMutexMustLock((RECPTR)->mlok) +#define UNLOCKREC(RECPTR) {epicsMutexUnlock((RECPTR)->mlok);} static void *dbevEventUserFreeList; static void *dbevEventQueueFreeList; -static void *dbevEventBlockFreeList; +static void *dbevEventSubscriptionFreeList; static char *EVENT_PEND_NAME = "eventTask"; @@ -164,7 +157,7 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) DBADDR addr; long status; struct evSubscrip *pevent; - dbFldDes *pdbFldDes; + dbFldDes *pdbFldDes; if ( ! pname ) return DB_EVENT_OK; status = dbNameToAddr ( pname, &addr ); @@ -173,13 +166,13 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) return DB_EVENT_ERROR; } - LOCKREC ( addr.precord ); + LOCKREC (addr.precord) pevent = (struct evSubscrip *) ellFirst ( &addr.precord->mlis ); if ( ! pevent ) { printf ( "\"%s\": No PV event subscriptions ( monitors ).\n", pname ); - UNLOCKREC (addr.precord); + UNLOCKREC (addr.precord) return DB_EVENT_OK; } @@ -187,16 +180,16 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) ellCount ( &addr.precord->mlis ) ); while ( pevent ) { - pdbFldDes = dbChannelFldDes(pevent->chan); + pdbFldDes = dbChannelFldDes(pevent->chan); if ( level > 0 ) { printf ( "%4.4s", pdbFldDes->name ); printf ( " { " ); - if ( pevent->select & DBE_VALUE ) printf( "VALUE " ); - if ( pevent->select & DBE_LOG ) printf( "LOG " ); - if ( pevent->select & DBE_ALARM ) printf( "ALARM " ); - if ( pevent->select & DBE_PROPERTY ) printf( "PROPERTY " ); + if ( pevent->select & DBE_VALUE ) printf( "VALUE " ); + if ( pevent->select & DBE_LOG ) printf( "LOG " ); + if ( pevent->select & DBE_ALARM ) printf( "ALARM " ); + if ( pevent->select & DBE_PROPERTY ) printf( "PROPERTY " ); printf ( "}" ); if ( pevent->npend ) { @@ -230,7 +223,7 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) if ( pevent->nreplace ) { printf (", discarded by replacement=%ld", pevent->nreplace); } - if ( ! pevent->valque ) { + if ( ! pevent->useValque ) { printf (", queueing disabled" ); } LOCKEVQUE(pevent->ev_que); @@ -255,10 +248,10 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) printf( "\n" ); } - pevent = (struct evSubscrip *) ellNext ( &pevent->node ); + pevent = (struct evSubscrip *) ellNext ( &pevent->node ); } - UNLOCKREC ( addr.precord ); + UNLOCKREC (addr.precord) return DB_EVENT_OK; } @@ -284,8 +277,8 @@ dbEventCtx epicsShareAPI db_init_events (void) freeListInitPvt(&dbevEventQueueFreeList, sizeof(struct event_que),8); } - if (!dbevEventBlockFreeList) { - freeListInitPvt(&dbevEventBlockFreeList, + if (!dbevEventSubscriptionFreeList) { + freeListInitPvt(&dbevEventSubscriptionFreeList, sizeof(struct evSubscrip),256); } @@ -390,7 +383,7 @@ dbEventSubscription epicsShareAPI db_add_event ( return NULL; } - pevent = freeListCalloc ( dbevEventBlockFreeList ); + pevent = freeListCalloc (dbevEventSubscriptionFreeList); if ( ! pevent ) { return NULL; } @@ -445,10 +438,10 @@ dbEventSubscription epicsShareAPI db_add_event ( */ if( dbChannelElements(chan) == 1 && dbChannelElementSize(chan) <= sizeof(union native_value)) { - pevent->valque = TRUE; + pevent->useValque = TRUE; } else { - pevent->valque = FALSE; + pevent->useValque = FALSE; } return pevent; @@ -457,33 +450,33 @@ dbEventSubscription epicsShareAPI db_add_event ( /* * db_event_enable() */ -void epicsShareAPI db_event_enable (dbEventSubscription es) +void epicsShareAPI db_event_enable (dbEventSubscription event) { - struct evSubscrip * const pevent = (struct evSubscrip *) es; + struct evSubscrip * const pevent = (struct evSubscrip *) event; struct dbCommon * const precord = dbChannelRecord(pevent->chan); - LOCKREC(precord); + LOCKREC (precord) if ( ! pevent->enabled ) { ellAdd (&precord->mlis, &pevent->node); pevent->enabled = TRUE; } - UNLOCKREC(precord); + UNLOCKREC (precord) } /* * db_event_disable() */ -void epicsShareAPI db_event_disable (dbEventSubscription es) +void epicsShareAPI db_event_disable (dbEventSubscription event) { - struct evSubscrip * const pevent = (struct evSubscrip *) es; + struct evSubscrip * const pevent = (struct evSubscrip *) event; struct dbCommon * const precord = dbChannelRecord(pevent->chan); - LOCKREC(precord); + LOCKREC (precord) if ( pevent->enabled ) { ellDelete(&precord->mlis, &pevent->node); pevent->enabled = FALSE; } - UNLOCKREC(precord); + UNLOCKREC (precord) } /* @@ -493,18 +486,18 @@ void epicsShareAPI db_event_disable (dbEventSubscription es) static void event_remove ( struct event_que *ev_que, unsigned short index, struct evSubscrip *placeHolder ) { - struct evSubscrip * const pEvent = ev_que->evque[index]; + struct evSubscrip * const pevent = ev_que->evque[index]; ev_que->evque[index] = placeHolder; - if ( pEvent->npend == 1u ) { - pEvent->pLastLog = NULL; + if ( pevent->npend == 1u ) { + pevent->pLastLog = NULL; } else { - assert ( pEvent->npend > 1u ); + assert ( pevent->npend > 1u ); assert ( ev_que->nDuplicates >= 1u ); ev_que->nDuplicates--; } - pEvent->npend--; + pevent->npend--; } /* @@ -514,12 +507,12 @@ static void event_remove ( struct event_que *ev_que, * the same block at the same time. * */ -void epicsShareAPI db_cancel_event (dbEventSubscription es) +void epicsShareAPI db_cancel_event (dbEventSubscription event) { - struct evSubscrip * const pevent = ( struct evSubscrip * ) es; + struct evSubscrip * const pevent = (struct evSubscrip *) event; unsigned short getix; - db_event_disable ( es ); + db_event_disable ( event ); /* * flag the event as canceled by NULLing out the callback handler @@ -527,7 +520,7 @@ void epicsShareAPI db_cancel_event (dbEventSubscription es) * make certain that the event isnt being accessed while * its call back changes */ - LOCKEVQUE ( pevent->ev_que ) + LOCKEVQUE (pevent->ev_que) pevent->user_sub = NULL; @@ -560,17 +553,17 @@ void epicsShareAPI db_cancel_event (dbEventSubscription es) } else { while ( pevent->callBackInProgress ) { - UNLOCKEVQUE ( pevent->ev_que ) + UNLOCKEVQUE (pevent->ev_que) epicsEventMustWait ( pevent->ev_que->evUser->pflush_sem ); - LOCKEVQUE ( pevent->ev_que ) + LOCKEVQUE (pevent->ev_que) } } pevent->ev_que->quota -= EVENTENTRIES; - UNLOCKEVQUE ( pevent->ev_que ) + UNLOCKEVQUE (pevent->ev_que) - freeListFree ( dbevEventBlockFreeList, pevent ); + freeListFree ( dbevEventSubscriptionFreeList, pevent ); return; } @@ -584,7 +577,7 @@ void epicsShareAPI db_flush_extra_labor_event (dbEventCtx ctx) { struct event_user * const evUser = (struct event_user *) ctx; - epicsMutexMustLock ( evUser->lock ); + epicsMutexMustLock ( evUser->lock ); while ( evUser->extraLaborBusy ) { epicsMutexUnlock ( evUser->lock ); epicsThreadSleep(0.1); @@ -655,7 +648,7 @@ static void db_post_single_event_private (struct evSubscrip *event) * threads writing/reading it */ - LOCKEVQUE(ev_que) + LOCKEVQUE (ev_que) /* * if we have an event on the queue and we are @@ -664,8 +657,8 @@ static void db_post_single_event_private (struct evSubscrip *event) * events (saving them without the current valuye * serves no purpose) */ - if (!event->valque && event->npend>0u) { - UNLOCKEVQUE(ev_que) + if (!event->useValque && event->npend>0u) { + UNLOCKEVQUE (ev_que) return; } @@ -717,12 +710,12 @@ static void db_post_single_event_private (struct evSubscrip *event) ev_que->putix = RNGINC ( ev_que->putix ); } - if (pLog && event->valque) { + if (pLog && event->useValque) { struct dbChannel *chan = event->chan; - struct dbCommon *precord = dbChannelRecord(chan); - pLog->stat = precord->stat; - pLog->sevr = precord->sevr; - pLog->time = precord->time; + struct dbCommon *prec = dbChannelRecord(chan); + pLog->stat = prec->stat; + pLog->sevr = prec->sevr; + pLog->time = prec->time; /* * use memcpy to avoid a bus error on @@ -736,7 +729,7 @@ static void db_post_single_event_private (struct evSubscrip *event) event->pLastLog = pLog; } - UNLOCKEVQUE(ev_que) + UNLOCKEVQUE (ev_que) /* * its more efficent to notify the event handler @@ -764,27 +757,27 @@ void *pField, unsigned int caEventMask ) { - struct dbCommon * const pdbc = (struct dbCommon *)pRecord; - struct evSubscrip * event; + struct dbCommon * const prec = (struct dbCommon *) pRecord; + struct evSubscrip *pevent; - if (pdbc->mlis.count == 0) return DB_EVENT_OK; /* no monitors set */ + if (prec->mlis.count == 0) return DB_EVENT_OK; /* no monitors set */ - LOCKREC(pdbc); + LOCKREC (prec) - for (event = (struct evSubscrip *) pdbc->mlis.node.next; - event; event = (struct evSubscrip *) event->node.next){ + for (pevent = (struct evSubscrip *) prec->mlis.node.next; + pevent; pevent = (struct evSubscrip *) pevent->node.next){ /* * Only send event msg if they are waiting on the field which * changed or pval==NULL and waiting on alarms and alarms changed */ - if ( (dbChannelField(event->chan) == (void *)pField || pField==NULL) && - (caEventMask & event->select) ) { - db_post_single_event_private (event); + if ( (dbChannelField(pevent->chan) == (void *)pField || pField==NULL) && + (caEventMask & pevent->select)) { + db_post_single_event_private (pevent); } } - UNLOCKREC(pdbc); + UNLOCKREC (prec) return DB_EVENT_OK; } @@ -792,14 +785,14 @@ unsigned int caEventMask /* * DB_POST_SINGLE_EVENT() */ -void epicsShareAPI db_post_single_event (dbEventSubscription es) +void epicsShareAPI db_post_single_event (dbEventSubscription event) { - struct evSubscrip * const event = (struct evSubscrip *) es; - struct dbCommon * const precord = dbChannelRecord(event->chan); + struct evSubscrip * const pevent = (struct evSubscrip *) event; + struct dbCommon * const prec = dbChannelRecord(pevent->chan); - dbScanLock (precord); - db_post_single_event_private (event); - dbScanUnlock (precord); + dbScanLock (prec); + db_post_single_event_private (pevent); + dbScanUnlock (prec); } /* @@ -815,7 +808,7 @@ static int event_read ( struct event_que *ev_que ) * evUser ring buffer must be locked for the multiple * threads writing/reading it */ - LOCKEVQUE ( ev_que ) + LOCKEVQUE (ev_que) /* * if in flow control mode drain duplicates and then @@ -823,15 +816,15 @@ static int event_read ( struct event_que *ev_que ) * mode is over */ if ( ev_que->evUser->flowCtrlMode && ev_que->nDuplicates == 0u ) { - UNLOCKEVQUE(ev_que); + UNLOCKEVQUE (ev_que) return DB_EVENT_OK; } while ( ev_que->evque[ev_que->getix] != EVENTQEMPTY ) { db_field_log fl = ev_que->valque[ev_que->getix]; - struct evSubscrip *event = ev_que->evque[ev_que->getix]; + struct evSubscrip *pevent = ev_que->evque[ev_que->getix]; - if ( event == &canceledEvent ) { + if ( pevent == &canceledEvent ) { ev_que->evque[ev_que->getix] = EVENTQEMPTY; ev_que->getix = RNGINC ( ev_que->getix ); assert ( ev_que->nCanceled > 0 ); @@ -844,10 +837,9 @@ static int event_read ( struct event_que *ev_que ) * communication. (for other types they get whatever happens * to be there upon wakeup) */ - if ( event->valque ) { + if ( pevent->useValque ) { pfl = &fl; - } - else { + } else { pfl = NULL; } @@ -858,7 +850,7 @@ static int event_read ( struct event_que *ev_que ) * create a local copy of the call back parameters while * we still have the lock */ - user_sub = event->user_sub; + user_sub = pevent->user_sub; /* * Next event pointer can be used by event tasks to determine @@ -876,11 +868,11 @@ static int event_read ( struct event_que *ev_que ) * despite the fact that the event queue does not point to * it. */ - event->callBackInProgress = TRUE; - UNLOCKEVQUE ( ev_que ) - ( *user_sub ) ( event->user_arg, event->chan, + pevent->callBackInProgress = TRUE; + UNLOCKEVQUE (ev_que) + ( *user_sub ) ( pevent->user_arg, pevent->chan, ev_que->evque[ev_que->getix] != EVENTQEMPTY, pfl ); - LOCKEVQUE ( ev_que ) + LOCKEVQUE (ev_que) /* * check to see if this event has been canceled each @@ -889,22 +881,22 @@ static int event_read ( struct event_que *ev_que ) * complete sem if there are no longer any events on the * queue */ - if ( ev_que->evUser->pSuicideEvent == event ) { + if ( ev_que->evUser->pSuicideEvent == pevent ) { ev_que->evUser->pSuicideEvent = NULL; } else { - if ( event->user_sub==NULL && event->npend==0u ) { - event->callBackInProgress = FALSE; + if ( pevent->user_sub==NULL && pevent->npend==0u ) { + pevent->callBackInProgress = FALSE; epicsEventSignal ( ev_que->evUser->pflush_sem ); } else { - event->callBackInProgress = FALSE; + pevent->callBackInProgress = FALSE; } } } } - UNLOCKEVQUE ( ev_que ) + UNLOCKEVQUE (ev_que) return DB_EVENT_OK; } @@ -924,7 +916,7 @@ static void event_task (void *pParm) taskwdInsert ( epicsThreadGetIdSelf(), NULL, NULL ); - do{ + do { void (*pExtraLaborSub) (void *); void *pExtraLaborArg; epicsEventMustWait(evUser->ppendsem); @@ -959,7 +951,7 @@ static void event_task (void *pParm) } epicsMutexUnlock ( evUser->lock ); - } while( ! evUser->pendexit ); + } while ( ! evUser->pendexit ); epicsMutexDestroy(evUser->firstque.writelock); @@ -967,7 +959,7 @@ static void event_task (void *pParm) struct event_que *nextque; ev_que = evUser->firstque.nextque; - while(ev_que){ + while (ev_que) { nextque = ev_que->nextque; epicsMutexDestroy(ev_que->writelock); freeListFree(dbevEventQueueFreeList, ev_que); @@ -1071,5 +1063,3 @@ void epicsShareAPI db_event_flow_ctrl_mode_off (dbEventCtx ctx) printf("fc off %lu\n", tickGet()); #endif } - - From dd03d19cf2a170b571ea797a3258d9eef65d1b40 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:53 -0400 Subject: [PATCH 34/81] dbEvent.c: use a free list for the field logs and keep a pointer in the event queue --- src/ioc/db/dbEvent.c | 52 +++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index 9c45da977..3c80031a0 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -74,7 +74,7 @@ struct event_que { /* lock writers to the ring buffer only */ /* readers must never slow up writers */ epicsMutexId writelock; - db_field_log valque[EVENTQUESIZE]; + db_field_log *valque[EVENTQUESIZE]; struct evSubscrip *evque[EVENTQUESIZE]; struct event_que *nextque; /* in case que quota exceeded */ struct event_user *evUser; /* event user parent struct */ @@ -123,6 +123,7 @@ struct event_user { static void *dbevEventUserFreeList; static void *dbevEventQueueFreeList; static void *dbevEventSubscriptionFreeList; +static void *dbevFieldLogFreeList; static char *EVENT_PEND_NAME = "eventTask"; @@ -281,6 +282,10 @@ dbEventCtx epicsShareAPI db_init_events (void) freeListInitPvt(&dbevEventSubscriptionFreeList, sizeof(struct evSubscrip),256); } + if (!dbevFieldLogFreeList) { + freeListInitPvt(&dbevFieldLogFreeList, + sizeof(struct db_field_log),2048); + } evUser = (struct event_user *) freeListCalloc(dbevEventUserFreeList); @@ -639,7 +644,7 @@ int epicsShareAPI db_post_extra_labor (dbEventCtx ctx) static void db_post_single_event_private (struct evSubscrip *event) { struct event_que * const ev_que = event->ev_que; - db_field_log * pLog; + db_field_log *pLog = NULL; int firstEventFlag; unsigned rngSpace; @@ -691,7 +696,6 @@ static void db_post_single_event_private (struct evSubscrip *event) */ else { assert ( ev_que->evque[ev_que->putix] == EVENTQEMPTY ); - pLog = &ev_que->valque[ev_que->putix]; ev_que->evque[ev_que->putix] = event; if (event->npend>0u) { ev_que->nDuplicates++; @@ -710,23 +714,28 @@ static void db_post_single_event_private (struct evSubscrip *event) ev_que->putix = RNGINC ( ev_que->putix ); } - if (pLog && event->useValque) { - struct dbChannel *chan = event->chan; - struct dbCommon *prec = dbChannelRecord(chan); - pLog->stat = prec->stat; - pLog->sevr = prec->sevr; - pLog->time = prec->time; + if (event->useValque) { + if (!pLog) { + pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList); + } + if (pLog) { + struct dbChannel *chan = event->chan; + struct dbCommon *prec = dbChannelRecord(chan); + pLog->stat = prec->stat; + pLog->sevr = prec->sevr; + pLog->time = prec->time; - /* - * use memcpy to avoid a bus error on - * union copy of char in the db at an odd - * address - */ - memcpy(& pLog->field, - dbChannelField(chan), - dbChannelElementSize(chan)); + /* + * use memcpy to avoid a bus error on + * union copy of char in the db at an odd + * address + */ + memcpy(& pLog->field, + dbChannelField(chan), + dbChannelElementSize(chan)); - event->pLastLog = pLog; + event->pLastLog = pLog; + } } UNLOCKEVQUE (ev_que) @@ -821,7 +830,7 @@ static int event_read ( struct event_que *ev_que ) } while ( ev_que->evque[ev_que->getix] != EVENTQEMPTY ) { - db_field_log fl = ev_que->valque[ev_que->getix]; + pfl = ev_que->valque[ev_que->getix]; struct evSubscrip *pevent = ev_que->evque[ev_que->getix]; if ( pevent == &canceledEvent ) { @@ -837,9 +846,7 @@ static int event_read ( struct event_que *ev_que ) * communication. (for other types they get whatever happens * to be there upon wakeup) */ - if ( pevent->useValque ) { - pfl = &fl; - } else { + if ( !pevent->useValque ) { pfl = NULL; } @@ -894,6 +901,7 @@ static int event_read ( struct event_que *ev_que ) } } } + if (pfl) freeListFree (dbevFieldLogFreeList, pfl); } UNLOCKEVQUE (ev_que) From 4dd42383efac1c14e5eaf1d1fe28bd76ba5ef69d Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:53 -0400 Subject: [PATCH 35/81] Add filter/plugin insertion into the high-priority filter list (pre event queue). * Move evSubscrip from dbEvent.[ch] (private) to dbChannel.h (public). * Split up db_post_single_event_private into two parts: ..._first creates a new db_field_log chunk and copies from db. ..._final puts a db_field_log into the event queue. * Add a typedef for the pre-event-queue recursive callbacks. * Add a register function for the callback (plus void* arg) to filter and plugin interfaces. * Add the code that builds up the pre-event-queue callback chain. * Changes to the test files to compile (no tests added yet). * Fix missing include in db_field_log.h. --- src/ioc/db/chfPlugin.c | 12 +++- src/ioc/db/chfPlugin.h | 27 ++++++++ src/ioc/db/dbChannel.c | 23 ++++++- src/ioc/db/dbChannel.h | 30 +++++++- src/ioc/db/dbEvent.c | 118 +++++++++++++++++--------------- src/ioc/db/dbEvent.h | 2 + src/ioc/db/db_field_log.h | 4 +- src/ioc/db/test/chfPluginTest.c | 12 ++++ src/ioc/db/test/dbChannelTest.c | 12 +++- 9 files changed, 176 insertions(+), 64 deletions(-) diff --git a/src/ioc/db/chfPlugin.c b/src/ioc/db/chfPlugin.c index f32eb846e..d0ef74ab4 100644 --- a/src/ioc/db/chfPlugin.c +++ b/src/ioc/db/chfPlugin.c @@ -367,7 +367,6 @@ static parse_result parse_end(chFilter *filter) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; - const chfPluginArgDef *cur; int i; /* Check if all required arguments were supplied */ @@ -481,6 +480,16 @@ static long channel_open(chFilter *filter) else return 0; } +static long channel_register_pre_eventq_cb(chFilter *filter, chPostEventFunc* cb_in, void *arg_in, + chPostEventFunc **cb_out, void **arg_out) +{ + chfPlugin *p = (chfPlugin*) filter->plug->puser; + chfFilter *f = (chfFilter*) filter->puser; + + if (p->pif->channelRegisterPreEventQueCB) return p->pif->channelRegisterPreEventQueCB(filter->chan, f->puser, cb_in, arg_in, cb_out, arg_out); + else return -1; +} + static void channel_report(chFilter *filter, const char *intro, int level) { chfPlugin *p = (chfPlugin*) filter->plug->puser; @@ -523,6 +532,7 @@ static chFilterIf wrapper_fif = { NULL, /* parse_end_array, */ channel_open, + channel_register_pre_eventq_cb, channel_report, channel_close }; diff --git a/src/ioc/db/chfPlugin.h b/src/ioc/db/chfPlugin.h index 43669c4a5..07c9ff069 100644 --- a/src/ioc/db/chfPlugin.h +++ b/src/ioc/db/chfPlugin.h @@ -14,6 +14,8 @@ #include #include +struct db_field_log; + /* Based on the linkoptions utility by Michael Davidsaver (BNL) */ /** @file chfPlugin.h @@ -121,15 +123,40 @@ typedef struct chfPluginIf { /** @brief Open channel. * * Called as part of the channel connection setup. + * * @param chan dbChannel for which the connection is being made. * @param pvt Pointer to private structure. * @return 0 for success, -1 if operation is to be aborted. */ long (* channel_open) (dbChannel *chan, void *pvt); + /** @brief Register callback for pre-event-queue post_event operation. + * + * Called as part of the channel connection setup. + * + * This function is called to establish the stack of plugins that an event + * is passed through between the database and the event queue. + * + * The plugin must call the supplied 'cb_in' function (usually within + * its own event callback) with 'arg_in' as first argument to forward the + * data to the next plugin towards the event queue. + * + * @param chan dbChannel for which the connection is being made. + * @param pvt Pointer to private structure. + * @param cb_in Pointer to the next plugin's event callback + * @param arg_in Argument that must be supplied when this plugin calls + * next plugin's callback + * @param cb_out Pointer to this plugin's event callback + * @param arg_out Argument that must be supplied when calling this plugin's callback + * @return 0 for success (cb_out and arg_out written), -1 if callback not required + */ + long (* channelRegisterPreEventQueCB) (dbChannel *chan, void *pvt, chPostEventFunc* cb_in, void *arg_in, + chPostEventFunc **cb_out, void **arg_out ); + /** @brief Channel report request. * * Called as part of show... routines. + * * @param chan dbChannel for which the report is requested. * @param pvt Pointer to private structure. * @param intro Line header string to be printed at the beginning of each line. diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index ee8ac34f6..2af5f3895 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -12,6 +12,7 @@ #include "dbChannel.h" #include "dbCommon.h" #include "dbBase.h" +#include "dbEvent.h" #include "link.h" #include "dbAccessDefs.h" #include "dbLock.h" @@ -413,14 +414,32 @@ long dbChannelOpen(dbChannel *chan) { chFilter *filter; long status = 0; + chPostEventFunc *nextcb = db_post_single_event_final; + void *nextarg = NULL; + chPostEventFunc *thiscb = NULL; + void *thisarg = NULL; filter = (chFilter *) ellFirst(&chan->filters); while (filter) { status = filter->plug->fif->channel_open(filter); - if (status) - break; + if (status) goto finish; filter = (chFilter *) ellNext(&filter->node); } + + /* Build up the pre-event-queue chain */ + filter = (chFilter *) ellLast(&chan->filters); + while (filter) { + long status = filter->plug->fif->channel_register_pre_eventq_cb(filter, nextcb, nextarg, &thiscb, &thisarg); + if (status == 0) { + nextcb = thiscb; + nextarg = thisarg; + } + filter = (chFilter *) ellPrevious(&filter->node); + } + chan->post_event_cb = nextcb; + chan->post_event_arg = nextarg; + +finish: return status; } diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index 1539ee038..ce3814e0c 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -14,20 +14,45 @@ #include "epicsTypes.h" #include "errMdef.h" #include "shareLib.h" +#include "db_field_log.h" +#include "dbEvent.h" #ifdef __cplusplus extern "C" { #endif +/* + * event subscription + */ +typedef struct evSubscrip { + ELLNODE node; + struct dbChannel *chan; + EVENTFUNC *user_sub; + void *user_arg; + struct event_que *ev_que; + db_field_log **pLastLog; + unsigned long npend; /* n times this event is on the queue */ + unsigned long nreplace; /* n times replacing event on the queue */ + unsigned char select; + char useValque; + char callBackInProgress; + char enabled; +} evSubscrip; + +typedef struct chFilter chFilter; + +/* Prototype for the post event function that is called recursively in filter stacks */ +typedef void (chPostEventFunc)(void *pvt, struct evSubscrip *event, db_field_log *pLog); + /* A dbChannel points to a record field, and can have multiple filters */ typedef struct dbChannel { const char *name; dbAddr addr; + chPostEventFunc *post_event_cb; + void *post_event_arg; ELLLIST filters; } dbChannel; -typedef struct chFilter chFilter; - /* Return values from chFilterIf->parse_* routines: */ typedef enum { parse_stop, parse_continue @@ -68,6 +93,7 @@ typedef struct chFilterIf { /* Channel operations: */ long (* channel_open)(chFilter *filter); + long (* channel_register_pre_eventq_cb)(chFilter *filter, chPostEventFunc *cb_in, void *arg_in, chPostEventFunc **cb_out, void**arg_out); void (* channel_report)(chFilter *filter, const char *intro, int level); /* FIXME: More filter routines here ... */ void (* channel_close)(chFilter *filter); diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index 3c80031a0..66b96b982 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -49,24 +49,6 @@ #define EVENTQUESIZE (EVENTENTRIES * EVENTSPERQUE) #define EVENTQEMPTY ((struct evSubscrip *)NULL) -/* - * event subscruiption - */ -struct evSubscrip { - ELLNODE node; - struct dbChannel *chan; - EVENTFUNC *user_sub; - void *user_arg; - struct event_que *ev_que; - db_field_log *pLastLog; - unsigned long npend; /* n times this event is on the queue */ - unsigned long nreplace; /* n times replacing event on the queue */ - unsigned char select; - char useValque; - char callBackInProgress; - char enabled; -}; - /* * really a ring buffer */ @@ -487,6 +469,7 @@ void epicsShareAPI db_event_disable (dbEventSubscription event) /* * event_remove() * event queue lock _must_ be applied + * this nulls the entry in the queue, but doesn't delete the db_field_log chunk */ static void event_remove ( struct event_que *ev_que, unsigned short index, struct evSubscrip *placeHolder ) @@ -494,6 +477,7 @@ static void event_remove ( struct event_que *ev_que, struct evSubscrip * const pevent = ev_que->evque[index]; ev_que->evque[index] = placeHolder; + ev_que->valque[index] = NULL; if ( pevent->npend == 1u ) { pevent->pLastLog = NULL; } @@ -637,17 +621,48 @@ int epicsShareAPI db_post_extra_labor (dbEventCtx ctx) } /* - * DB_POST_SINGLE_EVENT_PRIVATE() + * DB_POST_SINGLE_EVENT_FIRST() * * NOTE: This assumes that the db scan lock is already applied + * (as it copies data from the record) */ -static void db_post_single_event_private (struct evSubscrip *event) +db_field_log* db_post_single_event_first (struct evSubscrip *pevent) { - struct event_que * const ev_que = event->ev_que; db_field_log *pLog = NULL; + + if (pevent->useValque) { + pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList); + if (pLog) { + struct dbChannel *chan = pevent->chan; + struct dbCommon *prec = dbChannelRecord(chan); + pLog->stat = prec->stat; + pLog->sevr = prec->sevr; + pLog->time = prec->time; + + /* + * use memcpy to avoid a bus error on + * union copy of char in the db at an odd + * address + */ + memcpy(&pLog->field, + dbChannelField(chan), + dbChannelElementSize(chan)); + } + } + return pLog; +} + +/* + * DB_POST_SINGLE_EVENT_FINAL() + * + */ +void db_post_single_event_final (void *pvt, evSubscrip *pevent, db_field_log *pLog) +{ + struct event_que *ev_que; int firstEventFlag; unsigned rngSpace; + ev_que = pevent->ev_que; /* * evUser ring buffer must be locked for the multiple * threads writing/reading it @@ -662,7 +677,7 @@ static void db_post_single_event_private (struct evSubscrip *event) * events (saving them without the current valuye * serves no purpose) */ - if (!event->useValque && event->npend>0u) { + if (!pevent->useValque && pevent->npend>0u) { UNLOCKEVQUE (ev_que) return; } @@ -677,13 +692,16 @@ static void db_post_single_event_private (struct evSubscrip *event) * then replace the last event on the queue (for this monitor) */ rngSpace = ringSpace ( ev_que ); - if ( event->npend>0u && + if ( pevent->npend>0u && (ev_que->evUser->flowCtrlMode || rngSpace<=EVENTSPERQUE) ) { /* * replace last event if no space is left */ - pLog = event->pLastLog; - event->nreplace++; + if (*pevent->pLastLog) { + freeListFree(dbevFieldLogFreeList, *pevent->pLastLog); + *pevent->pLastLog = pLog; + } + pevent->nreplace++; /* * the event task has already been notified about * this so we dont need to post the semaphore @@ -691,16 +709,18 @@ static void db_post_single_event_private (struct evSubscrip *event) firstEventFlag = 0; } /* - * Otherwise, the current entry must be available. + * Otherwise, the current entry must be available. * Fill it in and advance the ring buffer. */ else { assert ( ev_que->evque[ev_que->putix] == EVENTQEMPTY ); - ev_que->evque[ev_que->putix] = event; - if (event->npend>0u) { + ev_que->evque[ev_que->putix] = pevent; + ev_que->valque[ev_que->putix] = pLog; + pevent->pLastLog = &ev_que->valque[ev_que->putix]; + if (pevent->npend>0u) { ev_que->nDuplicates++; } - event->npend++; + pevent->npend++; /* * if the ring buffer was empty before * adding this event @@ -714,30 +734,6 @@ static void db_post_single_event_private (struct evSubscrip *event) ev_que->putix = RNGINC ( ev_que->putix ); } - if (event->useValque) { - if (!pLog) { - pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList); - } - if (pLog) { - struct dbChannel *chan = event->chan; - struct dbCommon *prec = dbChannelRecord(chan); - pLog->stat = prec->stat; - pLog->sevr = prec->sevr; - pLog->time = prec->time; - - /* - * use memcpy to avoid a bus error on - * union copy of char in the db at an odd - * address - */ - memcpy(& pLog->field, - dbChannelField(chan), - dbChannelElementSize(chan)); - - event->pLastLog = pLog; - } - } - UNLOCKEVQUE (ev_que) /* @@ -782,7 +778,10 @@ unsigned int caEventMask */ if ( (dbChannelField(pevent->chan) == (void *)pField || pField==NULL) && (caEventMask & pevent->select)) { - db_post_single_event_private (pevent); + /* Call the head of the filter chain */ + pevent->chan->post_event_cb(pevent->chan->post_event_arg, + pevent, + db_post_single_event_first(pevent)); } } @@ -800,7 +799,12 @@ void epicsShareAPI db_post_single_event (dbEventSubscription event) struct dbCommon * const prec = dbChannelRecord(pevent->chan); dbScanLock (prec); - db_post_single_event_private (pevent); + + /* Call the head of the filter chain */ + pevent->chan->post_event_cb(pevent->chan->post_event_arg, + pevent, + db_post_single_event_first(pevent)); + dbScanUnlock (prec); } @@ -835,6 +839,10 @@ static int event_read ( struct event_que *ev_que ) if ( pevent == &canceledEvent ) { ev_que->evque[ev_que->getix] = EVENTQEMPTY; + if (ev_que->valque[ev_que->getix]) { + freeListFree(dbevFieldLogFreeList, ev_que->valque[ev_que->getix]); + ev_que->valque[ev_que->getix] = NULL; + } ev_que->getix = RNGINC ( ev_que->getix ); assert ( ev_que->nCanceled > 0 ); ev_que->nCanceled--; diff --git a/src/ioc/db/dbEvent.h b/src/ioc/db/dbEvent.h index d93e31594..9c46b5f56 100644 --- a/src/ioc/db/dbEvent.h +++ b/src/ioc/db/dbEvent.h @@ -36,6 +36,7 @@ extern "C" { struct dbChannel; struct db_field_log; +struct evSubscrip; epicsShareFunc int epicsShareAPI db_event_list ( const char *name, unsigned level); @@ -71,6 +72,7 @@ epicsShareFunc void epicsShareAPI db_cancel_event (dbEventSubscription es); epicsShareFunc void epicsShareAPI db_post_single_event (dbEventSubscription es); epicsShareFunc void epicsShareAPI db_event_enable (dbEventSubscription es); epicsShareFunc void epicsShareAPI db_event_disable (dbEventSubscription es); +epicsShareFunc void epicsShareAPI db_post_single_event_final (void *pvt, struct evSubscrip *pevent, struct db_field_log *pLog); #define DB_EVENT_OK 0 #define DB_EVENT_ERROR (-1) diff --git a/src/ioc/db/db_field_log.h b/src/ioc/db/db_field_log.h index 2012f49f6..c38830bd9 100644 --- a/src/ioc/db/db_field_log.h +++ b/src/ioc/db/db_field_log.h @@ -14,6 +14,8 @@ #ifndef INCLdb_field_logh #define INCLdb_field_logh +#include "epicsTime.h" + #ifdef __cplusplus extern "C" { #endif @@ -49,7 +51,7 @@ typedef struct db_field_log { unsigned short sevr; /* Alarm Severity */ epicsTimeStamp time; /* time stamp */ union native_value field; /* field value */ -}db_field_log; +} db_field_log; #ifdef __cplusplus } diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index 95f0a3c79..564a81302 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -43,6 +43,8 @@ typedef struct myStruct { int sent6; char c; char c1[2]; + chPostEventFunc *callback; + void *arg; } myStruct; static const @@ -164,6 +166,15 @@ static long channel_open(dbChannel *chan, void *user) return c_open_return; } +static long channelRegisterPreEventQueCB(dbChannel *chan, void *user, chPostEventFunc *cb_in, void* arg_in, + chPostEventFunc **cb_out, void **arg_out) +{ + myStruct *my = (myStruct*)user; + my->callback = cb_in; + my->arg = arg_in; + return 0; +} + static void channel_report(dbChannel *chan, void *user, const char *intro, int level) { testOk(e & e_report, "channel_report called"); @@ -186,6 +197,7 @@ static chfPluginIf myPif = { parse_ok, channel_open, + channelRegisterPreEventQueCB, channel_report, channel_close }; diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index 68195a288..352eb2adb 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -26,8 +26,9 @@ #define e_start_array 0x00000800 #define e_end_array 0x00001000 #define e_open 0x00002000 -#define e_report 0x00004000 -#define e_close 0x00008000 +#define e_reg_pre_cb 0x00004000 +#define e_report 0x00008000 +#define e_close 0x00010000 #define r_any (e_start | e_abort | e_end | \ e_null | e_boolean | e_integer | e_double | e_string | \ @@ -114,6 +115,11 @@ long c_open(chFilter *filter) testOk(e & e_open, "channel_open called"); return 0; } +long c_reg_pre_cb(chFilter *filter, chPostEventFunc *cb_in, void *arg_in, chPostEventFunc **cb_out, void **arg_out) +{ + testOk(e & e_reg_pre_cb, "channel_register_pre_event_queue_callback called"); + return 0; +} void c_report(chFilter *filter, const char *intro, int level) { testOk(e & e_report, "channel_report called, level = %d", level); @@ -126,7 +132,7 @@ void c_close(chFilter *filter) chFilterIf testIf = { p_start, p_abort, p_end, p_null, p_boolean, p_integer, p_double, p_string, p_start_map, p_map_key, p_end_map, p_start_array, p_end_array, - c_open, c_report, c_close }; + c_open, c_reg_pre_cb, c_report, c_close }; MAIN(dbChannelTest) { From 5ea973151f963c5495518f25c98def2dc0ef7108 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:54 -0400 Subject: [PATCH 36/81] Make reference (array/string) updates use a reference type db_field_log. * Redefine db_field_log to be a union between a "value" type (as before) and a "reference" type (pointers to values, dtor for plugin array copies). * Add db_delete_field_log function that correctly destructs a db_field_log. --- src/ioc/db/dbAccess.c | 16 ++++++------- src/ioc/db/dbEvent.c | 47 +++++++++++++++++++++++++++------------ src/ioc/db/dbEvent.h | 2 ++ src/ioc/db/db_field_log.h | 26 +++++++++++++++++----- 4 files changed, 64 insertions(+), 27 deletions(-) diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index 10dc7f468..7fbfcc458 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -331,11 +331,11 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options, unsigned short *pushort = (unsigned short *)pbuffer; if(pfl!=NULL) { - *pushort++ = pfl->stat; - *pushort++ = pfl->sevr; + *pushort++ = pfl->u.v.stat; + *pushort++ = pfl->u.v.sevr; } else { - *pushort++ = pcommon->stat; - *pushort++ = pcommon->sevr; + *pushort++ = pcommon->stat; + *pushort++ = pcommon->sevr; } *pushort++ = pcommon->acks; *pushort++ = pcommon->ackt; @@ -365,8 +365,8 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options, epicsUInt32 *ptime = (epicsUInt32 *)pbuffer; if(pfl!=NULL) { - *ptime++ = pfl->time.secPastEpoch; - *ptime++ = pfl->time.nsec; + *ptime++ = pfl->u.v.time.secPastEpoch; + *ptime++ = pfl->u.v.time.nsec; } else { *ptime++ = pcommon->time.secPastEpoch; *ptime++ = pcommon->time.nsec; @@ -818,7 +818,7 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, if (pfl != NULL) { DBADDR localAddr = *paddr; /* Structure copy */ - localAddr.pfield = (char *)&pfl->field; + localAddr.pfield = (char *)&pfl->u.v.field; status = dbFastGetConvertRoutine[field_type][dbrType] (localAddr.pfield, pbuffer, &localAddr); } else { @@ -850,7 +850,7 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, } else if (pfl) { DBADDR localAddr = *paddr; /* Structure copy */ - localAddr.pfield = (char *)&pfl->field; + localAddr.pfield = (char *)&pfl->u.v.field; status = convert(&localAddr, pbuffer, n, no_elements, offset); } else { status = convert(paddr, pbuffer, n, no_elements, offset); diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index 66b96b982..6bd27dc35 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -628,25 +628,31 @@ int epicsShareAPI db_post_extra_labor (dbEventCtx ctx) */ db_field_log* db_post_single_event_first (struct evSubscrip *pevent) { - db_field_log *pLog = NULL; - - if (pevent->useValque) { - pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList); - if (pLog) { - struct dbChannel *chan = pevent->chan; - struct dbCommon *prec = dbChannelRecord(chan); - pLog->stat = prec->stat; - pLog->sevr = prec->sevr; - pLog->time = prec->time; + db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);; + if (pLog) { + struct dbChannel *chan = pevent->chan; + struct dbCommon *prec = dbChannelRecord(chan); + if (pevent->useValque) { + pLog->isValue = 1; + pLog->u.v.stat = prec->stat; + pLog->u.v.sevr = prec->sevr; + pLog->u.v.time = prec->time; /* * use memcpy to avoid a bus error on * union copy of char in the db at an odd * address */ - memcpy(&pLog->field, + memcpy(&pLog->u.v.field, dbChannelField(chan), dbChannelElementSize(chan)); + } else { + pLog->isValue = 0; + pLog->u.p.stat = &prec->stat; + pLog->u.p.sevr = &prec->sevr; + pLog->u.p.time = &prec->time; + pLog->u.p.field = dbChannelField(chan); + pLog->u.p.freeFld = NULL; } } return pLog; @@ -698,7 +704,7 @@ void db_post_single_event_final (void *pvt, evSubscrip *pevent, db_field_log *pL * replace last event if no space is left */ if (*pevent->pLastLog) { - freeListFree(dbevFieldLogFreeList, *pevent->pLastLog); + db_delete_field_log(*pevent->pLastLog); *pevent->pLastLog = pLog; } pevent->nreplace++; @@ -840,7 +846,7 @@ static int event_read ( struct event_que *ev_que ) if ( pevent == &canceledEvent ) { ev_que->evque[ev_que->getix] = EVENTQEMPTY; if (ev_que->valque[ev_que->getix]) { - freeListFree(dbevFieldLogFreeList, ev_que->valque[ev_que->getix]); + db_delete_field_log(ev_que->valque[ev_que->getix]); ev_que->valque[ev_que->getix] = NULL; } ev_que->getix = RNGINC ( ev_que->getix ); @@ -909,7 +915,7 @@ static int event_read ( struct event_que *ev_que ) } } } - if (pfl) freeListFree (dbevFieldLogFreeList, pfl); + db_delete_field_log(pfl); } UNLOCKEVQUE (ev_que) @@ -1079,3 +1085,16 @@ void epicsShareAPI db_event_flow_ctrl_mode_off (dbEventCtx ctx) printf("fc off %lu\n", tickGet()); #endif } + +/* + * db_delete_field_log() + */ +void epicsShareAPI db_delete_field_log (db_field_log *pfl) +{ + if (pfl) { + /* Free field if reference type field log and dtor is set */ + if (!pfl->isValue && pfl->u.p.freeFld) pfl->u.p.freeFld(pfl->u.p.field); + /* Free the field log chunk */ + freeListFree(dbevFieldLogFreeList, pfl); + } +} diff --git a/src/ioc/db/dbEvent.h b/src/ioc/db/dbEvent.h index 9c46b5f56..92776703d 100644 --- a/src/ioc/db/dbEvent.h +++ b/src/ioc/db/dbEvent.h @@ -74,6 +74,8 @@ epicsShareFunc void epicsShareAPI db_event_enable (dbEventSubscription es); epicsShareFunc void epicsShareAPI db_event_disable (dbEventSubscription es); epicsShareFunc void epicsShareAPI db_post_single_event_final (void *pvt, struct evSubscrip *pevent, struct db_field_log *pLog); +epicsShareFunc void epicsShareAPI db_delete_field_log (struct db_field_log *pfl); + #define DB_EVENT_OK 0 #define DB_EVENT_ERROR (-1) diff --git a/src/ioc/db/db_field_log.h b/src/ioc/db/db_field_log.h index c38830bd9..8ed545063 100644 --- a/src/ioc/db/db_field_log.h +++ b/src/ioc/db/db_field_log.h @@ -29,7 +29,7 @@ extern "C" { * will most likely change infrequently. * */ -union native_value{ +union native_value { short dbf_int; short dbf_short; float dbf_float; @@ -46,11 +46,27 @@ union native_value{ * structure to log the state of a data base field at the time * an event is triggered. */ +struct dbfl_val { + unsigned short stat; /* Alarm Status */ + unsigned short sevr; /* Alarm Severity */ + epicsTimeStamp time; /* Time stamp */ + union native_value field; /* Field value */ +}; + +struct dbfl_ref { + unsigned short *stat; /* Alarm Status */ + unsigned short *sevr; /* Alarm Severity */ + epicsTimeStamp *time; /* Time stamp */ + void (*freeFld)(void*); /* Callback to free a filter-allocated field */ + void *field; /* Field value */ +}; + typedef struct db_field_log { - unsigned short stat; /* Alarm Status */ - unsigned short sevr; /* Alarm Severity */ - epicsTimeStamp time; /* time stamp */ - union native_value field; /* field value */ + char isValue; /* is a value (*not* string/array) */ + union { + struct dbfl_val v; + struct dbfl_ref p; + } u; } db_field_log; #ifdef __cplusplus From b09a9758b0565413014bdd8839a1a58ac19bcd2e Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:54 -0400 Subject: [PATCH 37/81] Changed db_field_log types to add support for plugin-created array copies * db_field_log now is one of rec = use record, val = val inside, ref = reference inside * add field_type and no_elements (to allow plugins change type and array size) * add destructor in the ref type (called when deleting the db_field_log) * change dbEvent.c and dbAccess.c to use the new field log features --- src/ioc/db/dbAccess.c | 72 +++++++++++++++++++++------------ src/ioc/db/dbEvent.c | 36 ++++++++--------- src/ioc/db/db_field_log.h | 34 +++++++++++----- src/ioc/db/test/chfPluginTest.c | 7 ++-- 4 files changed, 90 insertions(+), 59 deletions(-) diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index 7fbfcc458..1d224f976 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -315,28 +315,36 @@ static void get_alarm(DBADDR *paddr, char **ppbuffer, return; } +/* + * This code relies on *poriginal being aligned and all increments done by the + * blocks only changing the buffer pointer in a way that does not break alignment. + */ static void getOptions(DBADDR *paddr, char **poriginal, long *options, void *pflin) { db_field_log *pfl= (db_field_log *)pflin; struct rset *prset; - short field_type=paddr->field_type; + short field_type; dbCommon *pcommon; char *pbuffer = *poriginal; + if (!pfl || pfl->type == dbfl_type_rec) + field_type = paddr->field_type; + else + field_type = pfl->field_type; prset=dbGetRset(paddr); /* Process options */ pcommon = paddr->precord; if( (*options) & DBR_STATUS ) { unsigned short *pushort = (unsigned short *)pbuffer; - if(pfl!=NULL) { - *pushort++ = pfl->u.v.stat; - *pushort++ = pfl->u.v.sevr; - } else { + if (!pfl || pfl->type == dbfl_type_rec) { *pushort++ = pcommon->stat; *pushort++ = pcommon->sevr; - } + } else { + *pushort++ = pfl->stat; + *pushort++ = pfl->sevr; + } *pushort++ = pcommon->acks; *pushort++ = pcommon->ackt; pbuffer = (char *)pushort; @@ -364,12 +372,12 @@ static void getOptions(DBADDR *paddr, char **poriginal, long *options, if( (*options) & DBR_TIME ) { epicsUInt32 *ptime = (epicsUInt32 *)pbuffer; - if(pfl!=NULL) { - *ptime++ = pfl->u.v.time.secPastEpoch; - *ptime++ = pfl->u.v.time.nsec; - } else { - *ptime++ = pcommon->time.secPastEpoch; - *ptime++ = pcommon->time.nsec; + if (!pfl || pfl->type == dbfl_type_rec) { + *ptime++ = pcommon->time.secPastEpoch; + *ptime++ = pcommon->time.nsec; + } else { + *ptime++ = pfl->time.secPastEpoch; + *ptime++ = pfl->time.nsec; } pbuffer = (char *)ptime; } @@ -778,8 +786,8 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, { char *pbuf = pbuffer; db_field_log *pfl = (db_field_log *)pflin; - short field_type = paddr->field_type; - long no_elements = paddr->no_elements; + short field_type; + long no_elements; long offset; struct rset *prset; long status = 0; @@ -789,6 +797,14 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, if (nRequest && *nRequest == 0) return 0; + if (!pfl || pfl->type == dbfl_type_rec) { + field_type = paddr->field_type; + no_elements = paddr->no_elements; + } else { + field_type = pfl->field_type; + no_elements = pfl->no_elements; + } + if (field_type >= DBF_INLINK && field_type <= DBF_FWDLINK) return getLinkValue(paddr, dbrType, pbuf, nRequest); @@ -815,15 +831,18 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, if (offset == 0 && (!nRequest || no_elements == 1)) { if (nRequest) *nRequest = 1; - if (pfl != NULL) { - DBADDR localAddr = *paddr; /* Structure copy */ - - localAddr.pfield = (char *)&pfl->u.v.field; - status = dbFastGetConvertRoutine[field_type][dbrType] - (localAddr.pfield, pbuffer, &localAddr); - } else { + if (!pfl || pfl->type == dbfl_type_rec) { status = dbFastGetConvertRoutine[field_type][dbrType] (paddr->pfield, pbuffer, paddr); + } else { + DBADDR localAddr = *paddr; /* Structure copy */ + + if (pfl->type == dbfl_type_val) + localAddr.pfield = (char *) &pfl->u.v.field; + else + localAddr.pfield = (char *) pfl->u.r.field; + status = dbFastGetConvertRoutine[field_type][dbrType] + (localAddr.pfield, pbuffer, &localAddr); } } else { long n; @@ -847,13 +866,16 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, /* convert database field and place it in the buffer */ if (n <= 0) { ;/*do nothing*/ - } else if (pfl) { + } else if (!pfl || pfl->type == dbfl_type_rec) { + status = convert(paddr, pbuffer, n, no_elements, offset); + } else { DBADDR localAddr = *paddr; /* Structure copy */ - localAddr.pfield = (char *)&pfl->u.v.field; + if (pfl->type == dbfl_type_val) + localAddr.pfield = (char *) &pfl->u.v.field; + else + localAddr.pfield = (char *) pfl->u.r.field; status = convert(&localAddr, pbuffer, n, no_elements, offset); - } else { - status = convert(paddr, pbuffer, n, no_elements, offset); } } return status; diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index 6bd27dc35..adea52b9f 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -634,10 +634,12 @@ db_field_log* db_post_single_event_first (struct evSubscrip *pevent) struct dbChannel *chan = pevent->chan; struct dbCommon *prec = dbChannelRecord(chan); if (pevent->useValque) { - pLog->isValue = 1; - pLog->u.v.stat = prec->stat; - pLog->u.v.sevr = prec->sevr; - pLog->u.v.time = prec->time; + 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 @@ -647,12 +649,7 @@ db_field_log* db_post_single_event_first (struct evSubscrip *pevent) dbChannelField(chan), dbChannelElementSize(chan)); } else { - pLog->isValue = 0; - pLog->u.p.stat = &prec->stat; - pLog->u.p.sevr = &prec->sevr; - pLog->u.p.time = &prec->time; - pLog->u.p.field = dbChannelField(chan); - pLog->u.p.freeFld = NULL; + pLog->type = dbfl_type_rec; } } return pLog; @@ -677,13 +674,15 @@ void db_post_single_event_final (void *pvt, evSubscrip *pevent, db_field_log *pL LOCKEVQUE (ev_que) /* - * if we have an event on the queue and we are - * not saving the current value (because this is a - * string or an array) then ignore duplicate - * events (saving them without the current valuye - * serves no purpose) + * 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->useValque && pevent->npend>0u) { + if (pevent->npend > 0u && + (*pevent->pLastLog)->type == dbfl_type_rec && + pLog->type == dbfl_type_rec) { + db_delete_field_log(pLog); UNLOCKEVQUE (ev_que) return; } @@ -860,9 +859,6 @@ static int event_read ( struct event_que *ev_que ) * communication. (for other types they get whatever happens * to be there upon wakeup) */ - if ( !pevent->useValque ) { - pfl = NULL; - } event_remove ( ev_que, ev_que->getix, EVENTQEMPTY ); ev_que->getix = RNGINC ( ev_que->getix ); @@ -1093,7 +1089,7 @@ void epicsShareAPI db_delete_field_log (db_field_log *pfl) { if (pfl) { /* Free field if reference type field log and dtor is set */ - if (!pfl->isValue && pfl->u.p.freeFld) pfl->u.p.freeFld(pfl->u.p.field); + if (pfl->type == dbfl_type_ref && pfl->u.r.dtor) pfl->u.r.dtor(pfl); /* Free the field log chunk */ freeListFree(dbevFieldLogFreeList, pfl); } diff --git a/src/ioc/db/db_field_log.h b/src/ioc/db/db_field_log.h index 8ed545063..a01a21152 100644 --- a/src/ioc/db/db_field_log.h +++ b/src/ioc/db/db_field_log.h @@ -46,26 +46,38 @@ union native_value { * structure to log the state of a data base field at the time * an event is triggered. */ +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 */ +typedef enum dbfl_type { + dbfl_type_rec = 0, + dbfl_type_val, + dbfl_type_ref +} dbfl_type; + +#define dbflTypeStr(t) (t==dbfl_type_val?"val":t==dbfl_type_rec?"rec":"ref") + struct dbfl_val { - unsigned short stat; /* Alarm Status */ - unsigned short sevr; /* Alarm Severity */ - epicsTimeStamp time; /* Time stamp */ - union native_value field; /* Field value */ + union native_value field; /* Field value */ }; struct dbfl_ref { - unsigned short *stat; /* Alarm Status */ - unsigned short *sevr; /* Alarm Severity */ - epicsTimeStamp *time; /* Time stamp */ - void (*freeFld)(void*); /* Callback to free a filter-allocated field */ - void *field; /* Field value */ + dbfl_freeFunc *dtor; /* Callback to free filter-allocated resources */ + void *pvt; /* Private pointer */ + void *field; /* Field value */ }; typedef struct db_field_log { - char isValue; /* is a value (*not* string/array) */ + enum dbfl_type type; /* type (union) selector */ + epicsTimeStamp time; /* Time stamp */ + unsigned short stat; /* Alarm Status */ + unsigned short sevr; /* Alarm Severity */ + short field_type; /* DBF type of data */ + long no_elements; /* No of array elements */ union { struct dbfl_val v; - struct dbfl_ref p; + struct dbfl_ref r; } u; } db_field_log; diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index 564a81302..043da3cf8 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -22,12 +22,13 @@ #define e_error 0x00000004 #define e_ok 0x00000008 #define e_open 0x00000010 -#define e_report 0x00000020 -#define e_close 0x00000040 +#define e_reg_pre_cb 0x00000020 +#define e_report 0x00000040 +#define e_close 0x00000080 unsigned int e, c; -#define e_any (e_alloc | e_free | e_error | e_ok | e_open | e_report | e_close) +#define e_any (e_alloc | e_free | e_error | e_ok | e_open | e_reg_pre_cb| e_report | e_close) typedef struct myStruct { int sent1; From b99975cf713dfd598704c0932dfc5a7ebe70df61 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:54 -0400 Subject: [PATCH 38/81] Allow filter plugin to change the type of data and the array size * Add field_size to db_field_log * Add final type, dbr_type, no_elements, and field size to dbChannel (plus matching access methods) * Add set-type callback chain to find out final sizes and types * Add registration calls for pre-event-queue and post-event-queue insertion --- src/ioc/db/chfPlugin.c | 35 ++++++++++-- src/ioc/db/chfPlugin.h | 83 +++++++++++++++++++++++---- src/ioc/db/dbAccess.c | 11 +++- src/ioc/db/dbAccessDefs.h | 1 + src/ioc/db/dbChannel.c | 99 +++++++++++++++++++++++++++++---- src/ioc/db/dbChannel.h | 24 +++++++- src/ioc/db/dbEvent.c | 4 +- src/ioc/db/db_field_log.h | 1 + src/ioc/db/test/chfPluginTest.c | 36 +++++++++--- src/ioc/db/test/dbChannelTest.c | 22 +++++--- src/ioc/rsrv/camessage.c | 13 +++-- 11 files changed, 273 insertions(+), 56 deletions(-) diff --git a/src/ioc/db/chfPlugin.c b/src/ioc/db/chfPlugin.c index d0ef74ab4..dc309ce7f 100644 --- a/src/ioc/db/chfPlugin.c +++ b/src/ioc/db/chfPlugin.c @@ -480,14 +480,38 @@ static long channel_open(chFilter *filter) else return 0; } -static long channel_register_pre_eventq_cb(chFilter *filter, chPostEventFunc* cb_in, void *arg_in, - chPostEventFunc **cb_out, void **arg_out) +static void channel_register_pre_eventq(chFilter *filter, + chPostEventFunc *pe_in, void *pe_arg_in, + chSetTypeFunc *st_in, void *st_arg_in, + chPostEventFunc **pe_out, void **pe_arg_out, + chSetTypeFunc **st_out, void **st_arg_out) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; - if (p->pif->channelRegisterPreEventQueCB) return p->pif->channelRegisterPreEventQueCB(filter->chan, f->puser, cb_in, arg_in, cb_out, arg_out); - else return -1; + if (p->pif->channelRegisterPreEventQue) + p->pif->channelRegisterPreEventQue(filter->chan, f->puser, + pe_in, pe_arg_in, + st_in, st_arg_in, + pe_out, pe_arg_out, + st_out, st_arg_out); +} + +static void channel_register_post_eventq(chFilter *filter, + chPostEventFunc *pe_in, void *pe_arg_in, + chSetTypeFunc *st_in, void *st_arg_in, + chPostEventFunc **pe_out, void **pe_arg_out, + chSetTypeFunc **st_out, void **st_arg_out) +{ + chfPlugin *p = (chfPlugin*) filter->plug->puser; + chfFilter *f = (chfFilter*) filter->puser; + + if (p->pif->channelRegisterPostEventQue) + p->pif->channelRegisterPostEventQue(filter->chan, f->puser, + pe_in, pe_arg_in, + st_in, st_arg_in, + pe_out, pe_arg_out, + st_out, st_arg_out); } static void channel_report(chFilter *filter, const char *intro, int level) @@ -532,7 +556,8 @@ static chFilterIf wrapper_fif = { NULL, /* parse_end_array, */ channel_open, - channel_register_pre_eventq_cb, + channel_register_pre_eventq, + channel_register_post_eventq, channel_report, channel_close }; diff --git a/src/ioc/db/chfPlugin.h b/src/ioc/db/chfPlugin.h index 07c9ff069..c55d4bfe2 100644 --- a/src/ioc/db/chfPlugin.h +++ b/src/ioc/db/chfPlugin.h @@ -130,28 +130,87 @@ typedef struct chfPluginIf { */ long (* channel_open) (dbChannel *chan, void *pvt); - /** @brief Register callback for pre-event-queue post_event operation. + /** @brief Register callbacks for pre-event-queue operation. * * Called as part of the channel connection setup. * * This function is called to establish the stack of plugins that an event * is passed through between the database and the event queue. * - * The plugin must call the supplied 'cb_in' function (usually within - * its own event callback) with 'arg_in' as first argument to forward the - * data to the next plugin towards the event queue. + * The plugin must call the supplied 'pe_in' function (usually within + * its own post-event callback) with 'pe_arg_in' as first argument to forward + * the data to the next plugin towards the event queue. + * It must set pe_out to point to its own post-event callback in order to be + * called when a data update is sent from the database towards the event queue. + * + * The plugin must call the supplied 'st_in' function (from within + * its own set-type callback) with 'st_arg_in' as first argument after changing + * the data type and/or array size of 'chan'. * * @param chan dbChannel for which the connection is being made. * @param pvt Pointer to private structure. - * @param cb_in Pointer to the next plugin's event callback - * @param arg_in Argument that must be supplied when this plugin calls - * next plugin's callback - * @param cb_out Pointer to this plugin's event callback - * @param arg_out Argument that must be supplied when calling this plugin's callback - * @return 0 for success (cb_out and arg_out written), -1 if callback not required + * @param pe_in Pointer to the next plugin's post-event callback + * @param pe_arg_in Argument that must be supplied when this plugin calls + * the next plugin's post-event callback + * @param st_in Pointer to the next plugin's set-type callback + * @param st_arg_in Argument that must be supplied when this plugin calls + * the next plugin's set-type callback + * @param pe_out Pointer to this plugin's post-event callback (set to NULL to bypass + * this plugin) + * @param pe_arg_out Argument that must be supplied when calling + * this plugin's post-event callback + * @param st_out Pointer to this plugin's set-type callback (set to NULL if plugin + * does not change the data type or size) + * @param st_arg_out Argument that must be supplied when calling + * this plugin's set-type callback */ - long (* channelRegisterPreEventQueCB) (dbChannel *chan, void *pvt, chPostEventFunc* cb_in, void *arg_in, - chPostEventFunc **cb_out, void **arg_out ); + void (* channelRegisterPreEventQue) (dbChannel *chan, void *pvt, + chPostEventFunc *pe_in, void *pe_arg_in, + chSetTypeFunc *st_in, void *st_arg_in, + chPostEventFunc **pe_out, void **pe_arg_out, + chSetTypeFunc **st_out, void **st_arg_out); + + /** @brief Register callbacks for post-event-queue operation. + * + * Called as part of the channel connection setup. + * + * This function is called to establish the stack of plugins that an event + * is passed through between the event queue and the final user (CA server or + * database access). + * + * The plugin must call the supplied 'pe_in' function (usually within + * its own post-event callback) with 'arg_in' as first argument to forward + * the data to the next plugin towards the final user. + * It must set pe_out to point to its own post-event callback in order to be + * called when a data update is sent from the event queue towards the final + * user. + * + * The plugin must call the supplied 'st_in' function (from within + * its own set-type callback) with 'arg_in' as first argument after changing + * the data type and/or array size of 'chan'. + * + * @param chan dbChannel for which the connection is being made. + * @param pvt Pointer to private structure. + * @param pe_in Pointer to the next plugin's post-event callback + * @param pe_arg_in Argument that must be supplied when this plugin calls + * the next plugin's post-event callback + * @param st_in Pointer to the next plugin's set-type callback + * @param st_arg_in Argument that must be supplied when this plugin calls + * the next plugin's set-type callback + * @param pe_out Pointer to this plugin's post-event callback (set to NULL to bypass + * this plugin) + * @param pe_arg_out Argument that must be supplied when calling + * this plugin's post-event callback + * @param st_out Pointer to this plugin's set-type callback (set to NULL if plugin + * does not change the data type or size) + * @param st_arg_out Argument that must be supplied when calling + * this plugin's set-type callback + */ + void (* channelRegisterPostEventQue) (dbChannel *chan, void *pvt, + chPostEventFunc *pe_in, void *pe_arg_in, + chSetTypeFunc *st_in, void *st_arg_in, + chPostEventFunc **pe_out, void **pe_arg_out, + chSetTypeFunc **st_out, void **st_arg_out); /** @brief Channel report request. * diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index 1d224f976..50e95f331 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -821,7 +821,8 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, } /* check for array */ - if (paddr->special == SPC_DBADDR && + if ((!pfl || pfl->type == dbfl_type_rec) && + paddr->special == SPC_DBADDR && no_elements > 1 && (prset = dbGetRset(paddr)) && prset->get_array_info) { @@ -836,7 +837,9 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, (paddr->pfield, pbuffer, paddr); } else { DBADDR localAddr = *paddr; /* Structure copy */ - + localAddr.field_type = pfl->field_type; + localAddr.field_size = pfl->field_size; + localAddr.no_elements = pfl->no_elements; if (pfl->type == dbfl_type_val) localAddr.pfield = (char *) &pfl->u.v.field; else @@ -870,7 +873,9 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, status = convert(paddr, pbuffer, n, no_elements, offset); } else { DBADDR localAddr = *paddr; /* Structure copy */ - + localAddr.field_type = pfl->field_type; + localAddr.field_size = pfl->field_size; + localAddr.no_elements = pfl->no_elements; if (pfl->type == dbfl_type_val) localAddr.pfield = (char *) &pfl->u.v.field; else diff --git a/src/ioc/db/dbAccessDefs.h b/src/ioc/db/dbAccessDefs.h index 81fe939a4..ffbab3a2d 100644 --- a/src/ioc/db/dbAccessDefs.h +++ b/src/ioc/db/dbAccessDefs.h @@ -19,6 +19,7 @@ #include "epicsTypes.h" #include "epicsTime.h" +#include "dbBase.h" #ifdef INCLdb_accessh_epicsExportSharedSymbols # define epicsExportSharedSymbols diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index 2af5f3895..b7f68edfe 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -24,6 +24,9 @@ #include "special.h" #include "yajl_parse.h" +/* The following is defined in db_convert.h */ +extern unsigned short dbDBRnewToDBRold[DBR_ENUM+1]; + typedef struct parseContext { dbChannel *chan; chFilter *filter; @@ -410,14 +413,31 @@ finish: return chan; } +static void setFinalType(void *pvt, long no_elements, short field_type, short element_size) { + dbChannel *chan = (dbChannel*) pvt; + + chan->final_no_elements = no_elements; + chan->final_element_size = element_size; + chan->final_type = field_type; + chan->dbr_final_type = dbDBRnewToDBRold[mapDBFToDBR[field_type]]; +} + long dbChannelOpen(dbChannel *chan) { chFilter *filter; long status = 0; - chPostEventFunc *nextcb = db_post_single_event_final; - void *nextarg = NULL; - chPostEventFunc *thiscb = NULL; - void *thisarg = NULL; + chPostEventFunc *pre_pe_next = db_post_single_event_final; + void *pre_nextarg = NULL; + chPostEventFunc *pre_pe_this = NULL; + void *pre_thisarg = NULL; + chPostEventFunc *post_pe_next = NULL; + void *post_nextarg = NULL; + chPostEventFunc *post_pe_this = NULL; + void *post_thisarg = NULL; + chSetTypeFunc *st_next = setFinalType; + void *st_nextarg = chan; + chSetTypeFunc *st_this = NULL; + void *st_thisarg = NULL; filter = (chFilter *) ellFirst(&chan->filters); while (filter) { @@ -426,18 +446,53 @@ long dbChannelOpen(dbChannel *chan) filter = (chFilter *) ellNext(&filter->node); } - /* Build up the pre-event-queue chain */ + /* + * Build up the post-event-queue and pre-event-queue filter chains. + * Must be done top-down and separate, since there is only one callback chain + * for type/size changes. + */ filter = (chFilter *) ellLast(&chan->filters); - while (filter) { - long status = filter->plug->fif->channel_register_pre_eventq_cb(filter, nextcb, nextarg, &thiscb, &thisarg); - if (status == 0) { - nextcb = thiscb; - nextarg = thisarg; + while (filter && filter->plug->fif->channel_register_post_eventq) { + filter->plug->fif->channel_register_post_eventq(filter, + post_pe_next, post_nextarg, + st_next, st_nextarg, + &post_pe_this, &post_thisarg, + &st_this, &st_thisarg); + if (post_pe_this) { + post_pe_next = post_pe_this; + post_nextarg = post_thisarg; + } + if (st_this) { + st_next = st_this; + st_nextarg = st_thisarg; } filter = (chFilter *) ellPrevious(&filter->node); } - chan->post_event_cb = nextcb; - chan->post_event_arg = nextarg; + chan->post_event_cb = post_pe_next; + chan->post_event_arg = post_nextarg; + + filter = (chFilter *) ellLast(&chan->filters); + while (filter && filter->plug->fif->channel_register_pre_eventq) { + filter->plug->fif->channel_register_pre_eventq(filter, + pre_pe_next, pre_nextarg, + st_next, st_nextarg, + &pre_pe_this, &pre_thisarg, + &st_this, &st_thisarg); + if (pre_pe_this) { + pre_pe_next = pre_pe_this; + pre_nextarg = pre_thisarg; + } + if (st_this) { + st_next = st_this; + st_nextarg = st_thisarg; + } + filter = (chFilter *) ellPrevious(&filter->node); + } + chan->pre_event_cb = pre_pe_next; + chan->pre_event_arg = pre_nextarg; + + /* Call filter chain to determine data type/size changes */ + if (st_next) st_next(st_nextarg, chan->addr.no_elements, chan->addr.field_type, chan->addr.field_size); finish: return status; @@ -481,6 +536,26 @@ short dbChannelElementSize(dbChannel *chan) return chan->addr.field_size; } +long dbChannelFinalElements(dbChannel *chan) +{ + return chan->final_no_elements; +} + +short dbChannelFinalFieldType(dbChannel *chan) +{ + return chan->final_type; +} + +short dbChannelFinalExportType(dbChannel *chan) +{ + return chan->dbr_final_type; +} + +short dbChannelFinalElementSize(dbChannel *chan) +{ + return chan->final_element_size; +} + short dbChannelSpecial(dbChannel *chan) { return chan->addr.special; diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index ce3814e0c..6f0b992db 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -48,11 +48,20 @@ typedef void (chPostEventFunc)(void *pvt, struct evSubscrip *event, db_field_log typedef struct dbChannel { const char *name; dbAddr addr; + long final_no_elements; /* final number of elements (arrays) */ + short final_element_size; /* final size of element */ + short final_type; /* final type of database field */ + short dbr_final_type; /* final field type as seen by database request */ + chPostEventFunc *pre_event_cb; + void *pre_event_arg; chPostEventFunc *post_event_cb; void *post_event_arg; ELLLIST filters; } dbChannel; +/* Prototype for the set type function that is called recursively in filter stacks */ +typedef void (chSetTypeFunc)(void *pvt, long no_elements, short field_type, short element_size); + /* Return values from chFilterIf->parse_* routines: */ typedef enum { parse_stop, parse_continue @@ -93,7 +102,16 @@ typedef struct chFilterIf { /* Channel operations: */ long (* channel_open)(chFilter *filter); - long (* channel_register_pre_eventq_cb)(chFilter *filter, chPostEventFunc *cb_in, void *arg_in, chPostEventFunc **cb_out, void**arg_out); + void (* channel_register_pre_eventq) (chFilter *filter, + chPostEventFunc *pe_in, void *arg_pe_in, + chSetTypeFunc *st_in, void *arg_st_in, + chPostEventFunc **pe_out, void **arg_pe_out, + chSetTypeFunc **st_out, void **arg_st_out); + void (* channel_register_post_eventq)(chFilter *filter, + chPostEventFunc *pe_in, void *arg_pe_in, + chSetTypeFunc *st_in, void *arg_st_in, + chPostEventFunc **pe_out, void **arg_pe_out, + chSetTypeFunc **st_out, void **arg_st_out); void (* channel_report)(chFilter *filter, const char *intro, int level); /* FIXME: More filter routines here ... */ void (* channel_close)(chFilter *filter); @@ -128,6 +146,10 @@ epicsShareFunc long dbChannelElements(dbChannel *chan); epicsShareFunc short dbChannelFieldType(dbChannel *chan); epicsShareFunc short dbChannelExportType(dbChannel *chan); epicsShareFunc short dbChannelElementSize(dbChannel *chan); +epicsShareFunc long dbChannelFinalElements(dbChannel *chan); +epicsShareFunc short dbChannelFinalFieldType(dbChannel *chan); +epicsShareFunc short dbChannelFinalExportType(dbChannel *chan); +epicsShareFunc short dbChannelFinalElementSize(dbChannel *chan); epicsShareFunc short dbChannelSpecial(dbChannel *chan); epicsShareFunc void * dbChannelField(dbChannel *chan); epicsShareFunc long dbChannelGet(dbChannel *chan, short type, diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index adea52b9f..ff95dee37 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -784,7 +784,7 @@ unsigned int caEventMask if ( (dbChannelField(pevent->chan) == (void *)pField || pField==NULL) && (caEventMask & pevent->select)) { /* Call the head of the filter chain */ - pevent->chan->post_event_cb(pevent->chan->post_event_arg, + pevent->chan->pre_event_cb(pevent->chan->pre_event_arg, pevent, db_post_single_event_first(pevent)); } @@ -806,7 +806,7 @@ void epicsShareAPI db_post_single_event (dbEventSubscription event) dbScanLock (prec); /* Call the head of the filter chain */ - pevent->chan->post_event_cb(pevent->chan->post_event_arg, + pevent->chan->pre_event_cb(pevent->chan->pre_event_arg, pevent, db_post_single_event_first(pevent)); diff --git a/src/ioc/db/db_field_log.h b/src/ioc/db/db_field_log.h index a01a21152..363db8eb5 100644 --- a/src/ioc/db/db_field_log.h +++ b/src/ioc/db/db_field_log.h @@ -74,6 +74,7 @@ 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 */ union { struct dbfl_val v; diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index 043da3cf8..27fa9a35e 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -44,8 +44,14 @@ typedef struct myStruct { int sent6; char c; char c1[2]; - chPostEventFunc *callback; - void *arg; + chPostEventFunc *pe_pre; + void *pe_pre_arg; + chPostEventFunc *pe_post; + void *pe_post_arg; + chPostEventFunc *st_pre; + void *st_pre_arg; + chPostEventFunc *st_post; + void *st_post_arg; } myStruct; static const @@ -167,13 +173,26 @@ static long channel_open(dbChannel *chan, void *user) return c_open_return; } -static long channelRegisterPreEventQueCB(dbChannel *chan, void *user, chPostEventFunc *cb_in, void* arg_in, - chPostEventFunc **cb_out, void **arg_out) +static void channelRegisterPreEventQue(dbChannel *chan, void *user, + chPostEventFunc *pe_in, void *pe_arg_in, + chSetTypeFunc *st_in, void *st_arg_in, + chPostEventFunc **pe_out, void **pe_arg_out, + chSetTypeFunc **st_out, void **st_arg_out) { myStruct *my = (myStruct*)user; - my->callback = cb_in; - my->arg = arg_in; - return 0; + my->pe_pre = pe_in; + my->pe_pre_arg = pe_arg_in; +} + +static void channelRegisterPostEventQue(dbChannel *chan, void *user, + chPostEventFunc *pe_in, void *pe_arg_in, + chSetTypeFunc *st_in, void *st_arg_in, + chPostEventFunc **pe_out, void **pe_arg_out, + chSetTypeFunc **st_out, void **st_arg_out) +{ + myStruct *my = (myStruct*)user; + my->pe_post = pe_in; + my->pe_post_arg = pe_arg_in; } static void channel_report(dbChannel *chan, void *user, const char *intro, int level) @@ -198,7 +217,8 @@ static chfPluginIf myPif = { parse_ok, channel_open, - channelRegisterPreEventQueCB, + channelRegisterPreEventQue, + channelRegisterPostEventQue, channel_report, channel_close }; diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index 352eb2adb..49edad4e0 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -26,9 +26,10 @@ #define e_start_array 0x00000800 #define e_end_array 0x00001000 #define e_open 0x00002000 -#define e_reg_pre_cb 0x00004000 -#define e_report 0x00008000 -#define e_close 0x00010000 +#define e_reg_pre 0x00004000 +#define e_reg_post 0x00008000 +#define e_report 0x00010000 +#define e_close 0x00020000 #define r_any (e_start | e_abort | e_end | \ e_null | e_boolean | e_integer | e_double | e_string | \ @@ -115,10 +116,17 @@ long c_open(chFilter *filter) testOk(e & e_open, "channel_open called"); return 0; } -long c_reg_pre_cb(chFilter *filter, chPostEventFunc *cb_in, void *arg_in, chPostEventFunc **cb_out, void **arg_out) +void c_reg_pre(chFilter *filter, + chPostEventFunc *pe_in, void *pe_arg_in, chSetTypeFunc *st_in, void *st_arg_in, + chPostEventFunc **pe_out, void **arg_out, chSetTypeFunc **st_out, void **st_arg_out) { - testOk(e & e_reg_pre_cb, "channel_register_pre_event_queue_callback called"); - return 0; + testOk(e & e_reg_pre, "channel_register_pre_event_queue called"); +} +void c_reg_post(chFilter *filter, + chPostEventFunc *pe_in, void *pe_arg_in, chSetTypeFunc *st_in, void *st_arg_in, + chPostEventFunc **pe_out, void **arg_out, chSetTypeFunc **st_out, void **st_arg_out) +{ + testOk(e & e_reg_post, "channel_register_post_event_queue called"); } void c_report(chFilter *filter, const char *intro, int level) { @@ -132,7 +140,7 @@ void c_close(chFilter *filter) chFilterIf testIf = { p_start, p_abort, p_end, p_null, p_boolean, p_integer, p_double, p_string, p_start_map, p_map_key, p_end_map, p_start_array, p_end_array, - c_open, c_reg_pre_cb, c_report, c_close }; + c_open, c_reg_pre, c_reg_post, c_report, c_close }; MAIN(dbChannelTest) { diff --git a/src/ioc/rsrv/camessage.c b/src/ioc/rsrv/camessage.c index b3511ed2b..55ae0b15d 100644 --- a/src/ioc/rsrv/camessage.c +++ b/src/ioc/rsrv/camessage.c @@ -1200,7 +1200,7 @@ static void claim_ciu_reply ( struct channel_in_use * pciu ) ca_uint32_t nElem; long dbElem; SEND_LOCK ( pciu->client ); - dbElem = dbChannelElements(pciu->dbch); + dbElem = dbChannelFinalElements(pciu->dbch); if ( dbElem < 0 ) { nElem = 0; } @@ -1219,7 +1219,7 @@ static void claim_ciu_reply ( struct channel_in_use * pciu ) } status = cas_copy_in_header ( pciu->client, CA_PROTO_CREATE_CHAN, 0u, - dbChannelExportType(pciu->dbch), nElem, pciu->cid, + dbChannelFinalExportType(pciu->dbch), nElem, pciu->cid, pciu->sid, NULL ); if ( status == ECA_NORMAL ) { cas_commit_msg ( pciu->client, 0u ); @@ -2253,6 +2253,7 @@ static int search_reply_udp ( caHdrLargeArray *mp, void *pPayload, struct client * * New versions dont alloc the channel in response * to a search request. + * For these, allocation has been moved to claim_ciu_action(). * * m_count, m_cid are already in host format... */ @@ -2282,16 +2283,16 @@ static int search_reply_udp ( caHdrLargeArray *mp, void *pPayload, struct client return RSRV_OK; } sid = pchannel->sid; - if ( dbChannelElements(dbch) < 0 ) { + if ( dbChannelFinalElements(dbch) < 0 ) { count = 0; } - else if ( dbChannelElements(dbch) > 0xffff ) { + else if ( dbChannelFinalElements(dbch) > 0xffff ) { count = 0xfffe; } else { - count = (ca_uint16_t) dbChannelElements(dbch); + count = (ca_uint16_t) dbChannelFinalElements(dbch); } - type = (ca_uint16_t) dbChannelExportType(dbch); + type = (ca_uint16_t) dbChannelFinalExportType(dbch); } SEND_LOCK ( client ); From c41fcef260f4619b3d253508458bbbb51bc79bcb Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:55 -0400 Subject: [PATCH 39/81] Restructured pre- and post-event queue chains, type/size change detection * Add new db_field_log type "probe" for type change detection * Move linked lists for pre- and post-event-queue chains into dbChannel, function pointers and arguments into chFilter * Remove set-type-chain completely * Simplify register functions in filter and plugin interfaces * Add functions to run the pre and post chains to dbChannel * Refactor in db_field_log: field_size -> element_size * Refactor in dbEvent: db_post_single_event_first -> db_create_event_log db_post_single_event_final -> db_queue_event_log (removed from public interface) * Change tests to compile --- src/ioc/db/chfPlugin.c | 37 +++----- src/ioc/db/chfPlugin.h | 71 ++++------------ src/ioc/db/dbAccess.c | 8 +- src/ioc/db/dbChannel.c | 144 +++++++++++++++++--------------- src/ioc/db/dbChannel.h | 37 ++++---- src/ioc/db/dbEvent.c | 26 +++--- src/ioc/db/dbEvent.h | 1 - src/ioc/db/db_field_log.h | 9 +- src/ioc/db/test/chfPluginTest.c | 30 ++----- src/ioc/db/test/dbChannelTest.c | 12 +-- 10 files changed, 153 insertions(+), 222 deletions(-) diff --git a/src/ioc/db/chfPlugin.c b/src/ioc/db/chfPlugin.c index dc309ce7f..3548b6d6e 100644 --- a/src/ioc/db/chfPlugin.c +++ b/src/ioc/db/chfPlugin.c @@ -480,38 +480,24 @@ static long channel_open(chFilter *filter) else return 0; } -static void channel_register_pre_eventq(chFilter *filter, - chPostEventFunc *pe_in, void *pe_arg_in, - chSetTypeFunc *st_in, void *st_arg_in, - chPostEventFunc **pe_out, void **pe_arg_out, - chSetTypeFunc **st_out, void **st_arg_out) +static void channel_register_pre(chFilter *filter, + chPostEventFunc **cb_out, void **arg_out) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; - if (p->pif->channelRegisterPreEventQue) - p->pif->channelRegisterPreEventQue(filter->chan, f->puser, - pe_in, pe_arg_in, - st_in, st_arg_in, - pe_out, pe_arg_out, - st_out, st_arg_out); + if (p->pif->channelRegisterPre) + p->pif->channelRegisterPre(filter->chan, f->puser, cb_out, arg_out); } -static void channel_register_post_eventq(chFilter *filter, - chPostEventFunc *pe_in, void *pe_arg_in, - chSetTypeFunc *st_in, void *st_arg_in, - chPostEventFunc **pe_out, void **pe_arg_out, - chSetTypeFunc **st_out, void **st_arg_out) +static void channel_register_post(chFilter *filter, + chPostEventFunc **cb_out, void **arg_out) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; - if (p->pif->channelRegisterPostEventQue) - p->pif->channelRegisterPostEventQue(filter->chan, f->puser, - pe_in, pe_arg_in, - st_in, st_arg_in, - pe_out, pe_arg_out, - st_out, st_arg_out); + if (p->pif->channelRegisterPost) + p->pif->channelRegisterPost(filter->chan, f->puser, cb_out, arg_out); } static void channel_report(chFilter *filter, const char *intro, int level) @@ -519,7 +505,8 @@ static void channel_report(chFilter *filter, const char *intro, int level) chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; - if (p->pif->channel_report) p->pif->channel_report(filter->chan, f->puser, intro, level); + if (p->pif->channel_report) + p->pif->channel_report(filter->chan, f->puser, intro, level); } static void channel_close(chFilter *filter) @@ -556,8 +543,8 @@ static chFilterIf wrapper_fif = { NULL, /* parse_end_array, */ channel_open, - channel_register_pre_eventq, - channel_register_post_eventq, + channel_register_pre, + channel_register_post, channel_report, channel_close }; diff --git a/src/ioc/db/chfPlugin.h b/src/ioc/db/chfPlugin.h index c55d4bfe2..e95852d41 100644 --- a/src/ioc/db/chfPlugin.h +++ b/src/ioc/db/chfPlugin.h @@ -137,38 +137,19 @@ typedef struct chfPluginIf { * This function is called to establish the stack of plugins that an event * is passed through between the database and the event queue. * - * The plugin must call the supplied 'pe_in' function (usually within - * its own post-event callback) with 'pe_arg_in' as first argument to forward - * the data to the next plugin towards the event queue. - * It must set pe_out to point to its own post-event callback in order to be - * called when a data update is sent from the database towards the event queue. - * - * The plugin must call the supplied 'st_in' function (from within - * its own set-type callback) with 'st_arg_in' as first argument after changing - * the data type and/or array size of 'chan'. + * The plugin must set pe_out to point to its own post-event callback in order + * to be called when a data update is sent from the database towards the + * event queue. * * @param chan dbChannel for which the connection is being made. * @param pvt Pointer to private structure. - * @param pe_in Pointer to the next plugin's post-event callback - * @param pe_arg_in Argument that must be supplied when this plugin calls - * the next plugin's post-event callback - * @param st_in Pointer to the next plugin's set-type callback - * @param st_arg_in Argument that must be supplied when this plugin calls - * the next plugin's set-type callback - * @param pe_out Pointer to this plugin's post-event callback (set to NULL to bypass - * this plugin) - * @param pe_arg_out Argument that must be supplied when calling - * this plugin's post-event callback - * @param st_out Pointer to this plugin's set-type callback (set to NULL if plugin - * does not change the data type or size) - * @param st_arg_out Argument that must be supplied when calling - * this plugin's set-type callback + * @param cb_out Pointer to this plugin's post-event callback (NULL to bypass + * this plugin). + * @param arg_out Argument that must be supplied when calling + * this plugin's post-event callback. */ - void (* channelRegisterPreEventQue) (dbChannel *chan, void *pvt, - chPostEventFunc *pe_in, void *pe_arg_in, - chSetTypeFunc *st_in, void *st_arg_in, - chPostEventFunc **pe_out, void **pe_arg_out, - chSetTypeFunc **st_out, void **st_arg_out); + void (* channelRegisterPre) (dbChannel *chan, void *pvt, + chPostEventFunc **cb_out, void **arg_out); /** @brief Register callbacks for post-event-queue operation. * @@ -178,12 +159,9 @@ typedef struct chfPluginIf { * is passed through between the event queue and the final user (CA server or * database access). * - * The plugin must call the supplied 'pe_in' function (usually within - * its own post-event callback) with 'arg_in' as first argument to forward - * the data to the next plugin towards the final user. - * It must set pe_out to point to its own post-event callback in order to be - * called when a data update is sent from the event queue towards the final - * user. + * The plugin must set pe_out to point to its own post-event callback in order + * to be called when a data update is sent from the event queue towards the + * final user. * * The plugin must call the supplied 'st_in' function (from within * its own set-type callback) with 'arg_in' as first argument after changing @@ -191,26 +169,13 @@ typedef struct chfPluginIf { * * @param chan dbChannel for which the connection is being made. * @param pvt Pointer to private structure. - * @param pe_in Pointer to the next plugin's post-event callback - * @param pe_arg_in Argument that must be supplied when this plugin calls - * the next plugin's post-event callback - * @param st_in Pointer to the next plugin's set-type callback - * @param st_arg_in Argument that must be supplied when this plugin calls - * the next plugin's set-type callback - * @param pe_out Pointer to this plugin's post-event callback (set to NULL to bypass - * this plugin) - * @param pe_arg_out Argument that must be supplied when calling - * this plugin's post-event callback - * @param st_out Pointer to this plugin's set-type callback (set to NULL if plugin - * does not change the data type or size) - * @param st_arg_out Argument that must be supplied when calling - * this plugin's set-type callback + * @param cb_out Pointer to this plugin's post-event callback (NULL to bypass + * this plugin). + * @param arg_out Argument that must be supplied when calling + * this plugin's post-event callback. */ - void (* channelRegisterPostEventQue) (dbChannel *chan, void *pvt, - chPostEventFunc *pe_in, void *pe_arg_in, - chSetTypeFunc *st_in, void *st_arg_in, - chPostEventFunc **pe_out, void **pe_arg_out, - chSetTypeFunc **st_out, void **st_arg_out); + void (* channelRegisterPost) (dbChannel *chan, void *pvt, + chPostEventFunc **cb_out, void **arg_out); /** @brief Channel report request. * diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index 50e95f331..5fabc7290 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -838,9 +838,9 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, } else { DBADDR localAddr = *paddr; /* Structure copy */ localAddr.field_type = pfl->field_type; - localAddr.field_size = pfl->field_size; + localAddr.field_size = pfl->element_size; localAddr.no_elements = pfl->no_elements; - if (pfl->type == dbfl_type_val) + if (pfl->type == dbfl_type_value) localAddr.pfield = (char *) &pfl->u.v.field; else localAddr.pfield = (char *) pfl->u.r.field; @@ -874,9 +874,9 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, } else { DBADDR localAddr = *paddr; /* Structure copy */ localAddr.field_type = pfl->field_type; - localAddr.field_size = pfl->field_size; + localAddr.field_size = pfl->element_size; localAddr.no_elements = pfl->no_elements; - if (pfl->type == dbfl_type_val) + if (pfl->type == dbfl_type_value) localAddr.pfield = (char *) &pfl->u.v.field; else localAddr.pfield = (char *) pfl->u.r.field; diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index b7f68edfe..aaf150ba7 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -346,6 +346,8 @@ dbChannel * dbChannelCreate(const char *name) chan = (dbChannel *) callocMustSucceed(1, sizeof(*chan), "dbChannelCreate"); chan->name = strdup(name); /* FIXME: free-list */ ellInit(&chan->filters); + ellInit(&chan->pre_chain); + ellInit(&chan->post_chain); paddr = &chan->addr; pflddes = dbEntry.pflddes; @@ -413,89 +415,95 @@ finish: return chan; } -static void setFinalType(void *pvt, long no_elements, short field_type, short element_size) { - dbChannel *chan = (dbChannel*) pvt; +db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn) { + chFilter *filter; + ELLNODE *node; + db_field_log *pLog = pLogIn; - chan->final_no_elements = no_elements; - chan->final_element_size = element_size; - chan->final_type = field_type; - chan->dbr_final_type = dbDBRnewToDBRold[mapDBFToDBR[field_type]]; + for (node = ellFirst(&chan->pre_chain); node; node = ellNext(node)) { + filter = CONTAINER(node, chFilter, pre_node); + pLog = filter->pre_func(filter->pre_arg, chan, pLog); + } + return pLog; +} + +db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn) { + chFilter *filter; + ELLNODE *node; + db_field_log *pLog = pLogIn; + + for (node = ellFirst(&chan->post_chain); node; node = ellNext(node)) { + filter = CONTAINER(node, chFilter, post_node); + pLog = filter->post_func(filter->post_arg, chan, pLog); + } + return pLog; } long dbChannelOpen(dbChannel *chan) { chFilter *filter; - long status = 0; - chPostEventFunc *pre_pe_next = db_post_single_event_final; - void *pre_nextarg = NULL; - chPostEventFunc *pre_pe_this = NULL; - void *pre_thisarg = NULL; - chPostEventFunc *post_pe_next = NULL; - void *post_nextarg = NULL; - chPostEventFunc *post_pe_this = NULL; - void *post_thisarg = NULL; - chSetTypeFunc *st_next = setFinalType; - void *st_nextarg = chan; - chSetTypeFunc *st_this = NULL; - void *st_thisarg = NULL; + chPostEventFunc *func; + void *arg; + long status; filter = (chFilter *) ellFirst(&chan->filters); while (filter) { - status = filter->plug->fif->channel_open(filter); - if (status) goto finish; + /* + * Call channel_open for all filters + */ + status = 0; + if (filter->plug->fif->channel_open) + status = filter->plug->fif->channel_open(filter); + if (status) return status; + + /* + * Build up the pre- and post-event-queue filter chains + */ + func = NULL; + arg = NULL; + if (filter->plug->fif->channel_register_post) { + filter->plug->fif->channel_register_post(filter, &func, &arg); + if (func) { + ellAdd(&chan->post_chain, &filter->post_node); + filter->post_func = func; + filter->post_arg = arg; + } + } + func = NULL; + arg = NULL; + if (filter->plug->fif->channel_register_pre) { + filter->plug->fif->channel_register_pre(filter, &func, &arg); + if (func) { + ellAdd(&chan->pre_chain, &filter->pre_node); + filter->pre_func = func; + filter->pre_arg = arg; + } + } + filter = (chFilter *) ellNext(&filter->node); } - /* - * Build up the post-event-queue and pre-event-queue filter chains. - * Must be done top-down and separate, since there is only one callback chain - * for type/size changes. - */ - filter = (chFilter *) ellLast(&chan->filters); - while (filter && filter->plug->fif->channel_register_post_eventq) { - filter->plug->fif->channel_register_post_eventq(filter, - post_pe_next, post_nextarg, - st_next, st_nextarg, - &post_pe_this, &post_thisarg, - &st_this, &st_thisarg); - if (post_pe_this) { - post_pe_next = post_pe_this; - post_nextarg = post_thisarg; - } - if (st_this) { - st_next = st_this; - st_nextarg = st_thisarg; - } - filter = (chFilter *) ellPrevious(&filter->node); - } - chan->post_event_cb = post_pe_next; - chan->post_event_arg = post_nextarg; + /* Set up type probe */ + db_field_log probe; + db_field_log *pfl; + probe.type = dbfl_type_probe; + probe.field_type = dbChannelFieldType(chan); + probe.no_elements = dbChannelElements(chan); + probe.element_size = dbChannelElementSize(chan); - filter = (chFilter *) ellLast(&chan->filters); - while (filter && filter->plug->fif->channel_register_pre_eventq) { - filter->plug->fif->channel_register_pre_eventq(filter, - pre_pe_next, pre_nextarg, - st_next, st_nextarg, - &pre_pe_this, &pre_thisarg, - &st_this, &st_thisarg); - if (pre_pe_this) { - pre_pe_next = pre_pe_this; - pre_nextarg = pre_thisarg; - } - if (st_this) { - st_next = st_this; - st_nextarg = st_thisarg; - } - filter = (chFilter *) ellPrevious(&filter->node); - } - chan->pre_event_cb = pre_pe_next; - chan->pre_event_arg = pre_nextarg; + /* Call filter chains to determine data type/size changes */ + pfl = dbChannelRunPreChain(chan, &probe); + if (pfl != &probe) return -1; + pfl = dbChannelRunPostChain(chan, &probe); + if (pfl != &probe) return -1; - /* Call filter chain to determine data type/size changes */ - if (st_next) st_next(st_nextarg, chan->addr.no_elements, chan->addr.field_type, chan->addr.field_size); + /* Save probe results */ + chan->final_no_elements = probe.no_elements; + chan->final_element_size = probe.element_size; + chan->final_type = probe.field_type; + chan->dbr_final_type = dbDBRnewToDBRold[mapDBFToDBR[probe.field_type]]; -finish: - return status; + return 0; } /* FIXME: For performance we should make these one-liners into macros, diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index 6f0b992db..e5c8c1040 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -41,26 +41,21 @@ typedef struct evSubscrip { typedef struct chFilter chFilter; -/* Prototype for the post event function that is called recursively in filter stacks */ -typedef void (chPostEventFunc)(void *pvt, struct evSubscrip *event, db_field_log *pLog); - /* A dbChannel points to a record field, and can have multiple filters */ typedef struct dbChannel { const char *name; - dbAddr addr; + dbAddr addr; /* address structure for record/field */ long final_no_elements; /* final number of elements (arrays) */ short final_element_size; /* final size of element */ short final_type; /* final type of database field */ short dbr_final_type; /* final field type as seen by database request */ - chPostEventFunc *pre_event_cb; - void *pre_event_arg; - chPostEventFunc *post_event_cb; - void *post_event_arg; - ELLLIST filters; + ELLLIST filters; /* list of filters as created from JSON */ + ELLLIST pre_chain; /* list of filters to be called pre-event-queue */ + ELLLIST post_chain; /* list of filters to be called post-event-queue */ } dbChannel; -/* Prototype for the set type function that is called recursively in filter stacks */ -typedef void (chSetTypeFunc)(void *pvt, long no_elements, short field_type, short element_size); +/* Prototype for the post event function that is called in filter stacks */ +typedef db_field_log* (chPostEventFunc)(void *pvt, dbChannel *chan, db_field_log *pLog); /* Return values from chFilterIf->parse_* routines: */ typedef enum { @@ -102,16 +97,8 @@ typedef struct chFilterIf { /* Channel operations: */ long (* channel_open)(chFilter *filter); - void (* channel_register_pre_eventq) (chFilter *filter, - chPostEventFunc *pe_in, void *arg_pe_in, - chSetTypeFunc *st_in, void *arg_st_in, - chPostEventFunc **pe_out, void **arg_pe_out, - chSetTypeFunc **st_out, void **arg_st_out); - void (* channel_register_post_eventq)(chFilter *filter, - chPostEventFunc *pe_in, void *arg_pe_in, - chSetTypeFunc *st_in, void *arg_st_in, - chPostEventFunc **pe_out, void **arg_pe_out, - chSetTypeFunc **st_out, void **arg_st_out); + void (* channel_register_pre) (chFilter *filter, chPostEventFunc **cb_out, void **arg_out); + void (* channel_register_post)(chFilter *filter, chPostEventFunc **cb_out, void **arg_out); void (* channel_report)(chFilter *filter, const char *intro, int level); /* FIXME: More filter routines here ... */ void (* channel_close)(chFilter *filter); @@ -128,8 +115,14 @@ typedef struct chFilterPlugin { /* A chFilter holds data for a single filter instance */ struct chFilter { ELLNODE node; + ELLNODE pre_node; + ELLNODE post_node; dbChannel *chan; const chFilterPlugin *plug; + chPostEventFunc *pre_func; + void *pre_arg; + chPostEventFunc *post_func; + void *post_arg; void *puser; }; @@ -167,6 +160,8 @@ epicsShareFunc void dbChannelFilterShow(dbChannel *chan, const char *intro, epicsShareFunc void dbChannelDelete(dbChannel *chan); epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif, void *puser); +epicsShareFunc db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn); +epicsShareFunc db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn); epicsShareFunc const chFilterPlugin * dbFindFilter(const char *key, size_t len); #ifdef __cplusplus diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index ff95dee37..c652bdd5a 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -621,20 +621,20 @@ int epicsShareAPI db_post_extra_labor (dbEventCtx ctx) } /* - * DB_POST_SINGLE_EVENT_FIRST() + * 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_post_single_event_first (struct evSubscrip *pevent) +static db_field_log* db_create_event_log (struct evSubscrip *pevent) { - db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList);; + db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList); if (pLog) { struct dbChannel *chan = pevent->chan; struct dbCommon *prec = dbChannelRecord(chan); if (pevent->useValque) { - pLog->type = dbfl_type_val; + pLog->type = dbfl_type_value; pLog->stat = prec->stat; pLog->sevr = prec->sevr; pLog->time = prec->time; @@ -656,10 +656,10 @@ db_field_log* db_post_single_event_first (struct evSubscrip *pevent) } /* - * DB_POST_SINGLE_EVENT_FINAL() + * DB_QUEUE_EVENT_LOG() * */ -void db_post_single_event_final (void *pvt, evSubscrip *pevent, db_field_log *pLog) +static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog) { struct event_que *ev_que; int firstEventFlag; @@ -783,10 +783,9 @@ unsigned int caEventMask */ if ( (dbChannelField(pevent->chan) == (void *)pField || pField==NULL) && (caEventMask & pevent->select)) { - /* Call the head of the filter chain */ - pevent->chan->pre_event_cb(pevent->chan->pre_event_arg, - pevent, - db_post_single_event_first(pevent)); + db_field_log *pLog = db_create_event_log(pevent); + pLog = dbChannelRunPreChain(pevent->chan, pLog); + db_queue_event_log(pevent, pLog); } } @@ -805,10 +804,9 @@ void epicsShareAPI db_post_single_event (dbEventSubscription event) dbScanLock (prec); - /* Call the head of the filter chain */ - pevent->chan->pre_event_cb(pevent->chan->pre_event_arg, - pevent, - db_post_single_event_first(pevent)); + db_field_log *pLog = db_create_event_log(pevent); + pLog = dbChannelRunPreChain(pevent->chan, pLog); + db_queue_event_log(pevent, pLog); dbScanUnlock (prec); } diff --git a/src/ioc/db/dbEvent.h b/src/ioc/db/dbEvent.h index 92776703d..39da157f9 100644 --- a/src/ioc/db/dbEvent.h +++ b/src/ioc/db/dbEvent.h @@ -72,7 +72,6 @@ epicsShareFunc void epicsShareAPI db_cancel_event (dbEventSubscription es); epicsShareFunc void epicsShareAPI db_post_single_event (dbEventSubscription es); epicsShareFunc void epicsShareAPI db_event_enable (dbEventSubscription es); epicsShareFunc void epicsShareAPI db_event_disable (dbEventSubscription es); -epicsShareFunc void epicsShareAPI db_post_single_event_final (void *pvt, struct evSubscrip *pevent, struct db_field_log *pLog); epicsShareFunc void epicsShareAPI db_delete_field_log (struct db_field_log *pfl); diff --git a/src/ioc/db/db_field_log.h b/src/ioc/db/db_field_log.h index 363db8eb5..fb5ebed5f 100644 --- a/src/ioc/db/db_field_log.h +++ b/src/ioc/db/db_field_log.h @@ -52,11 +52,12 @@ typedef void (dbfl_freeFunc)(struct db_field_log *pfl); /* Types of db_field_log: rec = use record, val = val inside, ref = reference inside */ typedef enum dbfl_type { dbfl_type_rec = 0, - dbfl_type_val, - dbfl_type_ref + dbfl_type_value, + dbfl_type_ref, + dbfl_type_probe } dbfl_type; -#define dbflTypeStr(t) (t==dbfl_type_val?"val":t==dbfl_type_rec?"rec":"ref") +#define dbflTypeStr(t) (t==dbfl_type_value?"val":t==dbfl_type_rec?"rec":t==dbfl_type_probe?"prb":"ref") struct dbfl_val { union native_value field; /* Field value */ @@ -74,7 +75,7 @@ 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 */ + short element_size; /* Data size */ long no_elements; /* No of array elements */ union { struct dbfl_val v; diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index 27fa9a35e..bba5f29ca 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -44,14 +44,6 @@ typedef struct myStruct { int sent6; char c; char c1[2]; - chPostEventFunc *pe_pre; - void *pe_pre_arg; - chPostEventFunc *pe_post; - void *pe_post_arg; - chPostEventFunc *st_pre; - void *st_pre_arg; - chPostEventFunc *st_post; - void *st_post_arg; } myStruct; static const @@ -173,26 +165,16 @@ static long channel_open(dbChannel *chan, void *user) return c_open_return; } -static void channelRegisterPreEventQue(dbChannel *chan, void *user, - chPostEventFunc *pe_in, void *pe_arg_in, - chSetTypeFunc *st_in, void *st_arg_in, - chPostEventFunc **pe_out, void **pe_arg_out, - chSetTypeFunc **st_out, void **st_arg_out) +static void channelRegisterPre(dbChannel *chan, void *user, + chPostEventFunc **cb_out, void **arg_out) { myStruct *my = (myStruct*)user; - my->pe_pre = pe_in; - my->pe_pre_arg = pe_arg_in; } -static void channelRegisterPostEventQue(dbChannel *chan, void *user, - chPostEventFunc *pe_in, void *pe_arg_in, - chSetTypeFunc *st_in, void *st_arg_in, - chPostEventFunc **pe_out, void **pe_arg_out, - chSetTypeFunc **st_out, void **st_arg_out) +static void channelRegisterPost(dbChannel *chan, void *user, + chPostEventFunc **cb_out, void **arg_out) { myStruct *my = (myStruct*)user; - my->pe_post = pe_in; - my->pe_post_arg = pe_arg_in; } static void channel_report(dbChannel *chan, void *user, const char *intro, int level) @@ -217,8 +199,8 @@ static chfPluginIf myPif = { parse_ok, channel_open, - channelRegisterPreEventQue, - channelRegisterPostEventQue, + channelRegisterPre, + channelRegisterPost, channel_report, channel_close }; diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index 49edad4e0..ed69d6d84 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -116,17 +116,13 @@ long c_open(chFilter *filter) testOk(e & e_open, "channel_open called"); return 0; } -void c_reg_pre(chFilter *filter, - chPostEventFunc *pe_in, void *pe_arg_in, chSetTypeFunc *st_in, void *st_arg_in, - chPostEventFunc **pe_out, void **arg_out, chSetTypeFunc **st_out, void **st_arg_out) +void c_reg_pre(chFilter *filter, chPostEventFunc **cb_out, void **arg_out) { - testOk(e & e_reg_pre, "channel_register_pre_event_queue called"); + testOk(e & e_reg_pre, "channel_register_pre called"); } -void c_reg_post(chFilter *filter, - chPostEventFunc *pe_in, void *pe_arg_in, chSetTypeFunc *st_in, void *st_arg_in, - chPostEventFunc **pe_out, void **arg_out, chSetTypeFunc **st_out, void **st_arg_out) +void c_reg_post(chFilter *filter, chPostEventFunc **cb_out, void **arg_out) { - testOk(e & e_reg_post, "channel_register_post_event_queue called"); + testOk(e & e_reg_post, "channel_register_post called"); } void c_report(chFilter *filter, const char *intro, int level) { From ec7dfb18903aecc158e9cdcaab216dbfc5734d75 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:55 -0400 Subject: [PATCH 40/81] Change mechanism to detect data type/size changes by plugins * Remove "probe" type from db_field_log * Supply probe when registering plugin chains * Minor refactoring (rename) in dbfl_type and chFilter --- src/ioc/db/chfPlugin.c | 8 ++-- src/ioc/db/chfPlugin.h | 16 ++++--- src/ioc/db/dbAccess.c | 4 +- src/ioc/db/dbChannel.c | 80 ++++++++++++++++----------------- src/ioc/db/dbChannel.h | 6 +-- src/ioc/db/dbEvent.c | 2 +- src/ioc/db/db_field_log.h | 7 ++- src/ioc/db/test/chfPluginTest.c | 4 +- src/ioc/db/test/dbChannelTest.c | 4 +- 9 files changed, 68 insertions(+), 63 deletions(-) diff --git a/src/ioc/db/chfPlugin.c b/src/ioc/db/chfPlugin.c index 3548b6d6e..f249d854b 100644 --- a/src/ioc/db/chfPlugin.c +++ b/src/ioc/db/chfPlugin.c @@ -481,23 +481,23 @@ static long channel_open(chFilter *filter) } static void channel_register_pre(chFilter *filter, - chPostEventFunc **cb_out, void **arg_out) + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; if (p->pif->channelRegisterPre) - p->pif->channelRegisterPre(filter->chan, f->puser, cb_out, arg_out); + p->pif->channelRegisterPre(filter->chan, f->puser, cb_out, arg_out, probe); } static void channel_register_post(chFilter *filter, - chPostEventFunc **cb_out, void **arg_out) + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; if (p->pif->channelRegisterPost) - p->pif->channelRegisterPost(filter->chan, f->puser, cb_out, arg_out); + p->pif->channelRegisterPost(filter->chan, f->puser, cb_out, arg_out, probe); } static void channel_report(chFilter *filter, const char *intro, int level) diff --git a/src/ioc/db/chfPlugin.h b/src/ioc/db/chfPlugin.h index e95852d41..d6762481a 100644 --- a/src/ioc/db/chfPlugin.h +++ b/src/ioc/db/chfPlugin.h @@ -141,6 +141,10 @@ typedef struct chfPluginIf { * to be called when a data update is sent from the database towards the * event queue. * + * The plugin may find out the type of data it will receive by looking at 'probe'. + * If the plugin will change the data type and/or size, it must update 'probe' + * accordingly. + * * @param chan dbChannel for which the connection is being made. * @param pvt Pointer to private structure. * @param cb_out Pointer to this plugin's post-event callback (NULL to bypass @@ -149,7 +153,8 @@ typedef struct chfPluginIf { * this plugin's post-event callback. */ void (* channelRegisterPre) (dbChannel *chan, void *pvt, - chPostEventFunc **cb_out, void **arg_out); + chPostEventFunc **cb_out, void **arg_out, + db_field_log *probe); /** @brief Register callbacks for post-event-queue operation. * @@ -163,9 +168,9 @@ typedef struct chfPluginIf { * to be called when a data update is sent from the event queue towards the * final user. * - * The plugin must call the supplied 'st_in' function (from within - * its own set-type callback) with 'arg_in' as first argument after changing - * the data type and/or array size of 'chan'. + * The plugin may find out the type of data it will receive by looking at 'probe'. + * If the plugin will change the data type and/or size, it must update 'probe' + * accordingly. * * @param chan dbChannel for which the connection is being made. * @param pvt Pointer to private structure. @@ -175,7 +180,8 @@ typedef struct chfPluginIf { * this plugin's post-event callback. */ void (* channelRegisterPost) (dbChannel *chan, void *pvt, - chPostEventFunc **cb_out, void **arg_out); + chPostEventFunc **cb_out, void **arg_out, + db_field_log *probe); /** @brief Channel report request. * diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index 5fabc7290..9a7cc4137 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -840,7 +840,7 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, localAddr.field_type = pfl->field_type; localAddr.field_size = pfl->element_size; localAddr.no_elements = pfl->no_elements; - if (pfl->type == dbfl_type_value) + if (pfl->type == dbfl_type_val) localAddr.pfield = (char *) &pfl->u.v.field; else localAddr.pfield = (char *) pfl->u.r.field; @@ -876,7 +876,7 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, localAddr.field_type = pfl->field_type; localAddr.field_size = pfl->element_size; localAddr.no_elements = pfl->no_elements; - if (pfl->type == dbfl_type_value) + if (pfl->type == dbfl_type_val) localAddr.pfield = (char *) &pfl->u.v.field; else localAddr.pfield = (char *) pfl->u.r.field; diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index aaf150ba7..ca81540a9 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -44,7 +44,7 @@ static void chf_value(parseContext *parser, parse_result *presult) parser->filter = NULL; if (filter->plug->fif->parse_end(filter) == parse_continue) { - ellAdd(&parser->chan->filters, &filter->node); + ellAdd(&parser->chan->filters, &filter->list_node); } else { free(filter); // FIXME: Use free-list *presult = parse_stop; @@ -445,57 +445,57 @@ long dbChannelOpen(dbChannel *chan) chPostEventFunc *func; void *arg; long status; + ELLNODE *node; - filter = (chFilter *) ellFirst(&chan->filters); - while (filter) { - /* - * Call channel_open for all filters - */ + for (node = ellFirst(&chan->filters); node; node = ellNext(node)) { + filter = CONTAINER(node, chFilter, list_node); + /* Call channel_open */ status = 0; if (filter->plug->fif->channel_open) status = filter->plug->fif->channel_open(filter); if (status) return status; - - /* - * Build up the pre- and post-event-queue filter chains - */ - func = NULL; - arg = NULL; - if (filter->plug->fif->channel_register_post) { - filter->plug->fif->channel_register_post(filter, &func, &arg); - if (func) { - ellAdd(&chan->post_chain, &filter->post_node); - filter->post_func = func; - filter->post_arg = arg; - } - } - func = NULL; - arg = NULL; - if (filter->plug->fif->channel_register_pre) { - filter->plug->fif->channel_register_pre(filter, &func, &arg); - if (func) { - ellAdd(&chan->pre_chain, &filter->pre_node); - filter->pre_func = func; - filter->pre_arg = arg; - } - } - - filter = (chFilter *) ellNext(&filter->node); } /* Set up type probe */ db_field_log probe; - db_field_log *pfl; - probe.type = dbfl_type_probe; + db_field_log p; probe.field_type = dbChannelFieldType(chan); probe.no_elements = dbChannelElements(chan); probe.element_size = dbChannelElementSize(chan); + p = probe; - /* Call filter chains to determine data type/size changes */ - pfl = dbChannelRunPreChain(chan, &probe); - if (pfl != &probe) return -1; - pfl = dbChannelRunPostChain(chan, &probe); - if (pfl != &probe) return -1; + /* + * Build up the pre- and post-event-queue filter chains + * Separate loops because the probe must reach the filters in the right order. + */ + for (node = ellFirst(&chan->filters); node; node = ellNext(node)) { + filter = CONTAINER(node, chFilter, list_node); + func = NULL; + arg = NULL; + if (filter->plug->fif->channel_register_pre) { + filter->plug->fif->channel_register_pre(filter, &func, &arg, &p); + if (func) { + ellAdd(&chan->pre_chain, &filter->pre_node); + filter->pre_func = func; + filter->pre_arg = arg; + probe = p; + } + } + } + for (node = ellFirst(&chan->filters); node; node = ellNext(node)) { + filter = CONTAINER(node, chFilter, list_node); + func = NULL; + arg = NULL; + if (filter->plug->fif->channel_register_post) { + filter->plug->fif->channel_register_post(filter, &func, &arg, &p); + if (func) { + ellAdd(&chan->post_chain, &filter->post_node); + filter->post_func = func; + filter->post_arg = arg; + probe = p; + } + } + } /* Save probe results */ chan->final_no_elements = probe.no_elements; @@ -634,7 +634,7 @@ void dbChannelFilterShow(dbChannel *chan, const char *intro, int level) chFilter *filter = (chFilter *) ellFirst(&chan->filters); while (filter) { filter->plug->fif->channel_report(filter, intro, level); - filter = (chFilter *) ellNext(&filter->node); + filter = (chFilter *) ellNext(&filter->list_node); } } diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index e5c8c1040..04f571fd0 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -97,8 +97,8 @@ typedef struct chFilterIf { /* Channel operations: */ long (* channel_open)(chFilter *filter); - void (* channel_register_pre) (chFilter *filter, chPostEventFunc **cb_out, void **arg_out); - void (* channel_register_post)(chFilter *filter, chPostEventFunc **cb_out, void **arg_out); + void (* channel_register_pre) (chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); + void (* channel_register_post)(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); void (* channel_report)(chFilter *filter, const char *intro, int level); /* FIXME: More filter routines here ... */ void (* channel_close)(chFilter *filter); @@ -114,7 +114,7 @@ typedef struct chFilterPlugin { /* A chFilter holds data for a single filter instance */ struct chFilter { - ELLNODE node; + ELLNODE list_node; ELLNODE pre_node; ELLNODE post_node; dbChannel *chan; diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index c652bdd5a..51ae1fec6 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -634,7 +634,7 @@ static db_field_log* db_create_event_log (struct evSubscrip *pevent) struct dbChannel *chan = pevent->chan; struct dbCommon *prec = dbChannelRecord(chan); if (pevent->useValque) { - pLog->type = dbfl_type_value; + pLog->type = dbfl_type_val; pLog->stat = prec->stat; pLog->sevr = prec->sevr; pLog->time = prec->time; diff --git a/src/ioc/db/db_field_log.h b/src/ioc/db/db_field_log.h index fb5ebed5f..49cdb9d34 100644 --- a/src/ioc/db/db_field_log.h +++ b/src/ioc/db/db_field_log.h @@ -52,12 +52,11 @@ typedef void (dbfl_freeFunc)(struct db_field_log *pfl); /* Types of db_field_log: rec = use record, val = val inside, ref = reference inside */ typedef enum dbfl_type { dbfl_type_rec = 0, - dbfl_type_value, - dbfl_type_ref, - dbfl_type_probe + dbfl_type_val, + dbfl_type_ref } dbfl_type; -#define dbflTypeStr(t) (t==dbfl_type_value?"val":t==dbfl_type_rec?"rec":t==dbfl_type_probe?"prb":"ref") +#define dbflTypeStr(t) (t==dbfl_type_val?"val":t==dbfl_type_rec?"rec":"ref") struct dbfl_val { union native_value field; /* Field value */ diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index bba5f29ca..7c2613c44 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -166,13 +166,13 @@ static long channel_open(dbChannel *chan, void *user) } static void channelRegisterPre(dbChannel *chan, void *user, - chPostEventFunc **cb_out, void **arg_out) + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { myStruct *my = (myStruct*)user; } static void channelRegisterPost(dbChannel *chan, void *user, - chPostEventFunc **cb_out, void **arg_out) + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { myStruct *my = (myStruct*)user; } diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index ed69d6d84..df49c1183 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -116,11 +116,11 @@ long c_open(chFilter *filter) testOk(e & e_open, "channel_open called"); return 0; } -void c_reg_pre(chFilter *filter, chPostEventFunc **cb_out, void **arg_out) +void c_reg_pre(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { testOk(e & e_reg_pre, "channel_register_pre called"); } -void c_reg_post(chFilter *filter, chPostEventFunc **cb_out, void **arg_out) +void c_reg_post(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { testOk(e & e_reg_post, "channel_register_post called"); } From f806551fe3ca87412ce34f792f21e9788ea6537f Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:21:56 -0400 Subject: [PATCH 41/81] Add context qualifier to db_field_log, support plugins for CA read operations --- src/ioc/db/dbEvent.c | 18 +++++++++++++++++- src/ioc/db/dbEvent.h | 2 ++ src/ioc/db/db_field_log.h | 9 ++++++++- src/ioc/rsrv/camessage.c | 31 ++++++++++++++++++++++++++++++- 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index 51ae1fec6..58ff8e5dd 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -626,13 +626,14 @@ int epicsShareAPI db_post_extra_labor (dbEventCtx ctx) * NOTE: This assumes that the db scan lock is already applied * (as it copies data from the record) */ -static db_field_log* db_create_event_log (struct evSubscrip *pevent) +db_field_log* epicsShareAPI db_create_event_log (struct evSubscrip *pevent) { 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->type = dbfl_type_val; pLog->stat = prec->stat; @@ -655,6 +656,21 @@ static db_field_log* db_create_event_log (struct evSubscrip *pevent) return pLog; } +/* + * DB_CREATE_READ_LOG() + * + */ +db_field_log* epicsShareAPI db_create_read_log (struct dbChannel *chan) +{ + db_field_log *pLog = (db_field_log *) freeListCalloc(dbevFieldLogFreeList); + + if (pLog) { + pLog->ctx = dbfl_context_read; + pLog->type = dbfl_type_rec; + } + return pLog; +} + /* * DB_QUEUE_EVENT_LOG() * diff --git a/src/ioc/db/dbEvent.h b/src/ioc/db/dbEvent.h index 39da157f9..bbd4ff06b 100644 --- a/src/ioc/db/dbEvent.h +++ b/src/ioc/db/dbEvent.h @@ -73,6 +73,8 @@ epicsShareFunc void epicsShareAPI db_post_single_event (dbEventSubscription es); epicsShareFunc void epicsShareAPI db_event_enable (dbEventSubscription es); epicsShareFunc void epicsShareAPI db_event_disable (dbEventSubscription es); +epicsShareFunc struct db_field_log* epicsShareAPI db_create_event_log (struct evSubscrip *pevent); +epicsShareFunc struct db_field_log* epicsShareAPI db_create_read_log (struct dbChannel *chan); epicsShareFunc void epicsShareAPI db_delete_field_log (struct db_field_log *pfl); #define DB_EVENT_OK 0 diff --git a/src/ioc/db/db_field_log.h b/src/ioc/db/db_field_log.h index 49cdb9d34..53ea242d7 100644 --- a/src/ioc/db/db_field_log.h +++ b/src/ioc/db/db_field_log.h @@ -56,6 +56,12 @@ typedef enum dbfl_type { 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_event +} dbfl_context; + #define dbflTypeStr(t) (t==dbfl_type_val?"val":t==dbfl_type_rec?"rec":"ref") struct dbfl_val { @@ -69,7 +75,8 @@ struct dbfl_ref { }; typedef struct db_field_log { - enum dbfl_type type; /* type (union) selector */ + enum dbfl_type type:2; /* type (union) selector */ + enum dbfl_context ctx:1; /* context (operation type) */ epicsTimeStamp time; /* Time stamp */ unsigned short stat; /* Alarm Status */ unsigned short sevr; /* Alarm Severity */ diff --git a/src/ioc/rsrv/camessage.c b/src/ioc/rsrv/camessage.c index 55ae0b15d..f43df2c77 100644 --- a/src/ioc/rsrv/camessage.c +++ b/src/ioc/rsrv/camessage.c @@ -508,6 +508,7 @@ static void read_reply ( void *pArg, struct dbChannel *dbch, int status; int v41; int autosize; + int local_fl = 0; long item_count; ca_uint32_t payload_size; dbAddr *paddr=&dbch->addr; @@ -565,8 +566,21 @@ static void read_reply ( void *pArg, struct dbChannel *dbch, return; } + /* If filters are involved in a read, create field log and run filters */ + if (!pfl && (ellCount(&dbch->pre_chain) || ellCount(&dbch->post_chain))) { + pfl = db_create_read_log(dbch); + if (pfl) { + local_fl = 1; + pfl = dbChannelRunPreChain(dbch, pfl); + pfl = dbChannelRunPostChain(dbch, pfl); + } + } + status = dbChannel_get_count ( dbch, pevext->msg.m_dataType, pPayload, &item_count, pfl); + + if (local_fl) db_delete_field_log(pfl); + if ( status < 0 ) { /* * I cant wait to redesign this protocol from scratch! @@ -647,6 +661,8 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p void *pPayload; int status; int v41; + int local_fl = 0; + db_field_log *pfl = NULL; if ( ! pciu ) { logBadId ( pClient, mp, 0 ); @@ -689,8 +705,21 @@ static int read_action ( caHdrLargeArray *mp, void *pPayloadIn, struct client *p return RSRV_OK; } + /* If filters are involved in a read, create field log and run filters */ + if (ellCount(&pciu->dbch->pre_chain) || ellCount(&pciu->dbch->post_chain)) { + pfl = db_create_read_log(pciu->dbch); + if (pfl) { + local_fl = 1; + pfl = dbChannelRunPreChain(pciu->dbch, pfl); + pfl = dbChannelRunPostChain(pciu->dbch, pfl); + } + } + status = dbChannel_get ( pciu->dbch, mp->m_dataType, - pPayload, mp->m_count, 0 ); + pPayload, mp->m_count, pfl ); + + if (local_fl) db_delete_field_log(pfl); + if ( status < 0 ) { send_err ( mp, ECA_GETFAIL, pClient, RECORD_NAME ( pciu->dbch ) ); SEND_UNLOCK ( pClient ); From 0a6ca6011ab2cc78d0b8a54e4467347e2ebffec1 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:56 -0400 Subject: [PATCH 42/81] Run post-event-queue filters for events --- src/ioc/db/dbEvent.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index 58ff8e5dd..cd9794cbf 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -901,6 +901,11 @@ static int event_read ( struct event_que *ev_que ) */ pevent->callBackInProgress = TRUE; UNLOCKEVQUE (ev_que) + /* Run post-event-queue filter chain */ + if (ellCount(&pevent->chan->post_chain)) { + dbChannelRunPostChain(pevent->chan, pfl); + } + /* Issue user callback */ ( *user_sub ) ( pevent->user_arg, pevent->chan, ev_que->evque[ev_que->getix] != EVENTQEMPTY, pfl ); LOCKEVQUE (ev_que) From 926246a49525f2ef48f9d7df3f82fd2dc119666a Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:57 -0400 Subject: [PATCH 43/81] =?UTF-8?q?=3D=3FUTF-8=3Fq=3FUpdate=3D20copyright=3D?= =?UTF-8?q?20notices,=3D20refactor=3D20in=3D20dbEvent.c:=3D20make=3D20LOCK?= =?UTF-8?q?/UNLOCK=3D20macros=3D20require=3D20=3DC2=3DA8;=3DC2=3DA8=3F=3D?= =?UTF-8?q?=20MIME-Version:=201.0=20Content-Type:=20text/plain;=20charset?= =?UTF-8?q?=3DUTF-8=20Content-Transfer-Encoding:=208bit?= --- src/ioc/db/chfPlugin.c | 4 +++ src/ioc/db/chfPlugin.h | 8 +++-- src/ioc/db/dbAccess.c | 15 ++++---- src/ioc/db/dbChannel.c | 8 +++++ src/ioc/db/dbChannel.h | 8 +++++ src/ioc/db/dbEvent.c | 63 +++++++++++++++++---------------- src/ioc/db/dbEvent.h | 12 ++++--- src/ioc/db/dbNotify.c | 20 +++++------ src/ioc/db/db_access.c | 4 +-- src/ioc/db/db_access_routines.h | 5 ++- src/ioc/db/db_field_log.h | 17 +++++---- src/ioc/db/db_test.c | 18 +++++----- src/ioc/db/recGbl.c | 4 +-- src/ioc/db/test/chfPluginTest.c | 4 +++ src/ioc/db/test/dbChannelTest.c | 14 ++++++-- src/ioc/rsrv/camessage.c | 6 +++- src/libCom/ellLib/ellLib.h | 7 ++-- 17 files changed, 131 insertions(+), 86 deletions(-) diff --git a/src/ioc/db/chfPlugin.c b/src/ioc/db/chfPlugin.c index f249d854b..86ddbc662 100644 --- a/src/ioc/db/chfPlugin.c +++ b/src/ioc/db/chfPlugin.c @@ -6,6 +6,10 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ +/* + * Author: Ralph Lange + */ + /* Based on the linkoptions utility by Michael Davidsaver (BNL) */ #include diff --git a/src/ioc/db/chfPlugin.h b/src/ioc/db/chfPlugin.h index d6762481a..29e78fe76 100644 --- a/src/ioc/db/chfPlugin.h +++ b/src/ioc/db/chfPlugin.h @@ -6,6 +6,12 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ +/* + * Author: Ralph Lange + */ + +/* Based on the linkoptions utility by Michael Davidsaver (BNL) */ + #ifndef CHFPLUGIN_H #define CHFPLUGIN_H @@ -16,8 +22,6 @@ struct db_field_log; -/* Based on the linkoptions utility by Michael Davidsaver (BNL) */ - /** @file chfPlugin.h * @brief Channel filter simplified plugins. * diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index 9a7cc4137..c6247543c 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -1,20 +1,21 @@ /*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. * Copyright (c) 2002 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. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* dbAccess.c */ -/* $Revision-Id$ */ + /* * Original Author: Bob Dalesio * Current Author: Marty Kraimer - * Date: 11-7-90 -*/ - + * Andrew Johnson + * Ralph Lange + */ #include #include diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index ca81540a9..e4c932109 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -1,10 +1,18 @@ /*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ +/* + * Author: Andrew Johnson + * Ralph Lange + */ + #include #include diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index 04f571fd0..21ba8ac96 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -1,10 +1,18 @@ /*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ +/* + * Author: Andrew Johnson + * Ralph Lange + */ + #ifndef INC_dbChannel_H #define INC_dbChannel_H diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index cd9794cbf..9b509612d 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -1,19 +1,20 @@ /*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. * Copyright (c) 2002 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. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* dbEvent.c */ -/* $Revision-Id$ */ -/* routines for scheduling events to lower priority tasks via the RT kernel */ + /* - * Author: Jeffrey O. Hill - * Date: 4-1-89 -*/ + * Author: Jeffrey O. Hill + * + * Ralph Lange + */ #include #include @@ -98,9 +99,9 @@ struct event_user { ( (unsigned short) ( (OLD) >= (EVENTQUESIZE-1) ? 0 : (OLD)+1 ) ) #define LOCKEVQUE(EV_QUE) epicsMutexMustLock((EV_QUE)->writelock) -#define UNLOCKEVQUE(EV_QUE) {epicsMutexUnlock((EV_QUE)->writelock);} +#define UNLOCKEVQUE(EV_QUE) epicsMutexUnlock((EV_QUE)->writelock) #define LOCKREC(RECPTR) epicsMutexMustLock((RECPTR)->mlok) -#define UNLOCKREC(RECPTR) {epicsMutexUnlock((RECPTR)->mlok);} +#define UNLOCKREC(RECPTR) epicsMutexUnlock((RECPTR)->mlok) static void *dbevEventUserFreeList; static void *dbevEventQueueFreeList; @@ -149,13 +150,13 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) return DB_EVENT_ERROR; } - LOCKREC (addr.precord) + LOCKREC (addr.precord); pevent = (struct evSubscrip *) ellFirst ( &addr.precord->mlis ); if ( ! pevent ) { printf ( "\"%s\": No PV event subscriptions ( monitors ).\n", pname ); - UNLOCKREC (addr.precord) + UNLOCKREC (addr.precord); return DB_EVENT_OK; } @@ -234,7 +235,7 @@ int epicsShareAPI dbel ( const char *pname, unsigned level ) pevent = (struct evSubscrip *) ellNext ( &pevent->node ); } - UNLOCKREC (addr.precord) + UNLOCKREC (addr.precord); return DB_EVENT_OK; } @@ -442,12 +443,12 @@ void epicsShareAPI db_event_enable (dbEventSubscription event) struct evSubscrip * const pevent = (struct evSubscrip *) event; struct dbCommon * const precord = dbChannelRecord(pevent->chan); - LOCKREC (precord) + LOCKREC (precord); if ( ! pevent->enabled ) { ellAdd (&precord->mlis, &pevent->node); pevent->enabled = TRUE; } - UNLOCKREC (precord) + UNLOCKREC (precord); } /* @@ -458,12 +459,12 @@ void epicsShareAPI db_event_disable (dbEventSubscription event) struct evSubscrip * const pevent = (struct evSubscrip *) event; struct dbCommon * const precord = dbChannelRecord(pevent->chan); - LOCKREC (precord) + LOCKREC (precord); if ( pevent->enabled ) { ellDelete(&precord->mlis, &pevent->node); pevent->enabled = FALSE; } - UNLOCKREC (precord) + UNLOCKREC (precord); } /* @@ -509,7 +510,7 @@ void epicsShareAPI db_cancel_event (dbEventSubscription event) * make certain that the event isnt being accessed while * its call back changes */ - LOCKEVQUE (pevent->ev_que) + LOCKEVQUE (pevent->ev_que); pevent->user_sub = NULL; @@ -542,15 +543,15 @@ void epicsShareAPI db_cancel_event (dbEventSubscription event) } else { while ( pevent->callBackInProgress ) { - UNLOCKEVQUE (pevent->ev_que) + UNLOCKEVQUE (pevent->ev_que); epicsEventMustWait ( pevent->ev_que->evUser->pflush_sem ); - LOCKEVQUE (pevent->ev_que) + LOCKEVQUE (pevent->ev_que); } } pevent->ev_que->quota -= EVENTENTRIES; - UNLOCKEVQUE (pevent->ev_que) + UNLOCKEVQUE (pevent->ev_que); freeListFree ( dbevEventSubscriptionFreeList, pevent ); @@ -687,7 +688,7 @@ static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog) * threads writing/reading it */ - LOCKEVQUE (ev_que) + LOCKEVQUE (ev_que); /* * if we have an event on the queue and both the last @@ -699,7 +700,7 @@ static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog) (*pevent->pLastLog)->type == dbfl_type_rec && pLog->type == dbfl_type_rec) { db_delete_field_log(pLog); - UNLOCKEVQUE (ev_que) + UNLOCKEVQUE (ev_que); return; } @@ -755,7 +756,7 @@ static void db_queue_event_log (evSubscrip *pevent, db_field_log *pLog) ev_que->putix = RNGINC ( ev_que->putix ); } - UNLOCKEVQUE (ev_que) + UNLOCKEVQUE (ev_que); /* * its more efficent to notify the event handler @@ -788,7 +789,7 @@ unsigned int caEventMask if (prec->mlis.count == 0) return DB_EVENT_OK; /* no monitors set */ - LOCKREC (prec) + LOCKREC (prec); for (pevent = (struct evSubscrip *) prec->mlis.node.next; pevent; pevent = (struct evSubscrip *) pevent->node.next){ @@ -805,7 +806,7 @@ unsigned int caEventMask } } - UNLOCKREC (prec) + UNLOCKREC (prec); return DB_EVENT_OK; } @@ -840,7 +841,7 @@ static int event_read ( struct event_que *ev_que ) * evUser ring buffer must be locked for the multiple * threads writing/reading it */ - LOCKEVQUE (ev_que) + LOCKEVQUE (ev_que); /* * if in flow control mode drain duplicates and then @@ -848,7 +849,7 @@ static int event_read ( struct event_que *ev_que ) * mode is over */ if ( ev_que->evUser->flowCtrlMode && ev_que->nDuplicates == 0u ) { - UNLOCKEVQUE (ev_que) + UNLOCKEVQUE (ev_que); return DB_EVENT_OK; } @@ -900,7 +901,7 @@ static int event_read ( struct event_que *ev_que ) * it. */ pevent->callBackInProgress = TRUE; - UNLOCKEVQUE (ev_que) + UNLOCKEVQUE (ev_que); /* Run post-event-queue filter chain */ if (ellCount(&pevent->chan->post_chain)) { dbChannelRunPostChain(pevent->chan, pfl); @@ -908,7 +909,7 @@ static int event_read ( struct event_que *ev_que ) /* Issue user callback */ ( *user_sub ) ( pevent->user_arg, pevent->chan, ev_que->evque[ev_que->getix] != EVENTQEMPTY, pfl ); - LOCKEVQUE (ev_que) + LOCKEVQUE (ev_que); /* * check to see if this event has been canceled each @@ -933,7 +934,7 @@ static int event_read ( struct event_que *ev_que ) db_delete_field_log(pfl); } - UNLOCKEVQUE (ev_que) + UNLOCKEVQUE (ev_que); return DB_EVENT_OK; } diff --git a/src/ioc/db/dbEvent.h b/src/ioc/db/dbEvent.h index bbd4ff06b..323a3d4e0 100644 --- a/src/ioc/db/dbEvent.h +++ b/src/ioc/db/dbEvent.h @@ -1,17 +1,19 @@ /*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. * Copyright (c) 2002 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. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ + /* - * $Revision-Id$ + * Author: Jeffrey O. Hill * - * Author: Jeff Hill - * Date: 030393 + * Ralph Lange */ #ifndef INCLdbEventh diff --git a/src/ioc/db/dbNotify.c b/src/ioc/db/dbNotify.c index 1280291d2..401bc371b 100644 --- a/src/ioc/db/dbNotify.c +++ b/src/ioc/db/dbNotify.c @@ -1,17 +1,17 @@ /*************************************************************************\ * Copyright (c) 2002 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. - * EPICS BASE Versions 3.13.7 - * and higher are distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - \*************************************************************************/ -/* dbNotify.c */ -/* base/src/db $Revision-Id$ */ +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + /* * Author: Marty Kraimer - * Date: 03-30-95 + * Andrew Johnson + * * Extracted from dbLink.c */ diff --git a/src/ioc/db/db_access.c b/src/ioc/db/db_access.c index f1ea31a01..792469676 100644 --- a/src/ioc/db/db_access.c +++ b/src/ioc/db/db_access.c @@ -7,12 +7,10 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* $Revision-Id$ */ - /* Interface between old database access and new * * Author: Marty Kraimer - * Date: 6-1-90 + * Andrew Johnson */ #include diff --git a/src/ioc/db/db_access_routines.h b/src/ioc/db/db_access_routines.h index 380afb0fa..d890f1d1c 100644 --- a/src/ioc/db/db_access_routines.h +++ b/src/ioc/db/db_access_routines.h @@ -1,10 +1,9 @@ /*************************************************************************\ -* Copyright (c) 2002 The University of Chicago, as Operator of Argonne +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found +* EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ /* base/include/db_access_routines.h */ diff --git a/src/ioc/db/db_field_log.h b/src/ioc/db/db_field_log.h index 53ea242d7..22f3108d6 100644 --- a/src/ioc/db/db_field_log.h +++ b/src/ioc/db/db_field_log.h @@ -1,16 +1,21 @@ /*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. * Copyright (c) 2002 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. -* EPICS BASE Versions 3.13.7 -* and higher are distributed subject to a Software License Agreement found -* in file LICENSE that is included with this distribution. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ -/* $Revision-Id$ - * Author: Jeffrey O. Hill - * Date: 4-1-89 + +/* + * Author: Jeffrey O. Hill + * + * Ralph Lange */ + #ifndef INCLdb_field_logh #define INCLdb_field_logh diff --git a/src/ioc/db/db_test.c b/src/ioc/db/db_test.c index 352d0b285..45a5ceca5 100644 --- a/src/ioc/db/db_test.c +++ b/src/ioc/db/db_test.c @@ -1,17 +1,17 @@ /*************************************************************************\ * Copyright (c) 2002 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. - * EPICS BASE Versions 3.13.7 - * and higher are distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. - \*************************************************************************/ -/* share/src/db @(#)db_test.c 1.10 2/3/94 */ +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE Versions 3.13.7 +* and higher are distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + /* database access subroutines */ /* * Author: Bob Dalesio - * Date: 4/15/88 + * Andrew Johnson */ #include #include diff --git a/src/ioc/db/recGbl.c b/src/ioc/db/recGbl.c index 2ccac3a07..80c55eb64 100644 --- a/src/ioc/db/recGbl.c +++ b/src/ioc/db/recGbl.c @@ -6,12 +6,10 @@ * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* recGbl.c */ -/* $Revision-Id$ */ /* * Author: Marty Kraimer - * Date: 11-7-90 + * Andrew Johnson */ #include diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index 7c2613c44..ac0e579e5 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -6,6 +6,10 @@ * in file LICENSE that is included with this distribution. \*************************************************************************/ +/* + * Author: Ralph Lange + */ + #include #include "chfPlugin.h" diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index df49c1183..c126c7625 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -1,10 +1,18 @@ /*************************************************************************\ * Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne - * National Laboratory. - * EPICS BASE is distributed subject to a Software License Agreement found - * in file LICENSE that is included with this distribution. +* National Laboratory. +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. \*************************************************************************/ +/* + * Author: Andrew Johnson + * Ralph Lange + */ + #include "dbChannel.h" #include "dbStaticLib.h" #include "dbAccessDefs.h" diff --git a/src/ioc/rsrv/camessage.c b/src/ioc/rsrv/camessage.c index f43df2c77..c52d18347 100644 --- a/src/ioc/rsrv/camessage.c +++ b/src/ioc/rsrv/camessage.c @@ -1,4 +1,7 @@ /*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. * Copyright (c) 2002 The University of Chicago, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as @@ -8,8 +11,9 @@ \*************************************************************************/ /* - * Author: Jeffrey O. Hill + * Author: Jeffrey O. Hill * + * Ralph Lange */ #include diff --git a/src/libCom/ellLib/ellLib.h b/src/libCom/ellLib/ellLib.h index 3bc167a41..ea90774cf 100644 --- a/src/libCom/ellLib/ellLib.h +++ b/src/libCom/ellLib/ellLib.h @@ -1,15 +1,16 @@ /*************************************************************************\ -* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne * National Laboratory. * Copyright (c) 2002 The Regents of the University of California, as * Operator of Los Alamos National Laboratory. * EPICS BASE is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. \*************************************************************************/ -/* $Revision-Id$ + +/* * * Author: John Winans (ANL) - * Date: 07-02-92 + * Andrew Johnson */ #ifndef INC_ellLib_H #define INC_ellLib_H From f8e9a7643a057b28edd762d84596f372bc69c04b Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:57 -0400 Subject: [PATCH 44/81] Added pvt pointer argument to parse_error in the plugin interface --- src/ioc/db/chfPlugin.c | 4 ++-- src/ioc/db/chfPlugin.h | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/chfPlugin.c b/src/ioc/db/chfPlugin.c index 86ddbc662..b667666ce 100644 --- a/src/ioc/db/chfPlugin.c +++ b/src/ioc/db/chfPlugin.c @@ -362,7 +362,7 @@ static void parse_abort(chFilter *filter) { chfFilter *f = (chfFilter*) filter->puser; /* Call the plugin to tell it we're aborting */ - if (p->pif->parse_error) p->pif->parse_error(); + if (p->pif->parse_error) p->pif->parse_error(f->puser); if (p->pif->freePvt) p->pif->freePvt(f->puser); freeInstanceData(f); } @@ -376,7 +376,7 @@ static parse_result parse_end(chFilter *filter) /* Check if all required arguments were supplied */ for(i = 0; i < (p->nopts/32)+1; i++) { if ((f->found[i] & p->required[i]) != p->required[i]) { - if (p->pif->parse_error) p->pif->parse_error(); + if (p->pif->parse_error) p->pif->parse_error(f->puser); if (p->pif->freePvt) p->pif->freePvt(f->puser); freeInstanceData(f); return parse_stop; diff --git a/src/ioc/db/chfPlugin.h b/src/ioc/db/chfPlugin.h index 29e78fe76..2564867b4 100644 --- a/src/ioc/db/chfPlugin.h +++ b/src/ioc/db/chfPlugin.h @@ -109,8 +109,10 @@ typedef struct chfPluginIf { /** @brief A parsing error occurred. * * Called after parsing failed with an error. + * + * @param pvt Pointer to private structure. */ - void (* parse_error) (void); + void (* parse_error) (void *pvt); /** @brief Configuration has been parsed successfully. * From 47e2e228ab83623bbc628ad3f69ffe23d479384e Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:57 -0400 Subject: [PATCH 45/81] Extend and add lots of tests in chfPluginTest.c * Add tests for callback registration (pre and post) * Add tests for callback execution (pre and post) * Add tests for report function and array destructor * Add test for combinations of two plugins (that register pre eventq, post eventq or in both chains), checking the order of calls and db_field_log changes independently * Make test messages more verbose and comprehensive --- src/ioc/db/test/chfPluginTest.c | 460 ++++++++++++++++++++++++++------ 1 file changed, 380 insertions(+), 80 deletions(-) diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index ac0e579e5..38bc43c6f 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -19,20 +19,30 @@ #include "testMain.h" #define PATTERN 0x55555555 +#define TYPE_START 0xAAA +#define R_INTRO "abc" +#define R_LEVEL 42 -/* Expected call bit definitions */ +/* Expected / actually run callback bit definitions */ #define e_alloc 0x00000001 #define e_free 0x00000002 #define e_error 0x00000004 #define e_ok 0x00000008 #define e_open 0x00000010 -#define e_reg_pre_cb 0x00000020 -#define e_report 0x00000040 -#define e_close 0x00000080 +#define e_reg_pre 0x00000020 +#define e_reg_post 0x00000040 +#define e_report 0x00000080 +#define e_close 0x00000100 +#define e_pre 0x00000200 +#define e_post 0x00000400 +#define e_dtor 0x00000800 -unsigned int e, c; +unsigned int e1, e2, c1, c2; +unsigned int offset; +db_field_log *dtorpfl; -#define e_any (e_alloc | e_free | e_error | e_ok | e_open | e_reg_pre_cb| e_report | e_close) +#define e_any (e_alloc | e_free | e_error | e_ok | e_open \ +| e_reg_pre | e_reg_post | e_pre | e_post | e_dtor | e_report | e_close) typedef struct myStruct { int sent1; @@ -48,6 +58,8 @@ typedef struct myStruct { int sent6; char c; char c1[2]; + int offpre; + int offpost; } myStruct; static const @@ -86,13 +98,13 @@ chfPluginArgDef sloppyOpts[] = { /* Options defs with not enough room provided */ static const chfPluginArgDef brokenOpts1[] = { - chfInt32 (myStruct, c, "i" , 0, 1), + chfInt32 (myStruct, c1, "i" , 0, 1), chfPluginArgEnd }; static const chfPluginArgDef brokenOpts2[] = { - chfDouble(myStruct, c, "d" , 0, 1), + chfDouble(myStruct, c1, "d" , 0, 1), chfPluginArgEnd }; @@ -104,13 +116,13 @@ chfPluginArgDef brokenOpts3[] = { static const chfPluginArgDef brokenOpts4[] = { - chfEnum (myStruct, c, "c" , 0, 1, colorEnum), + chfEnum (myStruct, c1, "c" , 0, 1, colorEnum), chfPluginArgEnd }; int p_ok_return = 0; int c_open_return = 0; -void *puser; +void *puser1, *puser2; static void clearStruct(void *p) { myStruct *my = (myStruct*) p; @@ -125,74 +137,230 @@ static void clearStruct(void *p) { my->enumval = 4; } +static char inst(void* user) { + return user == puser1 ? '1' : user == puser2 ? '2' : 'x'; +} + static void * allocPvt(void) { - testOk(e & e_alloc, "allocPvt called"); - c |= e_alloc; + myStruct *my = (myStruct*) calloc(1, sizeof(myStruct)); - myStruct *my = puser = (myStruct*) calloc(1, sizeof(myStruct)); + if (!puser1) { + puser1 = my; + testOk(e1 & e_alloc, "allocPvt (1) called"); + c1 |= e_alloc; + } else if (!puser2) { + puser2 = my; + testOk(e2 & e_alloc, "allocPvt (2) called"); + c2 |= e_alloc; + } clearStruct (my); return my; } static void freePvt(void *user) { - testOk(e & e_free, "freePvt called"); - c |= e_free; - testOk(user == puser, "user pointer correct"); - - free(user); - puser = NULL; + testOk(user == puser1 || user == puser2, "freePvt: user pointer valid"); + if (user == puser1) { + testOk(e1 & e_free, "freePvt (1) called"); + c1 |= e_free; + free(user); + puser1 = NULL; + } else if (user == puser2) { + testOk(e2 & e_free, "freePvt (2) called"); + c2 |= e_free; + free(user); + puser2 = NULL; + } } -static void parse_error(void) +static void parse_error(void *user) { - testOk(e & e_error, "parse_error called"); - c |= e_error; + testOk(user == puser1 || user == puser2, "parse_error: user pointer valid"); + if (user == puser1) { + testOk(e1 & e_error, "parse_error (1) called"); + c1 |= e_error; + } else if (user == puser2) { + testOk(e2 & e_error, "parse_error (2) called"); + c2 |= e_error; + } } static int parse_ok(void *user) { - testOk(e & e_ok, "parse_ok called"); - c |= e_ok; - testOk(user == puser, "user pointer correct"); + testOk(user == puser1 || user == puser2, "parse_ok: user pointer valid"); + if (user == puser1) { + testOk(e1 & e_ok, "parse_ok (1) called"); + c1 |= e_ok; + } else if (user == puser2) { + testOk(e2 & e_ok, "parse_ok (2) called"); + c2 |= e_ok; + + } return p_ok_return; } static long channel_open(dbChannel *chan, void *user) { - testOk(e & e_open, "channel_open called"); - c |= e_open; - testOk(user == puser, "user pointer correct"); + testOk(user == puser1 || user == puser2, "channel_open: user pointer valid"); + if (user == puser1) { + testOk(e1 & e_open, "channel_open (1) called"); + c1 |= e_open; + } else if (user == puser2) { + testOk(e2 & e_open, "channel_open (2) called"); + c2 |= e_open; + } return c_open_return; } +static dbfl_free1(db_field_log *pfl) { + testOk(e1 & e_dtor, "dbfl_free (1) called"); + testOk(dtorpfl == pfl, "dbfl_free (1): db_field_log pointer correct"); + dtorpfl = NULL; + c1 |= e_dtor; +} + +static dbfl_free2(db_field_log *pfl) { + testOk(e2 & e_dtor, "dbfl_free (2) called"); + testOk(dtorpfl == pfl, "dbfl_free (2): db_field_log pointer correct"); + dtorpfl = NULL; + c2 |= e_dtor; +} + +static db_field_log * pre(void *user, dbChannel *chan, db_field_log *pLog) { + myStruct *my = (myStruct*)user; + dbfl_freeFunc *dtor = NULL; + + testOk(user == puser1 || user == puser2, "pre: user pointer valid"); + if (my == puser1) { + testOk(e1 & e_pre, "pre (1) called"); + testOk(!(c2 & e_pre), "pre (2) has not been called before pre (1)"); + c1 |= e_pre; + dtor = dbfl_free1; + } else if (my == puser2) { + testOk(e2 & e_pre, "pre (2) called"); + testOk(!(e1 & e_pre) || c1 & e_pre, "pre (1) has been called before pre (2)"); + c2 |= e_pre; + dtor = dbfl_free2; + } + testOk(!(c1 & e_post), "post (1) has not been called before pre (%c)", inst(user)); + testOk(!(c2 & e_post), "post (2) has not been called before pre (%c)", inst(user)); + + if (!testOk(pLog->field_type == TYPE_START + my->offpre, "pre (%c) got field log of expected type", inst(user))) + testDiag("expected: %d, got %d", TYPE_START + my->offpre, pLog->field_type); + pLog->field_type++; + + if (my->offpre == 0) { /* The first one registers a dtor and saves pfl */ + pLog->u.r.dtor = dtor; + dtorpfl = pLog; + } + return pLog; +} + +static db_field_log * post(void *user, dbChannel *chan, db_field_log *pLog) { + myStruct *my = (myStruct*)user; + dbfl_freeFunc *dtor = NULL; + + testOk(user == puser1 || user == puser2, "post: user pointer valid"); + if (my == puser1) { + testOk(e1 & e_post, "post (1) called"); + testOk(!(c2 & e_post), "post (2) has not been called before post (1)"); + c1 |= e_post; + dtor = dbfl_free1; + } else if (my == puser2) { + testOk(e2 & e_post, "post (2) called"); + testOk(!(e1 & e_post) || c1 & e_post, "post (1) has been called before post (2)"); + c2 |= e_post; + dtor = dbfl_free2; + } + testOk(!(e1 & e_pre) || c1 & e_pre, "pre (1) has been called before post (%c)", inst(user)); + testOk(!(e2 & e_pre) || c2 & e_pre, "pre (2) has been called before post (%c)", inst(user)); + + if (!testOk(pLog->field_type == TYPE_START + my->offpost, "post (%c) got field log of expected type", inst(user))) + testDiag("expected: %d, got %d", TYPE_START + my->offpost, pLog->field_type); + pLog->field_type++; + + if (my->offpost == 0) { /* The first one registers a dtor and remembers pfl */ + pLog->u.r.dtor = dtor; + dtorpfl = pLog; + } + return pLog; +} + static void channelRegisterPre(dbChannel *chan, void *user, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { myStruct *my = (myStruct*)user; + + testOk(user == puser1 || user == puser2, "register_pre: user pointer valid"); + if (my == puser1) { + testOk(e1 & e_reg_pre, "register_pre (1) called"); + testOk(!(c2 & e_reg_pre), "register_pre (2) has not been called before register_pre (1)"); + c1 |= e_reg_pre; + } else if (my == puser2) { + testOk(e2 & e_reg_pre, "register_pre (2) called"); + testOk(!(e1 & e_reg_pre) || c1 & e_reg_pre, "register_pre (1) has been called before register_pre (2)"); + c2 |= e_reg_pre; + } + testOk(!(c1 & e_reg_post), "register_post (1) has not been called before register_pre (%c)", inst(user)); + testOk(!(c2 & e_reg_post), "register_post (2) has not been called before register_pre (%c)", inst(user)); + + my->offpre = offset++; + probe->field_type++; + *cb_out = pre; + *arg_out = user; } static void channelRegisterPost(dbChannel *chan, void *user, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { myStruct *my = (myStruct*)user; + + testOk(user == puser1 || user == puser2, "register_post: user pointer valid"); + if (my == puser1) { + testOk(e1 & e_reg_post, "register_post (1) called"); + testOk(!(c2 & e_reg_post), "register_post (2) has not been called before register_post (1)"); + c1 |= e_reg_post; + } else if (my == puser2) { + testOk(e2 & e_reg_post, "register_post (2) called"); + testOk(!(e1 & e_reg_post) || c1 & e_reg_post, "register_post (1) has been called before register_post (2)"); + c2 |= e_reg_post; + } + testOk(!(e1 & e_reg_pre) || c1 & e_reg_pre, "register_pre (1) has been called before register_post (%c)", inst(user)); + testOk(!(e2 & e_reg_pre) || c2 & e_reg_pre, "register_pre (2) has been called before register_post (%c)", inst(user)); + + my->offpost = offset++; + probe->field_type++; + *cb_out = post; + *arg_out = user; } static void channel_report(dbChannel *chan, void *user, const char *intro, int level) { - testOk(e & e_report, "channel_report called"); - c |= e_report; - testOk(user == puser, "user pointer correct"); + testOk(user == puser1 || user == puser2, "channel_report: user pointer valid"); + testOk(intro == R_INTRO, "channel_report: intro string correct"); + testOk(level == R_LEVEL - 1, "channel_report: level correct"); + if (user == puser1) { + testOk(e1 & e_report, "channel_report (1) called"); + c1 |= e_report; + } else if (user == puser2) { + testOk(e2 & e_report, "channel_report (2) called"); + c2 |= e_report; + } } static void channel_close(dbChannel *chan, void *user) { - testOk(e & e_close, "channel_close called"); - c |= e_close; - testOk(user == puser, "user pointer correct"); + testOk(user == puser1 || user == puser2, "channel_close: user pointer valid"); + if (user == puser1) { + testOk(e1 & e_close, "channel_close (1) called"); + c1 |= e_close; + } else if (user == puser2) { + testOk(e2 & e_close, "channel_close (2) called"); + c2 |= e_close; + } } static chfPluginIf myPif = { @@ -209,6 +377,34 @@ static chfPluginIf myPif = { channel_close }; +static chfPluginIf prePif = { + allocPvt, + freePvt, + + parse_error, + parse_ok, + + channel_open, + channelRegisterPre, + NULL, + channel_report, + channel_close +}; + +static chfPluginIf postPif = { + allocPvt, + freePvt, + + parse_error, + parse_ok, + + channel_open, + NULL, + channelRegisterPost, + channel_report, + channel_close +}; + static int checkValues(myStruct *my, epicsUInt32 i, int f, double d, char *s, int c) { if (!my) return 0; if (my->sent1 == PATTERN && my->sent2 == PATTERN && my->sent3 == PATTERN @@ -221,99 +417,118 @@ static int checkValues(myStruct *my, epicsUInt32 i, int f, double d, char *s, in } } +static void testHead (char* title) { + testDiag("--------------------------------------------------------"); + testDiag(title); + testDiag("--------------------------------------------------------"); +} + MAIN(chfPluginTest) { dbChannel *pch; + db_field_log *pfl; + dbEventCtx evCtx = db_init_events(); - testPlan(667); + testPlan(1481); /* Enum to string conversion */ + testHead("Enum to string conversion"); testOk(strcmp(chfPluginEnumString(colorEnum, 1, "-"), "R") == 0, "Enum to string: R"); testOk(strcmp(chfPluginEnumString(colorEnum, 2, "-"), "G") == 0, "Enum to string: G"); testOk(strcmp(chfPluginEnumString(colorEnum, 4, "-"), "B") == 0, "Enum to string: B"); testOk(strcmp(chfPluginEnumString(colorEnum, 3, "-"), "-") == 0, "Enum to string: invalid index"); + testHead("Set up database"); testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.dbx", ".:..", NULL)); testOk(!!pdbbase, "pdbbase was set"); + testHead("Try to register buggy plugins"); testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts1), "not enough storage for integer"); testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts2), "not enough storage for double"); testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts3), "not enough storage for string"); testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts4), "not enough storage for enum"); + testHead("Register plugins"); testOk(!chfPluginRegister("strict", &myPif, strictOpts), "register plugin strict"); testOk(!chfPluginRegister("noconv", &myPif, noconvOpts), "register plugin noconv"); testOk(!chfPluginRegister("sloppy", &myPif, sloppyOpts), "register plugin sloppy"); + testOk(!chfPluginRegister("pre", &prePif, sloppyOpts), "register plugin pre"); + testOk(!chfPluginRegister("post", &postPif, sloppyOpts), "register plugin post"); /* STRICT parsing: mandatory, no conversion */ /* All perfect */ - e = e_alloc | e_ok; c = 0; + testHead("STRICT parsing: all ok"); + e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "strict parsing: JSON correct"); - testOk(checkValues(puser, 1, 0, 1.2e15, "bar", 1), "guards intact, values correct"); - if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); - e = e_close | e_free; c = 0; + testOk(checkValues(puser1, 1, 0, 1.2e15, "bar", 1), "guards intact, values correct"); + if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + e1 = e_close | e_free; c1 = 0; if (pch) dbChannelDelete(pch); - testOk(!puser, "user part cleaned up"); - if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); /* Any one missing must fail */ - e = e_alloc | e_error | e_free; c = 0; + testHead("STRICT parsing: any missing parameter must fail"); + e1 = e_alloc | e_error | e_free; c1 = 0; testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), "strict parsing: c missing"); - testOk(!puser, "user part cleaned up"); - if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); - e = e_alloc | e_error | e_free; c = 0; + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + e1 = e_alloc | e_error | e_free; c1 = 0; testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"f\":false,\"i\":1,\"d\":1.2e15,\"c\":\"R\"}}")), "strict parsing: s missing"); - testOk(!puser, "user part cleaned up"); - if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); - e = e_alloc | e_error | e_free; c = 0; + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + e1 = e_alloc | e_error | e_free; c1 = 0; testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"c\":\"R\",\"f\":false,\"s\":\"bar\"}}")), "strict parsing: d missing"); - testOk(!puser, "user part cleaned up"); - if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); - e = e_alloc | e_error | e_free; c = 0; + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + e1 = e_alloc | e_error | e_free; c1 = 0; testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"d\":1.2e15,\"c\":\"R\",\"i\":1,\"s\":\"bar\"}}")), "strict parsing: f missing"); - testOk(!puser, "user part cleaned up"); - if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); - e = e_alloc | e_error | e_free; c = 0; + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + e1 = e_alloc | e_error | e_free; c1 = 0; testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"c\":\"R\",\"s\":\"bar\",\"f\":false,\"d\":1.2e15}}")), "strict parsing: i missing"); - testOk(!puser, "user part cleaned up"); - if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); /* NOCONV parsing: optional, no conversion */ /* Any one missing must leave the default intact */ - e = e_alloc | e_ok; c = 0; + testHead("NOCONV parsing: any missing parameter must fall back to default value"); + e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), "noconv parsing: c missing"); - testOk(checkValues(puser, 1, 0, 1.2e15, "bar", 4), "guards intact, values correct"); - if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); - e = e_close | e_free; c = 0; + testOk(checkValues(puser1, 1, 0, 1.2e15, "bar", 4), "guards intact, values correct"); + if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + e1 = e_close | e_free; c1 = 0; if (pch) dbChannelDelete(pch); - testOk(!puser, "user part cleaned up"); - if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + testOk(!puser1, "user part cleaned up"); + if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); - e = e_any; + e1 = e_any; testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"c\":\"R\"}}")), "noconv parsing: s missing"); - testOk(checkValues(puser, 1, 0, 1.2e15, "hello", 1), "guards intact, values correct"); + testOk(checkValues(puser1, 1, 0, 1.2e15, "hello", 1), "guards intact, values correct"); if (pch) dbChannelDelete(pch); testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"f\":false,\"s\":\"bar\",\"c\":\"R\"}}")), "noconv parsing: d missing"); - testOk(checkValues(puser, 1, 0, 1.234e5, "bar", 1), "guards intact, values correct"); + testOk(checkValues(puser1, 1, 0, 1.234e5, "bar", 1), "guards intact, values correct"); if (pch) dbChannelDelete(pch); testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "noconv parsing: f missing"); - testOk(checkValues(puser, 1, 1, 1.2e15, "bar", 1), "guards intact, values correct"); + testOk(checkValues(puser1, 1, 1, 1.2e15, "bar", 1), "guards intact, values correct"); if (pch) dbChannelDelete(pch); testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "noconv parsing: i missing"); - testOk(checkValues(puser, 12, 0, 1.2e15, "bar", 1), "guards intact, values correct"); + testOk(checkValues(puser1, 12, 0, 1.2e15, "bar", 1), "guards intact, values correct"); if (pch) dbChannelDelete(pch); /* Reject wrong types */ #define WRONGTYPETEST(Var, Val, Typ) \ - e = e_alloc | e_error | e_free; c = 0; \ + e1 = e_alloc | e_error | e_free; c1 = 0; \ testOk(!(pch = dbChannelCreate("x.{\"noconv\":{\""#Var"\":"#Val"}}")), "noconv parsing: wrong type "#Typ" for "#Var); \ - testOk(!puser, "user part cleaned up"); \ - if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + testOk(!puser1, "user part cleaned up"); \ + if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + + testHead("NOCONV parsing: rejection of wrong parameter types"); WRONGTYPETEST(i, 123.0, double); WRONGTYPETEST(i, true, boolean); @@ -334,22 +549,23 @@ MAIN(chfPluginTest) /* SLOPPY parsing: optional, with conversion */ #define CONVTESTGOOD(Var, Val, Typ, Ival, Fval, Dval, Sval, Cval) \ - e = e_alloc | e_ok; c = 0; \ + e1 = e_alloc | e_ok; c1 = 0; \ testOk(!!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), "sloppy parsing: "#Typ" (good) for "#Var); \ - testOk(checkValues(puser, Ival, Fval, Dval, Sval, Cval), "guards intact, values correct"); \ - if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); \ - e = e_close | e_free; c = 0; \ + testOk(checkValues(puser1, Ival, Fval, Dval, Sval, Cval), "guards intact, values correct"); \ + if (!testOk(c1 == e1, "create channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + e1 = e_close | e_free; c1 = 0; \ if (pch) dbChannelDelete(pch); \ - testOk(!puser, "user part cleaned up"); \ - if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + testOk(!puser1, "user part cleaned up"); \ + if (!testOk(c1 == e1, "delete channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); #define CONVTESTBAD(Var, Val, Typ) \ - e = e_alloc | e_error | e_free; c = 0; \ + e1 = e_alloc | e_error | e_free; c1 = 0; \ testOk(!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), "sloppy parsing: "#Typ" (bad) for "#Var); \ - testOk(!puser, "user part cleaned up"); \ - if (!testOk(c == e, "all expected calls happened")) testDiag("expected %#x - called %#x", e, c); + testOk(!puser1, "user part cleaned up"); \ + if (!testOk(c1 == e1, "create channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); /* To integer */ + testHead("SLOPPY parsing: conversion to integer"); CONVTESTGOOD(i, "123e4", positive string, 123, 1, 1.234e5, "hello", 4); CONVTESTGOOD(i, "-12345", negative string, -12345, 1, 1.234e5, "hello", 4); CONVTESTBAD(i, "9234567890", out-of-range string); @@ -359,10 +575,11 @@ MAIN(chfPluginTest) CONVTESTBAD(i, 34.7e14, out-of-range double); /* To boolean */ + testHead("SLOPPY parsing: conversion to boolean"); CONVTESTGOOD(f, "false", valid string, 12, 0, 1.234e5, "hello", 4); CONVTESTGOOD(f, "False", capital valid string, 12, 0, 1.234e5, "hello", 4); CONVTESTGOOD(f, "0", 0 string, 12, 0, 1.234e5, "hello", 4); - CONVTESTGOOD(f, "15", 15 string, 12, 1, 1.234e5, "hello", 4); + CONVTESTGOOD(f, "15", 15 string, 12, 1, 1.234e5, "hello", 4); /* Any one missing must leave the default intact */ CONVTESTBAD(f, ".4", invalid .4 string); CONVTESTBAD(f, "Flase", misspelled invalid string); CONVTESTGOOD(f, 0, zero integer, 12, 0, 1.234e5, "hello", 4); @@ -374,6 +591,7 @@ MAIN(chfPluginTest) CONVTESTGOOD(f, -1.24e14, negative double, 12, 1, 1.234e5, "hello", 4); /* To double */ + testHead("SLOPPY parsing: conversion to double"); CONVTESTGOOD(d, "123e4", positive double string, 12, 1, 1.23e6, "hello", 4); CONVTESTGOOD(d, "-7.89e-14", negative double string, 12, 1, -7.89e-14, "hello", 4); CONVTESTGOOD(d, "123", positive integer string, 12, 1, 123.0, "hello", 4); @@ -386,6 +604,7 @@ MAIN(chfPluginTest) CONVTESTGOOD(d, false, false boolean, 12, 1, 0.0, "hello", 4); /* To string */ + testHead("SLOPPY parsing: conversion to string"); CONVTESTGOOD(s, 12345, positive integer, 12, 1, 1.234e5, "12345", 4); CONVTESTGOOD(s, -1234567891, negative integer, 12, 1, 1.234e5, "-1234567891", 4); CONVTESTGOOD(s, true, true boolean, 12, 1, 1.234e5, "true", 4); @@ -395,12 +614,93 @@ MAIN(chfPluginTest) CONVTESTGOOD(s, -1.23456789123456789e26, large rounded negative double, 12, 1, 1.234e5, "-1.234567891235e+26", 4); /* To Enum */ + testHead("SLOPPY parsing: conversion to enum"); CONVTESTGOOD(c, 2, valid integer choice, 12, 1, 1.234e5, "hello", 2); CONVTESTBAD(c, 3, invalid integer choice); CONVTESTBAD(c, 3.2, double); CONVTESTGOOD(c, "R", valid string choice, 12, 1, 1.234e5, "hello", 1); CONVTESTBAD(c, "blubb", invalid string choice); + /* Registering and running filter callbacks */ + +#define CHAINTEST1(Type, Json, ExpReg, ExpRun, DType) \ + testHead("Filter chain test, "Type" filter"); \ + offset = 0; \ + e1 = e_alloc | e_ok; c1 = 0; \ + testOk(!!(pch = dbChannelCreate("x."Json)), "filter chains: create channel with "Type" filter"); \ + if (!testOk(c1 == e1, "create channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + e1 = e_open | ExpReg; c1 = 0; \ + testOk(!dbChannelOpen(pch), "dbChannelOpen returned channel"); \ + if (!testOk(c1 == e1, "open channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + e1 = ExpRun; c1 = 0; \ + testOk(!!(pfl = db_create_read_log(pch)), "create db_field_log"); \ + pfl->type = dbfl_type_ref; \ + pfl->field_type = TYPE_START; \ + testOk(!!(pfl = dbChannelRunPreChain(pch, pfl)), "run pre eventq chain"); \ + testOk(!!(pfl = dbChannelRunPostChain(pch, pfl)), "run post eventq chain"); \ + testOk(pfl->field_type == TYPE_START + DType, "final data type is correct"); \ + db_delete_field_log(pfl); \ + if (!testOk(c1 == e1, "run filter chains: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + e1 = e_report; c1 = 0; \ + dbChannelShow(pch, R_INTRO, R_LEVEL); \ + if (!testOk(c1 == e1, "report: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + e1 = e_close | e_free; c1 = 0; \ + if (pch) dbChannelDelete(pch); \ + if (!testOk(c1 == e1, "delete channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + +#define CHAINTEST2(Type, Json, ExpReg1, ExpRun1, ExpReg2, ExpRun2, DType) \ + testHead("Filter chain test, "Type" filters"); \ + offset = 0; \ + e1 = e_alloc | e_ok; c1 = 0; \ + e2 = e_alloc | e_ok; c2 = 0; \ + testOk(!!(pch = dbChannelCreate("x."Json)), "filter chains: create channel with "Type" filters"); \ + if (!testOk(c1 == e1, "create channel (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + if (!testOk(c2 == e2, "create channel (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \ + e1 = e_open | ExpReg1; c1 = 0; \ + e2 = e_open | ExpReg2; c2 = 0; \ + if (pch) testOk(!dbChannelOpen(pch), "dbChannelOpen returned channel"); \ + if (!testOk(c1 == e1, "open channel (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + if (!testOk(c2 == e2, "open channel (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \ + e1 = ExpRun1; c1 = 0; \ + e2 = ExpRun2; c2 = 0; \ + if (pch) testOk(!!(pfl = db_create_read_log(pch)), "create db_field_log"); \ + pfl->type = dbfl_type_ref; \ + pfl->field_type = TYPE_START; \ + if (pch) testOk(!!(pfl = dbChannelRunPreChain(pch, pfl)), "run pre eventq chain"); \ + if (pch) testOk(!!(pfl = dbChannelRunPostChain(pch, pfl)), "run post eventq chain"); \ + testOk(pfl->field_type == TYPE_START + DType, "final data type is correct"); \ + db_delete_field_log(pfl); \ + if (!testOk(c1 == e1, "run filter chains (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + if (!testOk(c2 == e2, "run filter chains (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \ + e1 = e_report; c1 = 0; \ + e2 = e_report; c2 = 0; \ + dbChannelShow(pch, R_INTRO, R_LEVEL); \ + if (!testOk(c1 == e1, "report (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + if (!testOk(c2 == e2, "report (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \ + e1 = e_close | e_free; c1 = 0; \ + e2 = e_close | e_free; c2 = 0; \ + if (pch) dbChannelDelete(pch); \ + if (!testOk(c1 == e1, "delete channel (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + if (!testOk(c2 == e2, "delete channel (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); + + CHAINTEST1("1 pre", "{\"pre\":{}}", e_reg_pre, e_pre | e_dtor, 1); /* One filter, pre chain */ + CHAINTEST1("1 post", "{\"post\":{}}", e_reg_post, e_post | e_dtor, 1); /* One filter, post chain */ + CHAINTEST1("1 both", "{\"sloppy\":{}}", e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, 2); /* One, both chains */ + CHAINTEST2("2 pre", "{\"pre\":{},\"pre\":{}}", e_reg_pre, e_pre | e_dtor, e_reg_pre, e_pre, 2); /* Two filters, pre chain */ + CHAINTEST2("2 post", "{\"post\":{},\"post\":{}}", e_reg_post, e_post | e_dtor, e_reg_post, e_post, 2); /* Two filters, post chain */ + CHAINTEST2("2 both", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains */ + e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, e_reg_pre | e_reg_post, e_pre | e_post, 4); + CHAINTEST2("1 pre, 1 post", "{\"pre\":{},\"post\":{}}", e_reg_pre, e_pre | e_dtor, e_reg_post, e_post, 2); /* Two, pre then post */ + CHAINTEST2("1 post, 1 pre", "{\"post\":{},\"pre\":{}}", e_reg_post, e_post, e_reg_pre, e_pre | e_dtor, 2); /* Two, post then pre */ + CHAINTEST2("1 pre, 1 both", "{\"pre\":{},\"sloppy\":{}}", /* Two, pre then both */ + e_reg_pre, e_pre | e_dtor, e_reg_pre | e_reg_post, e_pre | e_post, 3); + CHAINTEST2("1 both, 1 pre", "{\"sloppy\":{},\"pre\":{}}", /* Two, both then pre */ + e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, e_reg_pre, e_pre, 3); + CHAINTEST2("1 post, 1 both", "{\"post\":{},\"sloppy\":{}}", /* Two, post then both */ + e_reg_post, e_post, e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, 3); + CHAINTEST2("1 both, 1 post", "{\"sloppy\":{},\"post\":{}}", /* Two, both then post */ + e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, e_reg_post, e_post, 3); + dbFreeBase(pdbbase); return testDone(); From 6923dc5fd22b0da581393bf0cbc68e4384f960ac Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:58 -0400 Subject: [PATCH 46/81] Add ability for plugins to drop db_field_log updates (+tests) --- src/ioc/db/dbChannel.c | 4 ++-- src/ioc/db/test/chfPluginTest.c | 40 +++++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index e4c932109..a3a899906 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -428,7 +428,7 @@ db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn) { ELLNODE *node; db_field_log *pLog = pLogIn; - for (node = ellFirst(&chan->pre_chain); node; node = ellNext(node)) { + for (node = ellFirst(&chan->pre_chain); node && pLog; node = ellNext(node)) { filter = CONTAINER(node, chFilter, pre_node); pLog = filter->pre_func(filter->pre_arg, chan, pLog); } @@ -440,7 +440,7 @@ db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn) { ELLNODE *node; db_field_log *pLog = pLogIn; - for (node = ellFirst(&chan->post_chain); node; node = ellNext(node)) { + for (node = ellFirst(&chan->post_chain); node && pLog; node = ellNext(node)) { filter = CONTAINER(node, chFilter, post_node); pLog = filter->post_func(filter->post_arg, chan, pLog); } diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index 38bc43c6f..1c7c00407 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -39,6 +39,7 @@ unsigned int e1, e2, c1, c2; unsigned int offset; +int drop = -1; db_field_log *dtorpfl; #define e_any (e_alloc | e_free | e_error | e_ok | e_open \ @@ -215,14 +216,14 @@ static long channel_open(dbChannel *chan, void *user) return c_open_return; } -static dbfl_free1(db_field_log *pfl) { +static void dbfl_free1(db_field_log *pfl) { testOk(e1 & e_dtor, "dbfl_free (1) called"); testOk(dtorpfl == pfl, "dbfl_free (1): db_field_log pointer correct"); dtorpfl = NULL; c1 |= e_dtor; } -static dbfl_free2(db_field_log *pfl) { +static void dbfl_free2(db_field_log *pfl) { testOk(e2 & e_dtor, "dbfl_free (2) called"); testOk(dtorpfl == pfl, "dbfl_free (2): db_field_log pointer correct"); dtorpfl = NULL; @@ -256,6 +257,11 @@ static db_field_log * pre(void *user, dbChannel *chan, db_field_log *pLog) { pLog->u.r.dtor = dtor; dtorpfl = pLog; } + + if (my->offpre == drop) { + testDiag("pre (%c) is dropping the field log", inst(user)); + return NULL; + } return pLog; } @@ -286,6 +292,11 @@ static db_field_log * post(void *user, dbChannel *chan, db_field_log *pLog) { pLog->u.r.dtor = dtor; dtorpfl = pLog; } + + if (my->offpost == drop) { + testDiag("post (%c) is dropping the field log", inst(user)); + return NULL; + } return pLog; } @@ -429,7 +440,7 @@ MAIN(chfPluginTest) db_field_log *pfl; dbEventCtx evCtx = db_init_events(); - testPlan(1481); + testPlan(0); /* Enum to string conversion */ testHead("Enum to string conversion"); @@ -666,10 +677,10 @@ MAIN(chfPluginTest) if (pch) testOk(!!(pfl = db_create_read_log(pch)), "create db_field_log"); \ pfl->type = dbfl_type_ref; \ pfl->field_type = TYPE_START; \ - if (pch) testOk(!!(pfl = dbChannelRunPreChain(pch, pfl)), "run pre eventq chain"); \ - if (pch) testOk(!!(pfl = dbChannelRunPostChain(pch, pfl)), "run post eventq chain"); \ - testOk(pfl->field_type == TYPE_START + DType, "final data type is correct"); \ - db_delete_field_log(pfl); \ + if (pch) testOk(!!(pfl = dbChannelRunPreChain(pch, pfl)) || (drop >=0 && drop <= 1), "run pre eventq chain"); \ + if (pch && (drop < 0 || drop >= 2)) testOk(!!(pfl = dbChannelRunPostChain(pch, pfl)) || drop >=2, "run post eventq chain"); \ + if (pfl) testOk(pfl->field_type == TYPE_START + DType, "final data type is correct"); \ + if (pfl) db_delete_field_log(pfl); \ if (!testOk(c1 == e1, "run filter chains (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ if (!testOk(c2 == e2, "run filter chains (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \ e1 = e_report; c1 = 0; \ @@ -701,6 +712,21 @@ MAIN(chfPluginTest) CHAINTEST2("1 both, 1 post", "{\"sloppy\":{},\"post\":{}}", /* Two, both then post */ e_reg_pre | e_reg_post, e_pre | e_post | e_dtor, e_reg_post, e_post, 3); + /* Plugins dropping updates */ + drop = 0; + CHAINTEST2("2 both (drop at 0)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 0 */ + e_reg_pre | e_reg_post, e_pre, e_reg_pre | e_reg_post, 0, -1); + drop = 1; + CHAINTEST2("2 both (drop at 1)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 1 */ + e_reg_pre | e_reg_post, e_pre, e_reg_pre | e_reg_post, e_pre, -1); + drop = 2; + CHAINTEST2("2 both (drop at 2)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 2 */ + e_reg_pre | e_reg_post, e_pre | e_post, e_reg_pre | e_reg_post, e_pre, -1); + drop = 3; + CHAINTEST2("2 both (drop at 3)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 3 */ + e_reg_pre | e_reg_post, e_pre | e_post, e_reg_pre | e_reg_post, e_pre | e_post, -1); + drop = -1; + dbFreeBase(pdbbase); return testDone(); From 317065dadbb177e95d76f32335ebee0e411c575c Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:58 -0400 Subject: [PATCH 47/81] Minor fixes in chfPluginTest.c --- src/ioc/db/test/chfPluginTest.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index 1c7c00407..2240f2e08 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -351,7 +351,7 @@ static void channelRegisterPost(dbChannel *chan, void *user, static void channel_report(dbChannel *chan, void *user, const char *intro, int level) { testOk(user == puser1 || user == puser2, "channel_report: user pointer valid"); - testOk(intro == R_INTRO, "channel_report: intro string correct"); + testOk(!(strcmp(intro, R_INTRO)), "channel_report: intro string correct"); testOk(level == R_LEVEL - 1, "channel_report: level correct"); if (user == puser1) { testOk(e1 & e_report, "channel_report (1) called"); @@ -438,9 +438,10 @@ MAIN(chfPluginTest) { dbChannel *pch; db_field_log *pfl; - dbEventCtx evCtx = db_init_events(); - testPlan(0); + testPlan(1783); + + db_init_events(); /* Enum to string conversion */ testHead("Enum to string conversion"); @@ -714,16 +715,16 @@ MAIN(chfPluginTest) /* Plugins dropping updates */ drop = 0; - CHAINTEST2("2 both (drop at 0)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 0 */ + CHAINTEST2("2 both (drop at 0)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 0 */ e_reg_pre | e_reg_post, e_pre, e_reg_pre | e_reg_post, 0, -1); drop = 1; - CHAINTEST2("2 both (drop at 1)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 1 */ + CHAINTEST2("2 both (drop at 1)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 1 */ e_reg_pre | e_reg_post, e_pre, e_reg_pre | e_reg_post, e_pre, -1); drop = 2; - CHAINTEST2("2 both (drop at 2)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 2 */ + CHAINTEST2("2 both (drop at 2)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 2 */ e_reg_pre | e_reg_post, e_pre | e_post, e_reg_pre | e_reg_post, e_pre, -1); drop = 3; - CHAINTEST2("2 both (drop at 3)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 3 */ + CHAINTEST2("2 both (drop at 3)", "{\"sloppy\":{},\"sloppy\":{}}", /* Two, both chains, drop at filter 3 */ e_reg_pre | e_reg_post, e_pre | e_post, e_reg_pre | e_reg_post, e_pre | e_post, -1); drop = -1; From 6e58dcfab60c661d7f4e0f8b44725ffce227205c Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:21:59 -0400 Subject: [PATCH 48/81] Change interface to ...Show functions, minor refactoring * ...Show functions take an indent argument (number of spaces to print in front of each line) instead of a char* intro to avoid allocation at each level, replacing '\t' with 8 spaces in top level code where necessary * Refactor element_size -> field_size (as used in dbAddr), also in dbChannel functions * Fix: wrong macro name in unused part of db_field_log.h --- src/ioc/db/chfPlugin.c | 4 +-- src/ioc/db/chfPlugin.h | 4 +-- src/ioc/db/dbAccess.c | 4 +-- src/ioc/db/dbChannel.c | 43 +++++++++++++++++++++++---------- src/ioc/db/dbChannel.h | 14 +++++------ src/ioc/db/dbChannelIO.cpp | 4 +-- src/ioc/db/dbEvent.c | 4 +-- src/ioc/db/db_field_log.h | 4 +-- src/ioc/db/db_test.c | 4 +-- src/ioc/db/test/chfPluginTest.c | 10 +++----- src/ioc/db/test/dbChannelTest.c | 4 +-- src/ioc/rsrv/caservertask.c | 4 +-- 12 files changed, 59 insertions(+), 44 deletions(-) diff --git a/src/ioc/db/chfPlugin.c b/src/ioc/db/chfPlugin.c index b667666ce..8b9cddfed 100644 --- a/src/ioc/db/chfPlugin.c +++ b/src/ioc/db/chfPlugin.c @@ -504,13 +504,13 @@ static void channel_register_post(chFilter *filter, p->pif->channelRegisterPost(filter->chan, f->puser, cb_out, arg_out, probe); } -static void channel_report(chFilter *filter, const char *intro, int level) +static void channel_report(chFilter *filter, int level, const unsigned short indent) { chfPlugin *p = (chfPlugin*) filter->plug->puser; chfFilter *f = (chfFilter*) filter->puser; if (p->pif->channel_report) - p->pif->channel_report(filter->chan, f->puser, intro, level); + p->pif->channel_report(filter->chan, f->puser, level, indent); } static void channel_close(chFilter *filter) diff --git a/src/ioc/db/chfPlugin.h b/src/ioc/db/chfPlugin.h index 2564867b4..44b04e5a5 100644 --- a/src/ioc/db/chfPlugin.h +++ b/src/ioc/db/chfPlugin.h @@ -195,10 +195,10 @@ typedef struct chfPluginIf { * * @param chan dbChannel for which the report is requested. * @param pvt Pointer to private structure. - * @param intro Line header string to be printed at the beginning of each line. * @param level Interest level. + * @param indent Number of spaces to print before each output line. */ - void (* channel_report) (dbChannel *chan, void *pvt, const char *intro, int level); + void (* channel_report) (dbChannel *chan, void *pvt, int level, const unsigned short indent); /* FIXME: More filter routines here ... */ diff --git a/src/ioc/db/dbAccess.c b/src/ioc/db/dbAccess.c index c6247543c..d40205f4a 100644 --- a/src/ioc/db/dbAccess.c +++ b/src/ioc/db/dbAccess.c @@ -839,7 +839,7 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, } else { DBADDR localAddr = *paddr; /* Structure copy */ localAddr.field_type = pfl->field_type; - localAddr.field_size = pfl->element_size; + localAddr.field_size = pfl->field_size; localAddr.no_elements = pfl->no_elements; if (pfl->type == dbfl_type_val) localAddr.pfield = (char *) &pfl->u.v.field; @@ -875,7 +875,7 @@ long epicsShareAPI dbGet(DBADDR *paddr, short dbrType, } else { DBADDR localAddr = *paddr; /* Structure copy */ localAddr.field_type = pfl->field_type; - localAddr.field_size = pfl->element_size; + localAddr.field_size = pfl->field_size; localAddr.no_elements = pfl->no_elements; if (pfl->type == dbfl_type_val) localAddr.pfield = (char *) &pfl->u.v.field; diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index a3a899906..07fd97c29 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -469,7 +469,7 @@ long dbChannelOpen(dbChannel *chan) db_field_log p; probe.field_type = dbChannelFieldType(chan); probe.no_elements = dbChannelElements(chan); - probe.element_size = dbChannelElementSize(chan); + probe.field_size = dbChannelFieldSize(chan); p = probe; /* @@ -507,7 +507,7 @@ long dbChannelOpen(dbChannel *chan) /* Save probe results */ chan->final_no_elements = probe.no_elements; - chan->final_element_size = probe.element_size; + chan->final_field_size = probe.field_size; chan->final_type = probe.field_type; chan->dbr_final_type = dbDBRnewToDBRold[mapDBFToDBR[probe.field_type]]; @@ -547,7 +547,7 @@ short dbChannelExportType(dbChannel *chan) return chan->addr.dbr_field_type; } -short dbChannelElementSize(dbChannel *chan) +short dbChannelFieldSize(dbChannel *chan) { return chan->addr.field_size; } @@ -567,9 +567,9 @@ short dbChannelFinalExportType(dbChannel *chan) return chan->dbr_final_type; } -short dbChannelFinalElementSize(dbChannel *chan) +short dbChannelFinalFieldSize(dbChannel *chan) { - return chan->final_element_size; + return chan->final_field_size; } short dbChannelSpecial(dbChannel *chan) @@ -624,24 +624,41 @@ long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer, return dbPutField(&chan->addr, type, pbuffer, nRequest); } -void dbChannelShow(dbChannel *chan, const char *intro, int level) +void dbChannelShow(dbChannel *chan, int level, const unsigned short indent) { long elems = chan->addr.no_elements; + long felems = chan->final_no_elements; int count = ellCount(&chan->filters); - printf("%schannel name: %s\n", intro, chan->name); + int pre = ellCount(&chan->pre_chain); + int post = ellCount(&chan->post_chain); + int i; + + for (i = 0; i < indent; i++) printf(" "); + printf("channel name: %s\n", chan->name); + for (i = 0; i < indent; i++) printf(" "); /* FIXME: show field_type as text */ - printf("%s field_type=%d, %ld element%s, %d filter%s\n", - intro, chan->addr.field_type, elems, elems == 1 ? "" : "s", - count, count == 1 ? "" : "s"); + printf(" field_type=%d (%dB), %ld element%s, %d filter%s", + chan->addr.field_type, chan->addr.field_size, elems, elems == 1 ? "" : "s", + count, count == 1 ? "" : "s"); + if (count) + printf(" (%d pre eventq, %d post eventq)\n", pre, post); + else + printf("\n"); if (level > 0) - dbChannelFilterShow(chan, intro, level - 1); + dbChannelFilterShow(chan, level - 1, indent + 2); + if (count) { + for (i = 0; i < indent; i++) printf(" "); + /* FIXME: show field_type as text */ + printf(" final field_type=%d (%dB), %ld element%s\n", + chan->final_type, chan->final_field_size, felems, felems == 1 ? "" : "s"); + } } -void dbChannelFilterShow(dbChannel *chan, const char *intro, int level) +void dbChannelFilterShow(dbChannel *chan, int level, const unsigned short indent) { chFilter *filter = (chFilter *) ellFirst(&chan->filters); while (filter) { - filter->plug->fif->channel_report(filter, intro, level); + filter->plug->fif->channel_report(filter, level, indent); filter = (chFilter *) ellNext(&filter->list_node); } } diff --git a/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index 21ba8ac96..485600311 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -54,7 +54,7 @@ typedef struct dbChannel { const char *name; dbAddr addr; /* address structure for record/field */ long final_no_elements; /* final number of elements (arrays) */ - short final_element_size; /* final size of element */ + short final_field_size; /* final size of element */ short final_type; /* final type of database field */ short dbr_final_type; /* final field type as seen by database request */ ELLLIST filters; /* list of filters as created from JSON */ @@ -107,7 +107,7 @@ typedef struct chFilterIf { long (* channel_open)(chFilter *filter); void (* channel_register_pre) (chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); void (* channel_register_post)(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe); - void (* channel_report)(chFilter *filter, const char *intro, int level); + void (* channel_report)(chFilter *filter, int level, const unsigned short indent); /* FIXME: More filter routines here ... */ void (* channel_close)(chFilter *filter); } chFilterIf; @@ -146,7 +146,7 @@ epicsShareFunc struct dbFldDes * dbChannelFldDes(dbChannel *chan); epicsShareFunc long dbChannelElements(dbChannel *chan); epicsShareFunc short dbChannelFieldType(dbChannel *chan); epicsShareFunc short dbChannelExportType(dbChannel *chan); -epicsShareFunc short dbChannelElementSize(dbChannel *chan); +epicsShareFunc short dbChannelFieldSize(dbChannel *chan); epicsShareFunc long dbChannelFinalElements(dbChannel *chan); epicsShareFunc short dbChannelFinalFieldType(dbChannel *chan); epicsShareFunc short dbChannelFinalExportType(dbChannel *chan); @@ -161,10 +161,10 @@ epicsShareFunc long dbChannelPut(dbChannel *chan, short type, const void *pbuffer, long nRequest); epicsShareFunc long dbChannelPutField(dbChannel *chan, short type, const void *pbuffer, long nRequest); -epicsShareFunc void dbChannelShow(dbChannel *chan, const char *intro, - int level); -epicsShareFunc void dbChannelFilterShow(dbChannel *chan, const char *intro, - int level); +epicsShareFunc void dbChannelShow(dbChannel *chan, int level, + const unsigned short indent); +epicsShareFunc void dbChannelFilterShow(dbChannel *chan, int level, + const unsigned short indent); epicsShareFunc void dbChannelDelete(dbChannel *chan); epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif, void *puser); diff --git a/src/ioc/db/dbChannelIO.cpp b/src/ioc/db/dbChannelIO.cpp index c1da6b920..280d55e0c 100644 --- a/src/ioc/db/dbChannelIO.cpp +++ b/src/ioc/db/dbChannelIO.cpp @@ -155,12 +155,12 @@ void dbChannelIO::show ( dbChannelRecord ( this->dbch ) -> name ); if ( level > 0u ) { - printf ( "\ttype %s, element count %li, field at %p\n", + printf ( " type %s, element count %li, field at %p\n", dbf_type_to_text ( dbChannelExportType ( this->dbch ) ), dbChannelElements ( this->dbch ), dbChannelField ( this->dbch ) ); if ( level > 1u ) { - dbChannelFilterShow ( this->dbch, "\t", level - 2u ); + dbChannelFilterShow ( this->dbch, level - 2u, 8 ); this->serviceIO.show ( level - 2u ); this->serviceIO.showAllIO ( *this, level - 2u ); } diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index 9b509612d..77fcbf0d1 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -425,7 +425,7 @@ dbEventSubscription epicsShareAPI db_add_event ( * there upon wakeup) */ if( dbChannelElements(chan) == 1 && - dbChannelElementSize(chan) <= sizeof(union native_value)) { + dbChannelFieldSize(chan) <= sizeof(union native_value)) { pevent->useValque = TRUE; } else { @@ -649,7 +649,7 @@ db_field_log* epicsShareAPI db_create_event_log (struct evSubscrip *pevent) */ memcpy(&pLog->u.v.field, dbChannelField(chan), - dbChannelElementSize(chan)); + dbChannelFieldSize(chan)); } else { pLog->type = dbfl_type_rec; } diff --git a/src/ioc/db/db_field_log.h b/src/ioc/db/db_field_log.h index 22f3108d6..1f1d212d0 100644 --- a/src/ioc/db/db_field_log.h +++ b/src/ioc/db/db_field_log.h @@ -43,7 +43,7 @@ union native_value { long dbf_long; double dbf_double; #ifdef DB_EVENT_LOG_STRINGS - char dbf_string[MAXSTRINGSIZE]; + char dbf_string[MAX_STRING_SIZE]; #endif }; @@ -86,7 +86,7 @@ typedef struct db_field_log { unsigned short stat; /* Alarm Status */ unsigned short sevr; /* Alarm Severity */ short field_type; /* DBF type of data */ - short element_size; /* Data size */ + short field_size; /* Data size */ long no_elements; /* No of array elements */ union { struct dbfl_val v; diff --git a/src/ioc/db/db_test.c b/src/ioc/db/db_test.c index 45a5ceca5..81a4f3d4b 100644 --- a/src/ioc/db/db_test.c +++ b/src/ioc/db/db_test.c @@ -54,7 +54,7 @@ int gft(char *pname) printf("Record Address: 0x%p\n", precord); printf(" Export Type: %d\n", type); printf(" Field Address: 0x%p\n", dbChannelField(chan)); - printf(" Field Size: %d\n", dbChannelElementSize(chan)); + printf(" Field Size: %d\n", dbChannelFieldSize(chan)); printf(" No Elements: %ld\n", elements); if (elements > MAX_ELEMS) @@ -108,7 +108,7 @@ int epicsShareAPI pft(char *pname, char *pvalue) printf("Record Address: 0x%p\n", precord); printf(" Export Type: %d\n", type); printf(" Field Address: 0x%p\n", dbChannelField(chan)); - printf(" Field Size: %d\n", dbChannelElementSize(chan)); + printf(" Field Size: %d\n", dbChannelFieldSize(chan)); printf(" No Elements: %ld\n", elements); if (dbChannel_put(chan, DBR_STRING,pvalue, 1) < 0) diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index 2240f2e08..e3c8604e7 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -20,7 +20,6 @@ #define PATTERN 0x55555555 #define TYPE_START 0xAAA -#define R_INTRO "abc" #define R_LEVEL 42 /* Expected / actually run callback bit definitions */ @@ -348,10 +347,9 @@ static void channelRegisterPost(dbChannel *chan, void *user, *arg_out = user; } -static void channel_report(dbChannel *chan, void *user, const char *intro, int level) +static void channel_report(dbChannel *chan, void *user, int level, const unsigned short indent) { testOk(user == puser1 || user == puser2, "channel_report: user pointer valid"); - testOk(!(strcmp(intro, R_INTRO)), "channel_report: intro string correct"); testOk(level == R_LEVEL - 1, "channel_report: level correct"); if (user == puser1) { testOk(e1 & e_report, "channel_report (1) called"); @@ -439,7 +437,7 @@ MAIN(chfPluginTest) dbChannel *pch; db_field_log *pfl; - testPlan(1783); + testPlan(1754); db_init_events(); @@ -654,7 +652,7 @@ MAIN(chfPluginTest) db_delete_field_log(pfl); \ if (!testOk(c1 == e1, "run filter chains: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ e1 = e_report; c1 = 0; \ - dbChannelShow(pch, R_INTRO, R_LEVEL); \ + dbChannelShow(pch, R_LEVEL, 0); \ if (!testOk(c1 == e1, "report: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ e1 = e_close | e_free; c1 = 0; \ if (pch) dbChannelDelete(pch); \ @@ -686,7 +684,7 @@ MAIN(chfPluginTest) if (!testOk(c2 == e2, "run filter chains (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \ e1 = e_report; c1 = 0; \ e2 = e_report; c2 = 0; \ - dbChannelShow(pch, R_INTRO, R_LEVEL); \ + dbChannelShow(pch, R_LEVEL, 0); \ if (!testOk(c1 == e1, "report (1): all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ if (!testOk(c2 == e2, "report (2): all expected calls happened")) testDiag("expected %#x - called %#x", e2, c2); \ e1 = e_close | e_free; c1 = 0; \ diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index c126c7625..1653fbe69 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -132,7 +132,7 @@ void c_reg_post(chFilter *filter, chPostEventFunc **cb_out, void **arg_out, db_f { testOk(e & e_reg_post, "channel_register_post called"); } -void c_report(chFilter *filter, const char *intro, int level) +void c_report(chFilter *filter, int level, const unsigned short indent) { testOk(e & e_report, "channel_report called, level = %d", level); } @@ -214,7 +214,7 @@ MAIN(dbChannelTest) testOk1(!!(pch = dbChannelCreate("x.{\"scalar\":null}"))); e = e_report; - dbChannelShow(pch, "# ", 1); + dbChannelShow(pch, 1, 2); e = e_close; if (pch) dbChannelDelete(pch); diff --git a/src/ioc/rsrv/caservertask.c b/src/ioc/rsrv/caservertask.c index ce7fef94e..e7dba10b7 100644 --- a/src/ioc/rsrv/caservertask.c +++ b/src/ioc/rsrv/caservertask.c @@ -355,8 +355,8 @@ static void showChanList ( epicsMutexMustLock ( client->chanListLock ); pciu = (struct channel_in_use *) pList->node.next; while ( pciu ){ - dbChannelShow ( pciu->dbch, "\t", level ); - printf( "\t # on eventq=%d, access=%c%c\n", + dbChannelShow ( pciu->dbch, level, 8 ); + printf( " # on eventq=%d, access=%c%c\n", ellCount ( &pciu->eventq ), asCheckGet ( pciu->asClientPVT ) ? 'r': '-', rsrvCheckPut ( pciu ) ? 'w': '-' ); From e67ef8a0050aa096cda9eb07cea4874104683d0a Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:21:59 -0400 Subject: [PATCH 49/81] Add ts filter plugin to set timestamp to "now" --- src/ioc/db/Makefile | 5 ++ src/ioc/db/filters/filters.dbd | 1 + src/ioc/db/filters/ts.c | 123 +++++++++++++++++++++++++++++++++ src/std/softIoc/base.dbd | 3 + 4 files changed, 132 insertions(+) create mode 100644 src/ioc/db/filters/filters.dbd create mode 100644 src/ioc/db/filters/ts.c diff --git a/src/ioc/db/Makefile b/src/ioc/db/Makefile index 3a47dae3e..ad3d2fe91 100644 --- a/src/ioc/db/Makefile +++ b/src/ioc/db/Makefile @@ -53,6 +53,9 @@ menuGlobal_DBD += menuSimm.dbd DBDINC += $(basename $(menuGlobal_DBD)) DBDINC += dbCommon +DBD_INSTALLS += filters.dbd + + dbCore_SRCS += dbLock.c dbCore_SRCS += dbAccess.c dbCore_SRCS += dbBkpt.c @@ -81,3 +84,5 @@ dbCore_SRCS += templateInstances.cpp dbCore_SRCS += dbIocRegister.c dbCore_SRCS += chfPlugin.c +LIB_SRCS += ts.c + diff --git a/src/ioc/db/filters/filters.dbd b/src/ioc/db/filters/filters.dbd new file mode 100644 index 000000000..bc84b4568 --- /dev/null +++ b/src/ioc/db/filters/filters.dbd @@ -0,0 +1 @@ +registrar(tsInitialize) diff --git a/src/ioc/db/filters/ts.c b/src/ioc/db/filters/ts.c new file mode 100644 index 000000000..af6149b7c --- /dev/null +++ b/src/ioc/db/filters/ts.c @@ -0,0 +1,123 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +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_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 +}; + +static void *tsStringFreeList; + +void freeArray(db_field_log *pfl) { + if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) { + freeListFree(tsStringFreeList, pfl->u.r.field); + } else { + free(pfl->u.r.field); + } +} + +static db_field_log* tsFilter(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) { + void *p; + struct dbCommon *prec = dbChannelRecord(chan); + pfl->type = dbfl_type_ref; + pfl->stat = prec->stat; + pfl->sevr = prec->sevr; + 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(tsStringFreeList); + } else { + p = calloc(pfl->no_elements, pfl->field_size); + } + if (p) dbGet(&chan->addr, mapDBFToDBR[pfl->field_type], p, NULL, &pfl->no_elements, NULL); + pfl->u.r.field = p; + } + + pfl->time = now; + return pfl; +} + +static void channelRegisterPre(dbChannel *chan, void *pvt, + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) +{ + *cb_out = tsFilter; +} + +void channel_report(dbChannel *chan, void *user, int level, const unsigned short indent) +{ + int i; + for (i = 0; i < indent; i++) printf(" "); + printf(" plugin ts\n"); +} + +static chfPluginIf tsPif = { + NULL, /* allocPvt, */ + NULL, /* freePvt, */ + + NULL, /* parse_error, */ + NULL, /* parse_ok, */ + + NULL, /* channel_open, */ + channelRegisterPre, + NULL, /* channelRegisterPost, */ + channel_report, + NULL /* channel_close */ +}; + +static void tsInitialize(void) +{ + static int firstTime = 1; + + if(!firstTime) return; + firstTime = 0; + + if (!tsStringFreeList) { + freeListInitPvt(&tsStringFreeList, + sizeof(epicsOldString), 64); + } + + chfPluginRegister("ts", &tsPif, NULL); +} + +epicsExportRegistrar(tsInitialize); diff --git a/src/std/softIoc/base.dbd b/src/std/softIoc/base.dbd index 894a46722..3134fc4fe 100644 --- a/src/std/softIoc/base.dbd +++ b/src/std/softIoc/base.dbd @@ -10,6 +10,9 @@ include "menuConvert.dbd" # Record types include "stdRecords.dbd" +# Channel filters & plugins +include "filters.dbd" + # "Soft Channel", "Raw Soft Channel", and "Async Soft Channel" device support include "devSoft.dbd" From d2abc71527f69e08444e7e6f81e56cf23d74f273 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:22:00 -0400 Subject: [PATCH 50/81] Add function to dbChannel.c to create a db_field_log copy of array/string data --- src/ioc/db/dbChannel.c | 43 +++++++++++++++++++++++++++++++ src/ioc/db/dbChannel.h | 2 ++ src/ioc/db/filters/ts.c | 56 +---------------------------------------- src/ioc/misc/iocInit.c | 2 ++ 4 files changed, 48 insertions(+), 55 deletions(-) diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index 07fd97c29..ec85da201 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -22,6 +22,7 @@ #include "dbBase.h" #include "dbEvent.h" #include "link.h" +#include #include "dbAccessDefs.h" #include "dbLock.h" #include "dbStaticLib.h" @@ -43,6 +44,16 @@ typedef struct parseContext { #define CALLIF(rtn) !rtn ? parse_stop : rtn +static void *dbchStringFreeList; + +void dbChannelInit (void) +{ + if (!dbchStringFreeList) { + freeListInitPvt(&dbchStringFreeList, + sizeof(epicsOldString), 128); + } +} + static void chf_value(parseContext *parser, parse_result *presult) { chFilter *filter = parser->filter; @@ -676,6 +687,38 @@ void dbChannelDelete(dbChannel *chan) free(chan); // FIXME: Use free-list } +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) +{ + void *p; + + if (!pfl->type == dbfl_type_rec) return; + + struct dbCommon *prec = dbChannelRecord(chan); + 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); + } + 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/src/ioc/db/dbChannel.h b/src/ioc/db/dbChannel.h index 485600311..2e50185aa 100644 --- a/src/ioc/db/dbChannel.h +++ b/src/ioc/db/dbChannel.h @@ -137,6 +137,7 @@ struct chFilter { struct dbCommon; struct dbFldDes; +epicsShareFunc void dbChannelInit (void); epicsShareFunc long dbChannelTest(const char *name); epicsShareFunc dbChannel * dbChannelCreate(const char *name); epicsShareFunc long dbChannelOpen(dbChannel *chan); @@ -171,6 +172,7 @@ epicsShareFunc void dbRegisterFilter(const char *key, const chFilterIf *fif, voi epicsShareFunc db_field_log* dbChannelRunPreChain(dbChannel *chan, db_field_log *pLogIn); epicsShareFunc db_field_log* dbChannelRunPostChain(dbChannel *chan, db_field_log *pLogIn); epicsShareFunc const chFilterPlugin * dbFindFilter(const char *key, size_t len); +epicsShareFunc void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan); #ifdef __cplusplus } diff --git a/src/ioc/db/filters/ts.c b/src/ioc/db/filters/ts.c index af6149b7c..26c56bfb8 100644 --- a/src/ioc/db/filters/ts.c +++ b/src/ioc/db/filters/ts.c @@ -11,43 +11,10 @@ */ #include -#include #include -#include #include #include -#include -#include - -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_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 -}; - -static void *tsStringFreeList; - -void freeArray(db_field_log *pfl) { - if (pfl->field_type == DBF_STRING && pfl->no_elements == 1) { - freeListFree(tsStringFreeList, pfl->u.r.field); - } else { - free(pfl->u.r.field); - } -} static db_field_log* tsFilter(void* pvt, dbChannel *chan, db_field_log *pfl) { epicsTimeStamp now; @@ -55,23 +22,7 @@ static db_field_log* tsFilter(void* pvt, dbChannel *chan, db_field_log *pfl) { /* If string or array, must make a copy (to ensure coherence between time and data) */ if (pfl->type == dbfl_type_rec) { - void *p; - struct dbCommon *prec = dbChannelRecord(chan); - pfl->type = dbfl_type_ref; - pfl->stat = prec->stat; - pfl->sevr = prec->sevr; - 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(tsStringFreeList); - } else { - p = calloc(pfl->no_elements, pfl->field_size); - } - if (p) dbGet(&chan->addr, mapDBFToDBR[pfl->field_type], p, NULL, &pfl->no_elements, NULL); - pfl->u.r.field = p; + dbChannelMakeArrayCopy(pvt, pfl, chan); } pfl->time = now; @@ -112,11 +63,6 @@ static void tsInitialize(void) if(!firstTime) return; firstTime = 0; - if (!tsStringFreeList) { - freeListInitPvt(&tsStringFreeList, - sizeof(epicsOldString), 64); - } - chfPluginRegister("ts", &tsPif, NULL); } diff --git a/src/ioc/misc/iocInit.c b/src/ioc/misc/iocInit.c index f4e3b7e9e..18b0c6cd5 100644 --- a/src/ioc/misc/iocInit.c +++ b/src/ioc/misc/iocInit.c @@ -58,6 +58,7 @@ #include "initHooks.h" #include "epicsExit.h" #include "epicsSignal.h" +#include "dbChannel.h" #define epicsExportSharedSymbols #include "epicsRelease.h" @@ -460,6 +461,7 @@ static void doInitRecord1(dbRecordType *pdbRecordType, dbCommon *precord, static void initDatabase(void) { + dbChannelInit(); iterateRecords(doInitRecord0, NULL); iterateRecords(doResolveLinks, NULL); iterateRecords(doInitRecord1, NULL); From 444a200b61c2ac77c7e1dfe85cd174d2272e3e29 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:22:00 -0400 Subject: [PATCH 51/81] Change how ...Show is generated, minor refactoring and fix in ts plugin --- src/ioc/db/dbChannel.c | 10 +++------- src/ioc/db/filters/ts.c | 14 ++++++-------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index ec85da201..70a22409a 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -642,13 +642,10 @@ void dbChannelShow(dbChannel *chan, int level, const unsigned short indent) int count = ellCount(&chan->filters); int pre = ellCount(&chan->pre_chain); int post = ellCount(&chan->post_chain); - int i; - for (i = 0; i < indent; i++) printf(" "); - printf("channel name: %s\n", chan->name); - for (i = 0; i < indent; i++) printf(" "); + printf("%*schannel name: %s\n", indent, "", chan->name); /* FIXME: show field_type as text */ - printf(" field_type=%d (%dB), %ld element%s, %d filter%s", + printf("%*s field_type=%d (%dB), %ld element%s, %d filter%s", indent, "", chan->addr.field_type, chan->addr.field_size, elems, elems == 1 ? "" : "s", count, count == 1 ? "" : "s"); if (count) @@ -658,9 +655,8 @@ void dbChannelShow(dbChannel *chan, int level, const unsigned short indent) if (level > 0) dbChannelFilterShow(chan, level - 1, indent + 2); if (count) { - for (i = 0; i < indent; i++) printf(" "); /* FIXME: show field_type as text */ - printf(" final field_type=%d (%dB), %ld element%s\n", + printf("%*s final field_type=%d (%dB), %ld element%s\n", indent, "", chan->final_type, chan->final_field_size, felems, felems == 1 ? "" : "s"); } } diff --git a/src/ioc/db/filters/ts.c b/src/ioc/db/filters/ts.c index 26c56bfb8..5bf2d4898 100644 --- a/src/ioc/db/filters/ts.c +++ b/src/ioc/db/filters/ts.c @@ -16,7 +16,7 @@ #include #include -static db_field_log* tsFilter(void* pvt, dbChannel *chan, db_field_log *pfl) { +static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { epicsTimeStamp now; epicsTimeGetCurrent(&now); @@ -32,17 +32,15 @@ static db_field_log* tsFilter(void* pvt, dbChannel *chan, db_field_log *pfl) { static void channelRegisterPre(dbChannel *chan, void *pvt, chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { - *cb_out = tsFilter; + *cb_out = filter; } -void channel_report(dbChannel *chan, void *user, int level, const unsigned short indent) +static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent) { - int i; - for (i = 0; i < indent; i++) printf(" "); - printf(" plugin ts\n"); + printf("%*s plugin ts\n", indent, ""); } -static chfPluginIf tsPif = { +static chfPluginIf pif = { NULL, /* allocPvt, */ NULL, /* freePvt, */ @@ -63,7 +61,7 @@ static void tsInitialize(void) if(!firstTime) return; firstTime = 0; - chfPluginRegister("ts", &tsPif, NULL); + chfPluginRegister("ts", &pif, NULL); } epicsExportRegistrar(tsInitialize); From b843a8990a8fcded60abfb275e0d197ec5184c7c Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:22:01 -0400 Subject: [PATCH 52/81] Fix: don't put dropped event (NULL) on the event queue --- src/ioc/db/dbEvent.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index 77fcbf0d1..d73a19a7c 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -802,7 +802,7 @@ unsigned int caEventMask (caEventMask & pevent->select)) { db_field_log *pLog = db_create_event_log(pevent); pLog = dbChannelRunPreChain(pevent->chan, pLog); - db_queue_event_log(pevent, pLog); + if (pLog) db_queue_event_log(pevent, pLog); } } From 0cfe224f47ecc69ee61425cc479402d494c273bc Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:22:01 -0400 Subject: [PATCH 53/81] Add deadband plugin (client defined monitor deadband) --- src/ioc/db/Makefile | 1 + src/ioc/db/filters/dbnd.c | 141 +++++++++++++++++++++++++++++++++ src/ioc/db/filters/filters.dbd | 1 + 3 files changed, 143 insertions(+) create mode 100644 src/ioc/db/filters/dbnd.c diff --git a/src/ioc/db/Makefile b/src/ioc/db/Makefile index ad3d2fe91..ed8b36f6e 100644 --- a/src/ioc/db/Makefile +++ b/src/ioc/db/Makefile @@ -85,4 +85,5 @@ dbCore_SRCS += dbIocRegister.c dbCore_SRCS += chfPlugin.c LIB_SRCS += ts.c +LIB_SRCS += dbnd.c diff --git a/src/ioc/db/filters/dbnd.c b/src/ioc/db/filters/dbnd.c new file mode 100644 index 000000000..e9d517ba1 --- /dev/null +++ b/src/ioc/db/filters/dbnd.c @@ -0,0 +1,141 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ +#include + +#include +#include +#include +#include +#include +#include + +typedef struct myStruct { + int mode; + double cval; + double hyst; + double last; +} myStruct; + +static void *myStructFreeList; + +static const +chfPluginEnumType modeEnum[] = { {"abs", 0}, {"rel", 1}, {NULL,0} }; + +static const +chfPluginArgDef opts[] = { + chfDouble (myStruct, cval, "d", 0, 1), + chfEnum (myStruct, mode, "m", 0, 1, modeEnum), + chfPluginArgEnd +}; + +void * allocPvt(void) +{ + return freeListCalloc(myStructFreeList); +} + +void freePvt(void *pvt) +{ + freeListFree(myStructFreeList, pvt); +} + +int parse_ok(void *pvt) +{ + myStruct *my = (myStruct*) pvt; + my->hyst = my->cval; + my->last = epicsNAN; + return 0; +} + +static void shiftval (myStruct *my, double val) { + my->last = val; + if (my->mode == 1) + my->hyst = val * my->cval/100.; +} + +static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { + myStruct *my = (myStruct*) pvt; + long status; + double val, delta; + short drop = 0; + + /* + * Only scalar values supported - strings, arrays, and conversion errors + * are just passed on + */ + if (pfl->type == dbfl_type_val) { + DBADDR localAddr = chan->addr; /* Structure copy */ + localAddr.field_type = pfl->field_type; + localAddr.field_size = pfl->field_size; + localAddr.no_elements = pfl->no_elements; + localAddr.pfield = (char *) &pfl->u.v.field; + status = dbFastGetConvertRoutine[pfl->field_type][DBR_DOUBLE] + (localAddr.pfield, (void*) &val, &localAddr); + if (!status) { + if (isnan(my->last)) { + shiftval(my, val); + } else { + delta = fabs(my->last - val); + if (delta <= my->hyst) { + drop = 1; + } else { + shiftval(my, val); + } + } + } + } + if (drop) return NULL; + else return pfl; +} + +static void channelRegisterPre(dbChannel *chan, void *pvt, + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) +{ + *cb_out = filter; + *arg_out = pvt; +} + +static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent) +{ + myStruct *my = (myStruct*) pvt; + printf("%*s plugin dbnd, mode=%s, delta=%g%s\n", indent, "", + chfPluginEnumString(modeEnum, my->mode, "n/a"), my->cval, + my->mode == 1 ? "%" : ""); +} + +static chfPluginIf pif = { + allocPvt, + freePvt, + + NULL, /* parse_error, */ + parse_ok, + + NULL, /* channel_open, */ + channelRegisterPre, + NULL, /* channelRegisterPost, */ + channel_report, + NULL /* channel_close */ +}; + +static void dbndInitialize(void) +{ + static int firstTime = 1; + + if (!firstTime) return; + firstTime = 0; + + if (!myStructFreeList) + freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64); + + chfPluginRegister("dbnd", &pif, opts); +} + +epicsExportRegistrar(dbndInitialize); diff --git a/src/ioc/db/filters/filters.dbd b/src/ioc/db/filters/filters.dbd index bc84b4568..c859f0f40 100644 --- a/src/ioc/db/filters/filters.dbd +++ b/src/ioc/db/filters/filters.dbd @@ -1 +1,2 @@ registrar(tsInitialize) +registrar(dbndInitialize) From 62ba76c71a608948999ec496719576dc95745771 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:22:02 -0400 Subject: [PATCH 54/81] Add dbExtractArray to support extracting same type arrays with offset and increment --- src/ioc/db/Makefile | 2 ++ src/ioc/db/dbExtractArray.c | 52 +++++++++++++++++++++++++++++++++++++ src/ioc/db/dbExtractArray.h | 30 +++++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 src/ioc/db/dbExtractArray.c create mode 100644 src/ioc/db/dbExtractArray.h diff --git a/src/ioc/db/Makefile b/src/ioc/db/Makefile index ed8b36f6e..27a26915e 100644 --- a/src/ioc/db/Makefile +++ b/src/ioc/db/Makefile @@ -20,6 +20,7 @@ INC += dbBkpt.h INC += dbChannel.h INC += dbConvert.h INC += dbConvertFast.h +INC += dbExtractArray.h INC += dbEvent.h INC += dbLink.h INC += dbLock.h @@ -62,6 +63,7 @@ dbCore_SRCS += dbBkpt.c dbCore_SRCS += dbChannel.c dbCore_SRCS += dbConvert.c dbCore_SRCS += dbFastLinkConv.c +dbCore_SRCS += dbExtractArray.c dbCore_SRCS += dbLink.c dbCore_SRCS += dbNotify.c dbCore_SRCS += dbScan.c diff --git a/src/ioc/db/dbExtractArray.c b/src/ioc/db/dbExtractArray.c new file mode 100644 index 000000000..fde0f6ca0 --- /dev/null +++ b/src/ioc/db/dbExtractArray.c @@ -0,0 +1,52 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2002 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. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + * + * based on dbConvert.c + * written by: Bob Dalesio, Marty Kraimer + */ + +#include + +#include "epicsTypes.h" +#include "dbAddr.h" +#define epicsExportSharedSymbols +#include "dbExtractArray.h" + +void dbExtractArray(const dbAddr *paddr, + void *pto, long nRequest, long no_elements, long offset, long increment) +{ + char *pdst = (char *) pto; + char *psrc = (char *) paddr->pfield; + long nUpperPart; + short srcSize = paddr->field_size; + short dstSize = srcSize; + char isString = (paddr->field_type == DBF_STRING); + + if (nRequest > no_elements) nRequest = no_elements; + if (isString && dstSize >= MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE - 1; + + if (increment == 1) { + 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)); + } else { + for (; nRequest > 0; nRequest--, pdst += srcSize, offset += increment) { + offset %= no_elements; + memcpy(pdst, &psrc[offset*srcSize], dstSize); + if (isString) pdst[dstSize] = '\0'; + } + } +} diff --git a/src/ioc/db/dbExtractArray.h b/src/ioc/db/dbExtractArray.h new file mode 100644 index 000000000..56f484212 --- /dev/null +++ b/src/ioc/db/dbExtractArray.h @@ -0,0 +1,30 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef INC_dbExtractArray_H +#define INC_dbExtractArray_H + +#include "dbFldTypes.h" +#include "shareLib.h" + +#ifdef __cplusplus +extern "C" { +#endif + +epicsShareExtern void dbExtractArray(const DBADDR *paddr, void *pbuffer, + long nRequest, long no_elements, long offset, long increment); + +#ifdef __cplusplus +} +#endif + +#endif // INC_dbExtractArray_H From d2d91a545f96d33df841addb069bea7c25b669db Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:22:02 -0400 Subject: [PATCH 55/81] Bunch of minor changes * Fix decorations in chfPlugin.c/.h * Fix missing include in dbAccessDefs.h * Beautification in dbChannel.c --- src/ioc/db/chfPlugin.c | 27 +++++++-------------------- src/ioc/db/chfPlugin.h | 10 ++-------- src/ioc/db/dbAccessDefs.h | 1 + src/ioc/db/dbChannel.c | 2 +- 4 files changed, 11 insertions(+), 29 deletions(-) diff --git a/src/ioc/db/chfPlugin.c b/src/ioc/db/chfPlugin.c index 8b9cddfed..cb91b0ef6 100644 --- a/src/ioc/db/chfPlugin.c +++ b/src/ioc/db/chfPlugin.c @@ -24,6 +24,7 @@ #include #include #include +#include #define epicsExportSharedSymbols #include "chfPlugin.h" @@ -67,9 +68,7 @@ typedef enum chfPluginType { * Convert the (long) integer value 'val' to the type named in 'opt->optType' * and store the result at 'user + opt->offset'. */ -static -int -store_integer_value(const chfPluginArgDef *opt, void *user, long val) +static int store_integer_value(const chfPluginArgDef *opt, void *user, long val) { long *ival; int *eval; @@ -128,9 +127,7 @@ store_integer_value(const chfPluginArgDef *opt, void *user, long val) * Convert the (int) boolean value 'val' to the type named in 'opt->optType' * and store the result at 'user + opt->offset'. */ -static -int -store_boolean_value(const chfPluginArgDef *opt, void *user, int val) +static int store_boolean_value(const chfPluginArgDef *opt, void *user, int val) { long *ival; int *eval; @@ -175,9 +172,7 @@ store_boolean_value(const chfPluginArgDef *opt, void *user, int val) * Convert the double value 'val' to the type named in 'opt->optType' * and store the result at 'user + opt->offset'. */ -static -int -store_double_value(const chfPluginArgDef *opt, void *user, double val) +static int store_double_value(const chfPluginArgDef *opt, void *user, double val) { long *ival; int *eval; @@ -230,9 +225,7 @@ store_double_value(const chfPluginArgDef *opt, void *user, double val) * Convert the (char*) string value 'val' to the type named in 'opt->optType' * and store the result at 'user + opt->offset'. */ -static -int -store_string_value(const chfPluginArgDef *opt, void *user, const char *val, size_t len) +static int store_string_value(const chfPluginArgDef *opt, void *user, const char *val, size_t len) { long *ival; int *eval; @@ -553,10 +546,7 @@ static chFilterIf wrapper_fif = { channel_close }; -epicsShareFunc -const char* -epicsShareAPI -chfPluginEnumString(const chfPluginEnumType *emap, int i, const char* def) +const char* chfPluginEnumString(const chfPluginEnumType *emap, int i, const char* def) { for(; emap && emap->name; emap++) { if ( i == emap->value ) { @@ -566,10 +556,7 @@ chfPluginEnumString(const chfPluginEnumType *emap, int i, const char* def) return def; } -epicsShareFunc -int -epicsShareAPI -chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef* opts) +int chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef* opts) { chfPlugin *p; size_t i; diff --git a/src/ioc/db/chfPlugin.h b/src/ioc/db/chfPlugin.h index 44b04e5a5..9b31654db 100644 --- a/src/ioc/db/chfPlugin.h +++ b/src/ioc/db/chfPlugin.h @@ -263,10 +263,7 @@ typedef struct chfPluginArgDef { * @param def String to be returned when 'i' isn't a valid Enum index. * @return The string associated with 'i'. */ -epicsShareFunc -const char* -epicsShareAPI -chfPluginEnumString(const chfPluginEnumType *Enums, int i, const char* def); +epicsShareFunc const char* chfPluginEnumString(const chfPluginEnumType *Enums, int i, const char* def); /** @brief Register a plugin. * @@ -274,10 +271,7 @@ chfPluginEnumString(const chfPluginEnumType *Enums, int i, const char* def); * @param pif Pointer to the plugin's interface. * @param opts Pointer to the configuration argument description table. */ -epicsShareFunc -int -epicsShareAPI -chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef* opts); +epicsShareFunc int chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginArgDef* opts); #ifdef __cplusplus } diff --git a/src/ioc/db/dbAccessDefs.h b/src/ioc/db/dbAccessDefs.h index ffbab3a2d..7e5c7a78d 100644 --- a/src/ioc/db/dbAccessDefs.h +++ b/src/ioc/db/dbAccessDefs.h @@ -20,6 +20,7 @@ #include "epicsTypes.h" #include "epicsTime.h" #include "dbBase.h" +#include "dbAddr.h" #ifdef INCLdb_accessh_epicsExportSharedSymbols # define epicsExportSharedSymbols diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index 70a22409a..f635db5dd 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -518,7 +518,7 @@ long dbChannelOpen(dbChannel *chan) /* Save probe results */ chan->final_no_elements = probe.no_elements; - chan->final_field_size = probe.field_size; + chan->final_field_size = probe.field_size; chan->final_type = probe.field_type; chan->dbr_final_type = dbDBRnewToDBRold[mapDBFToDBR[probe.field_type]]; From 0b32220e86211ea766198ed2062cb1ca9e163a85 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:22:04 -0400 Subject: [PATCH 56/81] Added tests for ts and dbnd plugins, fix in dbnd.c: make local functions static --- src/ioc/db/filters/dbnd.c | 7 +- src/ioc/db/filters/test/Makefile | 28 ++++ src/ioc/db/filters/test/dbndTest.c | 214 +++++++++++++++++++++++++ src/ioc/db/filters/test/filterTest.dbx | 13 ++ src/ioc/db/filters/test/tsTest.c | 97 +++++++++++ 5 files changed, 356 insertions(+), 3 deletions(-) create mode 100644 src/ioc/db/filters/test/Makefile create mode 100644 src/ioc/db/filters/test/dbndTest.c create mode 100644 src/ioc/db/filters/test/filterTest.dbx create mode 100644 src/ioc/db/filters/test/tsTest.c diff --git a/src/ioc/db/filters/dbnd.c b/src/ioc/db/filters/dbnd.c index e9d517ba1..33e79198d 100644 --- a/src/ioc/db/filters/dbnd.c +++ b/src/ioc/db/filters/dbnd.c @@ -9,6 +9,7 @@ /* * Author: Ralph Lange */ + #include #include @@ -37,17 +38,17 @@ chfPluginArgDef opts[] = { chfPluginArgEnd }; -void * allocPvt(void) +static void * allocPvt(void) { return freeListCalloc(myStructFreeList); } -void freePvt(void *pvt) +static void freePvt(void *pvt) { freeListFree(myStructFreeList, pvt); } -int parse_ok(void *pvt) +static int parse_ok(void *pvt) { myStruct *my = (myStruct*) pvt; my->hyst = my->cval; diff --git a/src/ioc/db/filters/test/Makefile b/src/ioc/db/filters/test/Makefile new file mode 100644 index 000000000..1e32b3056 --- /dev/null +++ b/src/ioc/db/filters/test/Makefile @@ -0,0 +1,28 @@ +#************************************************************************* +# Copyright (c) 2006 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# Copyright (c) 2002 The Regents of the University of California, as +# Operator of Los Alamos National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in the file LICENSE that is included with this distribution. +#************************************************************************* +TOP=../../../.. + +include $(TOP)/configure/CONFIG + +PROD_LIBS += dbIoc registryIoc dbStaticHost ca Com + +TESTPROD_HOST += tsTest +tsTest_SRCS += tsTest.c +OBJS_IOC_vxWorks += tsTest +TESTS += tsTest + +TESTPROD_HOST += dbndTest +dbndTest_SRCS += dbndTest.c +OBJS_IOC_vxWorks += dbndTest +TESTS += dbndTest + +TESTSCRIPTS_HOST += $(TESTS:%=%.t) + +include $(TOP)/configure/RULES + diff --git a/src/ioc/db/filters/test/dbndTest.c b/src/ioc/db/filters/test/dbndTest.c new file mode 100644 index 000000000..727b02906 --- /dev/null +++ b/src/ioc/db/filters/test/dbndTest.c @@ -0,0 +1,214 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#include "dbStaticLib.h" +#include "dbAccessDefs.h" +#include "db_field_log.h" +#include "dbCommon.h" +#include "chfPlugin.h" +#include "epicsUnitTest.h" +#include "epicsTime.h" +#include "testMain.h" + +#define PATTERN 0x55 + +epicsShareExtern void (*pvar_func_dbndInitialize)(void); + +static db_field_log fl; + +static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) { + return !(memcmp(pfl1, pfl2, sizeof(db_field_log))); +} + +static void fl_setup(dbChannel *chan, db_field_log *pfl) { + struct dbCommon *prec = dbChannelRecord(chan); + + pfl->ctx = dbfl_context_read; + pfl->type = dbfl_type_val; + pfl->stat = prec->stat; + pfl->sevr = prec->sevr; + pfl->time = prec->time; + pfl->field_type = dbChannelFieldType(chan); + pfl->no_elements = dbChannelElements(chan); + /* + * use memcpy to avoid a bus error on + * union copy of char in the db at an odd + * address + */ + memcpy(&pfl->u.v.field, + dbChannelField(chan), + dbChannelFieldSize(chan)); +} + +static changeValue(db_field_log *pfl2, long val) { + pfl2->u.v.field.dbf_long = val; + testDiag("new value: %ld", val); +} + +static void mustPassOnce(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) { + changeValue(pfl2, val); + testDiag("mode=%s delta=%g filter must pass once", m, d); + db_field_log *pfl = dbChannelRunPreChain(pch, pfl2); + testOk(pfl2 == pfl, "call 1 does not drop or replace field_log"); + testOk(fl_equal(pfl, pfl2), "call 1 does not change field_log data"); + pfl = dbChannelRunPreChain(pch, pfl2); + testOk(NULL == pfl, "call 2 drops field_log"); +} + +static void mustDrop(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) { + changeValue(pfl2, val); + testDiag("mode=%s delta=%g filter must drop", m, d); + db_field_log *pfl = dbChannelRunPreChain(pch, pfl2); + testOk(NULL == pfl, "call 1 drops field_log"); +} + +static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) { + changeValue(pfl2, val); + testDiag("mode=%s delta=%g filter must pass twice", m, d); + db_field_log *pfl = dbChannelRunPreChain(pch, pfl2); + testOk(pfl2 == pfl, "call 1 does not drop or replace field_log"); + testOk(fl_equal(pfl, pfl2), "call 1 does not change field_log data"); + pfl = dbChannelRunPreChain(pch, pfl2); + testOk(pfl2 == pfl, "call 2 does not drop or replace field_log"); + testOk(fl_equal(pfl, pfl2), "call 2 does not change field_log data"); +} + +static void testHead (char* title) { + testDiag("--------------------------------------------------------"); + testDiag(title); + testDiag("--------------------------------------------------------"); +} + +MAIN(dbndTest) +{ + dbChannel *pch; + chFilter *filter; + const chFilterPlugin *plug; + char dbnd[] = "dbnd"; + ELLNODE *node; + chPostEventFunc *cb_out = NULL; + void *arg_out = NULL; + db_field_log *pfl, *pfl2; + db_field_log fl1; + + testPlan(0); + + db_init_events(); + + testOk1(!dbReadDatabase(&pdbbase, "filterTest.dbx", ".:..", NULL)); + testOk(!!pdbbase, "pdbbase was set"); + + (*pvar_func_dbndInitialize)(); /* manually initialize plugin */ + + testOk(!!(plug = dbFindFilter(dbnd, strlen(dbnd))), "plugin dbnd registered correctly"); + + testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{}}")), "dbChannel with plugin dbnd (delta=0) created"); + testOk((ellCount(&pch->filters) == 1), "channel has one plugin"); + + memset(&fl, PATTERN, sizeof(fl)); + fl1 = fl; + node = ellFirst(&pch->filters); + filter = CONTAINER(node, chFilter, list_node); + plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1); + testOk(!!(cb_out) && !!(arg_out), "register_pre registers one filter with argument"); + testOk(fl_equal(&fl1, &fl), "register_pre does not change field_log data type"); + + testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened"); + node = ellFirst(&pch->pre_chain); + filter = CONTAINER(node, chFilter, pre_node); + testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg != NULL), + "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); + + fl1.type = dbfl_type_ref; + mustPassTwice(pch, &fl1, "abs field_log=ref", 0., 0); + + /* Delta = 0: pass any change */ + + testHead("Delta = 0: pass any change"); + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + + mustPassOnce(pch, pfl2, "abs", 0., 0); + mustPassOnce(pch, pfl2, "abs", 0., 1); + + db_delete_field_log(pfl2); + dbChannelDelete(pch); + + /* Delta = -1: pass any update */ + + testHead("Delta = -1: pass any update"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{\"d\":-1.0}}")), "dbChannel with plugin dbnd (delta=-1) created"); + testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened"); + + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + + mustPassTwice(pch, pfl2, "abs", -1., 0); + mustPassTwice(pch, pfl2, "abs", -1., 1); + + db_delete_field_log(pfl2); + dbChannelDelete(pch); + + /* Delta = absolute */ + + testHead("Delta = absolute"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{\"d\":3}}")), "dbChannel with plugin dbnd (delta=3) created"); + testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened"); + + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + + mustPassOnce(pch, pfl2, "abs", 3., 1); + mustDrop(pch, pfl2, "abs", 3., 3); + mustDrop(pch, pfl2, "abs", 3., 4); + mustPassOnce(pch, pfl2, "abs", 3., 5); + + db_delete_field_log(pfl2); + dbChannelDelete(pch); + + /* Delta = relative */ + + testHead("Delta = relative"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{\"m\":\"rel\",\"d\":50}}")), + "dbChannel with plugin dbnd (mode=rel, delta=50) created"); + testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dbnd opened"); + + pfl2 = db_create_read_log(pch); + testDiag("new field_log from record"); + fl_setup(pch, pfl2); + + mustPassOnce(pch, pfl2, "rel", 50., 1); + mustPassOnce(pch, pfl2, "rel", 50., 2); + mustDrop(pch, pfl2, "rel", 50., 3); + mustPassOnce(pch, pfl2, "rel", 50., 4); + mustDrop(pch, pfl2, "rel", 50., 5); + mustDrop(pch, pfl2, "rel", 50., 6); + mustPassOnce(pch, pfl2, "rel", 50., 7); + + db_delete_field_log(pfl2); + dbChannelDelete(pch); + dbFreeBase(pdbbase); + + return testDone(); +} diff --git a/src/ioc/db/filters/test/filterTest.dbx b/src/ioc/db/filters/test/filterTest.dbx new file mode 100644 index 000000000..b743359c1 --- /dev/null +++ b/src/ioc/db/filters/test/filterTest.dbx @@ -0,0 +1,13 @@ +# This is a combined minimal DBD and DB file + +recordtype(x) { + field(NAME, DBF_STRING) { + prompt("Record Name") + special(SPC_NOMOD) + size(61) + } + field(VAL, DBF_LONG) { + prompt("Value") + } +} +record(x, x) {} diff --git a/src/ioc/db/filters/test/tsTest.c b/src/ioc/db/filters/test/tsTest.c new file mode 100644 index 000000000..6dda76a21 --- /dev/null +++ b/src/ioc/db/filters/test/tsTest.c @@ -0,0 +1,97 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#include "dbStaticLib.h" +#include "dbAccessDefs.h" +#include "chfPlugin.h" +#include "epicsUnitTest.h" +#include "epicsTime.h" +#include "testMain.h" + +#define PATTERN 0x55 + +epicsShareExtern void (*pvar_func_tsInitialize)(void); + +static db_field_log fl; + +static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) { + return !(memcmp(pfl1, pfl2, sizeof(db_field_log))); +} + +static int fl_equal_ex_ts(const db_field_log *pfl1, const db_field_log *pfl2) { + db_field_log fl1 = *pfl1; + + fl1.time = pfl2->time; + return fl_equal(&fl1, pfl2); +} + +MAIN(tsTest) +{ + dbChannel *pch; + chFilter *filter; + const chFilterPlugin *plug; + char ts[] = "ts"; + ELLNODE *node; + chPostEventFunc *cb_out = NULL; + void *arg_out = NULL; + db_field_log fl1; + db_field_log *pfl2; + + testPlan(0); + + db_init_events(); + + testOk1(!dbReadDatabase(&pdbbase, "filterTest.dbx", ".:..", NULL)); + testOk(!!pdbbase, "pdbbase was set"); + + (*pvar_func_tsInitialize)(); /* manually initialize plugin */ + + testOk(!!(plug = dbFindFilter(ts, strlen(ts))), "plugin ts registered correctly"); + + testOk(!!(pch = dbChannelCreate("x.VAL{\"ts\":{}}")), "dbChannel with plugin ts created"); + testOk((ellCount(&pch->filters) == 1), "channel has one plugin"); + + memset(&fl, PATTERN, sizeof(fl)); + fl1 = fl; + node = ellFirst(&pch->filters); + filter = CONTAINER(node, chFilter, list_node); + plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1); + testOk(!!(cb_out) && !(arg_out), "register_pre registers one filter w/o argument"); + testOk(fl_equal(&fl1, &fl), "register_pre does not change field_log data type"); + + testOk(!(dbChannelOpen(pch)), "dbChannel with plugin ts opened"); + node = ellFirst(&pch->pre_chain); + filter = CONTAINER(node, chFilter, pre_node); + testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg == NULL), + "ts has one filter w/o argument in pre chain"); + testOk((ellCount(&pch->post_chain) == 0), "ts has no filter in post chain"); + + memset(&fl, PATTERN, sizeof(fl)); + fl1 = fl; + pfl2 = dbChannelRunPreChain(pch, &fl1); + testOk(pfl2 == &fl1, "ts filter does not drop or replace field_log"); + testOk(fl_equal_ex_ts(&fl1, pfl2), "ts filter does not change field_log data"); + + testOk(pfl2 = db_create_read_log(pch), "create field log from channel"); + epicsTimeStamp stamp = pfl2->time; + pfl2 = dbChannelRunPreChain(pch, &fl1); + epicsTimeStamp now; epicsTimeGetCurrent(&now); + testOk(epicsTimeDiffInSeconds(&pfl2->time, &stamp) > 0. && + epicsTimeDiffInSeconds(&now, &pfl2->time) > 0., "ts filter sets time stamp to \"now\""); + + dbChannelDelete(pch); + dbFreeBase(pdbbase); + + return testDone(); +} From 0e2a66e7215a0068e5a1a19fd50c5fa1b2b1400d Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:24:54 -0400 Subject: [PATCH 57/81] Fixes and additions in dbExtractArray.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add forced write of '\0' to destination to end strings correctly * Add dbExtractArrayFromBuf() to make array copies from ref type field logs easier --- src/ioc/db/dbExtractArray.c | 36 ++++++++++++++++++++++++++++++++++-- src/ioc/db/dbExtractArray.h | 8 ++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/ioc/db/dbExtractArray.c b/src/ioc/db/dbExtractArray.c index fde0f6ca0..95eca4bfa 100644 --- a/src/ioc/db/dbExtractArray.c +++ b/src/ioc/db/dbExtractArray.c @@ -24,12 +24,13 @@ #define epicsExportSharedSymbols #include "dbExtractArray.h" -void dbExtractArray(const dbAddr *paddr, - void *pto, long nRequest, long no_elements, long offset, long increment) +void dbExtractArrayFromRec(const dbAddr *paddr, void *pto, + 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); @@ -42,6 +43,9 @@ void dbExtractArray(const dbAddr *paddr, 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] = '\0'; } else { for (; nRequest > 0; nRequest--, pdst += srcSize, offset += increment) { offset %= no_elements; @@ -50,3 +54,31 @@ void dbExtractArray(const dbAddr *paddr, } } } + +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; + + if (increment == 1) { + memcpy(pdst, &psrc[offset * srcSize], dstSize * nRequest); + if (isString) + for (i = 1; i <= nRequest; i++) + pdst[dstSize*i] = '\0'; + } else { + for (; nRequest > 0; nRequest--, pdst += srcSize, offset += increment) { + memcpy(pdst, &psrc[offset*srcSize], dstSize); + if (isString) pdst[dstSize] = '\0'; + } + } +} diff --git a/src/ioc/db/dbExtractArray.h b/src/ioc/db/dbExtractArray.h index 56f484212..7ed35847f 100644 --- a/src/ioc/db/dbExtractArray.h +++ b/src/ioc/db/dbExtractArray.h @@ -14,14 +14,18 @@ #define INC_dbExtractArray_H #include "dbFldTypes.h" +#include "dbAddr.h" #include "shareLib.h" #ifdef __cplusplus extern "C" { #endif -epicsShareExtern void dbExtractArray(const DBADDR *paddr, void *pbuffer, - long nRequest, long no_elements, long offset, long increment); +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); #ifdef __cplusplus } From ef4b2e5892ba0cb3d573421051ed1b735bd2c16b Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:25:03 -0400 Subject: [PATCH 58/81] Fix in dbExtractArray.c for DBF_STRING copies with wrap around --- src/ioc/db/dbExtractArray.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ioc/db/dbExtractArray.c b/src/ioc/db/dbExtractArray.c index 95eca4bfa..29d532530 100644 --- a/src/ioc/db/dbExtractArray.c +++ b/src/ioc/db/dbExtractArray.c @@ -36,21 +36,21 @@ void dbExtractArrayFromRec(const dbAddr *paddr, void *pto, char isString = (paddr->field_type == DBF_STRING); if (nRequest > no_elements) nRequest = no_elements; - if (isString && dstSize >= MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE - 1; + if (isString && srcSize > MAX_STRING_SIZE) dstSize = MAX_STRING_SIZE; - if (increment == 1) { + 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] = '\0'; + pdst[dstSize*i-1] = '\0'; } else { - for (; nRequest > 0; nRequest--, pdst += srcSize, offset += increment) { + for (; nRequest > 0; nRequest--, pdst += dstSize, offset += increment) { offset %= no_elements; memcpy(pdst, &psrc[offset*srcSize], dstSize); - if (isString) pdst[dstSize] = '\0'; + if (isString) pdst[dstSize-1] = '\0'; } } } From 45a531b167488e23ced350b8af04e4153b237926 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:25:07 -0400 Subject: [PATCH 59/81] Add arr plugin for array subsets (+ testcode) --- src/ioc/db/Makefile | 1 + src/ioc/db/filters/arr.c | 201 +++++++++++++++++ src/ioc/db/filters/filters.dbd | 1 + src/ioc/db/filters/test/Makefile | 13 +- src/ioc/db/filters/test/arrRecord.c | 135 +++++++++++ src/ioc/db/filters/test/arrRecord.dbd | 32 +++ src/ioc/db/filters/test/arrTest.cpp | 307 ++++++++++++++++++++++++++ src/ioc/db/filters/test/arrTest.db | 15 ++ src/ioc/db/filters/test/dbndTest.c | 2 +- src/ioc/db/filters/test/tsTest.c | 2 +- 10 files changed, 706 insertions(+), 3 deletions(-) create mode 100644 src/ioc/db/filters/arr.c create mode 100644 src/ioc/db/filters/test/arrRecord.c create mode 100644 src/ioc/db/filters/test/arrRecord.dbd create mode 100644 src/ioc/db/filters/test/arrTest.cpp create mode 100644 src/ioc/db/filters/test/arrTest.db diff --git a/src/ioc/db/Makefile b/src/ioc/db/Makefile index 27a26915e..7e3c2526b 100644 --- a/src/ioc/db/Makefile +++ b/src/ioc/db/Makefile @@ -88,4 +88,5 @@ dbCore_SRCS += chfPlugin.c LIB_SRCS += ts.c LIB_SRCS += dbnd.c +LIB_SRCS += arr.c diff --git a/src/ioc/db/filters/arr.c b/src/ioc/db/filters/arr.c new file mode 100644 index 000000000..07e1309a5 --- /dev/null +++ b/src/ioc/db/filters/arr.c @@ -0,0 +1,201 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct myStruct { + epicsInt32 start; + epicsInt32 incr; + epicsInt32 end; + void *arrayFreeList; + long no_elements; +} myStruct; + +static void *myStructFreeList; + +static const +chfPluginArgDef opts[] = { + chfInt32 (myStruct, start, "s", 0, 1), + chfInt32 (myStruct, incr, "i", 0, 1), + chfInt32 (myStruct, end, "e", 0, 1), + chfPluginArgEnd +}; + +static void * allocPvt(void) +{ + myStruct *my = (myStruct*) freeListCalloc(myStructFreeList); + my->incr = 1; + return (void *) my; +} + +static void freePvt(void *pvt) +{ + myStruct *my = (myStruct*) pvt; + + if (my->arrayFreeList) freeListCleanup(my->arrayFreeList); + freeListFree(myStructFreeList, pvt); +} + +static int parse_ok(void *pvt) +{ + return 0; +} + +static void freeArray(db_field_log *pfl) { + if (pfl->type == dbfl_type_ref) { + freeListFree(pfl->u.r.pvt, pfl->u.r.field); + } +} + +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; + + if (*end <= 0) *end = no_elements + *end; + if (*end <= 0) *end = 0; + if (*end > no_elements) *end = no_elements; + + if (*end - *start >= 1) len = 1 + (*end - *start - 1 ) / increment; + return len; +} + +static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { + myStruct *my = (myStruct*) pvt; + struct dbCommon *prec; + struct rset *prset; + long start = my->start; + long end = my->end; + long nTarget = 0; + long offset = 0; + long status; + long nSource = chan->addr.no_elements; + + /* Only array data */ + if (pfl->type == dbfl_type_val) { + return pfl; + + /* Extract from record */ + } else if (pfl->type == dbfl_type_rec) { + if (chan->addr.special == SPC_DBADDR && + nSource > 1 && + (prset = dbGetRset(&chan->addr)) && + prset->get_array_info) { + status = prset->get_array_info(&chan->addr, &nSource, &offset); + nTarget = wrapArrayIndices(&start, my->incr, &end, nSource); + prec = dbChannelRecord(chan); + 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->field_size = chan->addr.field_size; + pfl->no_elements = nTarget; + if (nTarget) { + pfl->u.r.dtor = freeArray; + pfl->u.r.pvt = my->arrayFreeList; + void *pdst = freeListCalloc(my->arrayFreeList); + offset = (offset + start) % chan->addr.no_elements; + dbExtractArrayFromRec(&chan->addr, pdst, nTarget, nSource, offset, my->incr); + pfl->u.r.field = pdst; + } + } + + /* Extract from buffer */ + } else if (pfl->type == dbfl_type_ref) { + nSource = pfl->no_elements; + nTarget = wrapArrayIndices(&start, my->incr, &end, nSource); + pfl->no_elements = nTarget; + void *psrc = pfl->u.r.field; + void *pdst = NULL; + if (nTarget) { /* Copy the data out */ + pdst = freeListCalloc(my->arrayFreeList); + offset = start; + dbExtractArrayFromBuf(psrc, pdst, pfl->field_size, pfl->field_type, nTarget, nSource, offset, my->incr); + } + if (pfl->u.r.dtor) pfl->u.r.dtor(pfl); + if (nTarget) { + pfl->u.r.dtor = freeArray; + pfl->u.r.pvt = my->arrayFreeList; + pfl->u.r.field = pdst; + } + } + return pfl; +} + +static void channelRegisterPost(dbChannel *chan, void *pvt, + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) +{ + myStruct *my = (myStruct*) pvt; + long start = my->start; + long end = my->end; + long max = 0; + + if (probe->no_elements <= 1) return; /* array data only */ + + max = wrapArrayIndices(&start, my->incr, &end, probe->no_elements); /* wrap indices into array */ + if (max) { + if (!my->arrayFreeList) + freeListInitPvt(&my->arrayFreeList, max * probe->field_size, 2); + if (!my->arrayFreeList) return; + } + probe->no_elements = my->no_elements = max; + *cb_out = filter; + *arg_out = pvt; +} + +static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent) +{ + myStruct *my = (myStruct*) pvt; + printf("%*s plugin arr, start=%d, incr=%d, end=%d\n", indent, "", + my->start, my->incr, my->end); +} + +static chfPluginIf pif = { + allocPvt, + freePvt, + + NULL, /* parse_error, */ + parse_ok, + + NULL, /* channel_open, */ + NULL, /* channelRegisterPre, */ + channelRegisterPost, + channel_report, + NULL /* channel_close */ +}; + +static void arrInitialize(void) +{ + static int firstTime = 1; + + if (!firstTime) return; + firstTime = 0; + + if (!myStructFreeList) + freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64); + + chfPluginRegister("arr", &pif, opts); +} + +epicsExportRegistrar(arrInitialize); diff --git a/src/ioc/db/filters/filters.dbd b/src/ioc/db/filters/filters.dbd index c859f0f40..48cd94cd3 100644 --- a/src/ioc/db/filters/filters.dbd +++ b/src/ioc/db/filters/filters.dbd @@ -1,2 +1,3 @@ registrar(tsInitialize) registrar(dbndInitialize) +registrar(arrInitialize) diff --git a/src/ioc/db/filters/test/Makefile b/src/ioc/db/filters/test/Makefile index 1e32b3056..23ebe705e 100644 --- a/src/ioc/db/filters/test/Makefile +++ b/src/ioc/db/filters/test/Makefile @@ -10,18 +10,29 @@ TOP=../../../.. include $(TOP)/configure/CONFIG -PROD_LIBS += dbIoc registryIoc dbStaticHost ca Com +PROD_LIBS += dbIoc miscIoc registryIoc ca Com TESTPROD_HOST += tsTest tsTest_SRCS += tsTest.c +tsTest_LIBS += dbStaticHost OBJS_IOC_vxWorks += tsTest TESTS += tsTest TESTPROD_HOST += dbndTest dbndTest_SRCS += dbndTest.c +dbndTest_LIBS += dbStaticHost OBJS_IOC_vxWorks += dbndTest TESTS += dbndTest +DBDINC += arrRecord +TARGETS += $(COMMON_DIR)/arrTest.dbd +arrTest_DBD += arrRecord.dbd +TESTPROD_HOST += arrTest +arrTest_SRCS += arrTest.cpp arrRecord.c arrRecord_registerRecordDeviceDriver.cpp +arrTest_LIBS += dbStaticIoc +OBJS_IOC_vxWorks += arrTest +TESTS += arrTest + TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES diff --git a/src/ioc/db/filters/test/arrRecord.c b/src/ioc/db/filters/test/arrRecord.c new file mode 100644 index 000000000..5e9b2f02c --- /dev/null +++ b/src/ioc/db/filters/test/arrRecord.c @@ -0,0 +1,135 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2002 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* arrRecord.c - minimal array record for test purposes: no processing */ + +/* + * Author: Ralph Lange + * + * vaguely implemented like parts of recWaveform.c by Bob Dalesio + * + */ + +#include + +#include "dbDefs.h" +#include "epicsPrint.h" +#include "dbAccess.h" +#include "dbEvent.h" +#include "dbFldTypes.h" +#include "recSup.h" +#include "recGbl.h" +#include "cantProceed.h" +#define GEN_SIZE_OFFSET +#include "arrRecord.h" +#undef GEN_SIZE_OFFSET +#include "epicsExport.h" + +/* Create RSET - Record Support Entry Table*/ +#define report NULL +#define initialize NULL +static long init_record(arrRecord *, int); +static long process(arrRecord *); +#define special NULL +#define get_value NULL +static long cvt_dbaddr(DBADDR *); +static long get_array_info(DBADDR *, long *, long *); +static long put_array_info(DBADDR *, long); +#define get_units NULL +#define get_precision NULL +#define get_enum_str NULL +#define get_enum_strs NULL +#define put_enum_str NULL +#define get_graphic_double NULL +#define get_control_double NULL +#define get_alarm_double NULL + +rset arrRSET = { + RSETNUMBER, + report, + initialize, + init_record, + process, + special, + get_value, + cvt_dbaddr, + get_array_info, + put_array_info, + get_units, + get_precision, + get_enum_str, + get_enum_strs, + put_enum_str, + get_graphic_double, + get_control_double, + get_alarm_double +}; +epicsExportAddress(rset, arrRSET); + +static long init_record(arrRecord *prec, int pass) +{ + if (pass == 0) { + if (prec->nelm <= 0) + prec->nelm = 1; + if (prec->ftvl > DBF_ENUM) + prec->ftvl = DBF_UCHAR; + prec->bptr = callocMustSucceed(prec->nelm, dbValueSize(prec->ftvl), + "arr calloc failed"); + + if (prec->nelm == 1) { + prec->nord = 1; + } else { + prec->nord = 0; + } + return 0; + } + return 0; +} + +static long process(arrRecord *prec) +{ + return 0; +} + +static long cvt_dbaddr(DBADDR *paddr) +{ + arrRecord *prec = (arrRecord *) paddr->precord; + + paddr->pfield = prec->bptr; + paddr->no_elements = prec->nelm; + paddr->field_type = prec->ftvl; + paddr->field_size = dbValueSize(prec->ftvl); + paddr->dbr_field_type = prec->ftvl; + + return 0; +} + +static long get_array_info(DBADDR *paddr, long *no_elements, long *offset) +{ + arrRecord *prec = (arrRecord *) paddr->precord; + + *no_elements = prec->nord; + *offset = prec->off; + + return 0; +} + +static long put_array_info(DBADDR *paddr, long nNew) +{ + arrRecord *prec = (arrRecord *) paddr->precord; + + prec->nord = nNew; + if (prec->nord > prec->nelm) + prec->nord = prec->nelm; + + return 0; +} diff --git a/src/ioc/db/filters/test/arrRecord.dbd b/src/ioc/db/filters/test/arrRecord.dbd new file mode 100644 index 000000000..25a45351d --- /dev/null +++ b/src/ioc/db/filters/test/arrRecord.dbd @@ -0,0 +1,32 @@ +include "menuGlobal.dbd" +recordtype(arr) { + include "dbCommon.dbd" + field(VAL, DBF_NOACCESS) { + prompt("Value") + special(SPC_DBADDR) + pp(TRUE) + extra("void *val") + } + field(NELM, DBF_ULONG) { + prompt("Number of Elements") + special(SPC_NOMOD) + initial("1") + } + field(FTVL, DBF_MENU) { + prompt("Field Type of Value") + special(SPC_NOMOD) + menu(menuFtype) + } + field(NORD, DBF_ULONG) { + prompt("Number elements read") + special(SPC_NOMOD) + } + field(OFF, DBF_ULONG) { + prompt("Offset into array") + } + field(BPTR, DBF_NOACCESS) { + prompt("Buffer Pointer") + special(SPC_NOMOD) + extra("void *bptr") + } +} diff --git a/src/ioc/db/filters/test/arrTest.cpp b/src/ioc/db/filters/test/arrTest.cpp new file mode 100644 index 000000000..690bd555c --- /dev/null +++ b/src/ioc/db/filters/test/arrTest.cpp @@ -0,0 +1,307 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne +* National Laboratory. +* Copyright (c) 2003 The Regents of the University of California, as +* Operator of Los Alamos National Laboratory. +* EPICS BASE is distributed subject to the Software License Agreement +* found in the file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +/* using stuff from softIoc.cpp by Andrew Johnson */ + +#include +#include +#include +#include +#include + +#include "registryFunction.h" +#include "epicsThread.h" +#include "epicsExit.h" +#include "epicsStdio.h" +#include "envDefs.h" +#include "dbStaticLib.h" +#include "subRecord.h" +#include "dbAddr.h" +#include "dbAccess.h" +#include "asDbLib.h" +#include "iocInit.h" +#include "iocsh.h" +#include "dbChannel.h" +#include "epicsInstallDir.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +#include "arrRecord.h" + +extern "C" int arrRecord_registerRecordDeviceDriver(struct dbBase *pdbbase); +extern "C" void (*pvar_func_arrInitialize)(void); + +#define DBD_FILE EPICS_BASE "/src/db/filters/test/O.Common/arrTest.dbd" +#define DB_FILE EPICS_BASE "/src/db/filters/test/arrTest.db" +#define EXIT_FILE EPICS_BASE "/db/softIocExit.db" +#define CA_SERVER_PORT "65535" + +#define PATTERN 0x55 + +const char *base_dbd = DBD_FILE; +const char *arr_db = DB_FILE; +const char *exit_db = EXIT_FILE; +const char *server_port = CA_SERVER_PORT; + +static void exitSubroutine(subRecord *precord) { + epicsExit((precord->a == 0.0) ? EXIT_SUCCESS : EXIT_FAILURE); +} + +static int fl_equals_array(short type, const db_field_log *pfl1, void *p2) { + for (int i = 0; i < pfl1->no_elements; i++) { + switch (type) { + case DBR_DOUBLE: + if (((epicsFloat64*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) { + testDiag("at index=%d: field log has %g, should be %d", + i, ((epicsFloat64*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]); + return 0; + } + break; + case DBR_LONG: + if (((epicsInt32*)pfl1->u.r.field)[i] != ((epicsInt32*)p2)[i]) { + testDiag("at index=%d: field log has %d, should be %d", + i, ((epicsInt32*)pfl1->u.r.field)[i], ((epicsInt32*)p2)[i]); + return 0; + } + break; + case DBR_STRING: + if (strtol(&((const char*)pfl1->u.r.field)[i*MAX_STRING_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]); + return 0; + } + break; + default: + return 0; + } + } + return 1; +} + +static void createAndOpen(const char *chan, const char *json, const char *type, dbChannel**pch, short no) { + ELLNODE *node; + chFilter *filter; + char name[80]; + + strncpy(name, chan, sizeof(name)-1); + strncat(name, json, sizeof(name)-strlen(name)-1); + + testOk(!!(*pch = dbChannelCreate(name)), "dbChannel with plugin arr %s created", type); + testOk((ellCount(&(*pch)->filters) == no), "channel has %d filter(s) in filter list", no); + + testOk(!(dbChannelOpen(*pch)), "dbChannel with plugin arr opened"); + + node = ellFirst(&(*pch)->pre_chain); + filter = CONTAINER(node, chFilter, pre_node); + testOk((ellCount(&(*pch)->pre_chain) == 0), "arr has no filter in pre chain"); + + node = ellFirst(&(*pch)->post_chain); + filter = CONTAINER(node, chFilter, post_node); + testOk((ellCount(&(*pch)->post_chain) == no), + "arr has %d filter(s) in post chain", no); +} + +static void testHead (const char *title, const char *typ = "") { + const char *line = "------------------------------------------------------------------------------"; + testDiag(line); + testDiag(title, typ); + testDiag(line); +} + +static void check(short dbr_type) { + dbChannel *pch; + db_field_log *pfl, *pfl2; + int status; + dbAddr valaddr; + dbAddr offaddr; + char *offname, *valname, *typname; + long ar[10] = {10,11,12,13,14,15,16,17,18,19}; + long *ar10_0_1 = ar; + long ar10_4_1[10] = {14,15,16,17,18,19,10,11,12,13}; + long ar5_0_1[10] = {12,13,14,15,16}; + long ar5_3_1[10] = {15,16,17,18,19}; + long ar5_5_1[10] = {17,18,19,10,11}; + long ar5_9_1[10] = {11,12,13,14,15}; + long ar5_0_2[10] = {12,14,16}; + long ar5_3_2[10] = {15,17,19}; + long ar5_5_2[10] = {17,19,11}; + long ar5_9_2[10] = {11,13,15}; + long ar5_0_3[10] = {12,15}; + long ar5_3_3[10] = {15,18}; + long ar5_5_3[10] = {17,10}; + long ar5_9_3[10] = {11,14}; + long off = 0; + + switch (dbr_type) { + case DBR_LONG: + offname = "x.OFF"; + valname = "x.VAL"; + typname = "long"; + break; + case DBR_DOUBLE: + offname = "y.OFF"; + valname = "y.VAL"; + typname = "double"; + break; + case DBR_STRING: + offname = "z.OFF"; + valname = "z.VAL"; + typname = "string"; + break; + default: + testDiag("Invalid data type %d", dbr_type); + } + + status = dbNameToAddr(offname, &offaddr); + + status = dbNameToAddr(valname, &valaddr); + status = dbPutField(&valaddr, DBR_LONG, ar, 10); + +#define TEST1(Size, Offset, Incr, Text) \ + testDiag("Offset: %d (%s)", Offset, Text); \ + off = Offset; \ + status = dbPutField(&offaddr, DBR_LONG, &off, 1); \ + pfl = db_create_read_log(pch); \ + testOk(pfl->type == dbfl_type_rec, "original field log has type rec"); \ + 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"); \ + testOk(fl_equals_array(dbr_type, pfl2, ar##Size##_##Offset##_##Incr), "array data correct"); \ + db_delete_field_log(pfl); + + /* Default: should not change anything */ + + testHead("Ten %s elements from rec, increment 1, full size (default)", typname); + createAndOpen(valname, "{\"arr\":{}}", "(default)", &pch, 1); + + testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type); + testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld)", valaddr.no_elements); + + TEST1(10, 0, 1, "no offset"); + TEST1(10, 4, 1, "wrapped"); + + dbChannelDelete(pch); + +#define TEST5(Incr, Left, Right, Type) \ + testHead("Five %s elements from rec, increment " #Incr ", " Type " addressing", typname); \ + createAndOpen(valname, "{\"arr\":{\"s\":" #Left ",\"e\":" #Right ",\"i\":" #Incr "}}", \ + "(" #Left ":" #Incr ":" #Right ")", &pch, 1); \ + testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type); \ + testOk(pch->final_no_elements == 4 / Incr + 1, "final no_elements correct (%d)", 4 / Incr + 1); \ + TEST1(5, 0, Incr, "no offset"); \ + TEST1(5, 3, Incr, "from upper block"); \ + TEST1(5, 5, Incr, "wrapped"); \ + TEST1(5, 9, Incr, "from lower block"); \ + dbChannelDelete(pch); + + /* Contiguous block of 5 */ + + TEST5(1, 2, 7, "regular"); + TEST5(1, -8, 7, "left side from-end"); + TEST5(1, 2, -3, "right side from-end"); + TEST5(1, -8, -3, "both sides from-end"); + + /* 5 elements with increment 2 */ + + TEST5(2, 2, 7, "regular"); + TEST5(2, -8, 7, "left side from-end"); + TEST5(2, 2, -3, "right side from-end"); + TEST5(2, -8, -3, "both sides from-end"); + + /* 5 elements with increment 3 */ + + TEST5(3, 2, 7, "regular"); + TEST5(3, -8, 7, "left side from-end"); + TEST5(3, 2, -3, "right side from-end"); + TEST5(3, -8, -3, "both sides from-end"); + + /* From buffer (plugin chain) */ + +#define TEST5B(Incr, Left, Right, Type) \ + testHead("Five %s elements from buffer, increment " #Incr ", " Type " addressing", typname); \ + createAndOpen(valname, "{\"arr\":{},\"arr\":{\"s\":" #Left ",\"e\":" #Right ",\"i\":" #Incr "}}", \ + "(" #Left ":" #Incr ":" #Right ")", &pch, 2); \ + testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type); \ + testOk(pch->final_no_elements == 4 / Incr + 1, "final no_elements correct (%d)", 4 / Incr + 1); \ + TEST1(5, 0, Incr, "no offset"); \ + dbChannelDelete(pch); + + /* Contiguous block of 5 */ + + TEST5B(1, 2, 7, "regular"); + TEST5B(1, -8, 7, "left side from-end"); + TEST5B(1, 2, -3, "right side from-end"); + TEST5B(1, -8, -3, "both sides from-end"); + + /* 5 elements with increment 2 */ + + TEST5B(2, 2, 7, "regular"); + TEST5B(2, -8, 7, "left side from-end"); + TEST5B(2, 2, -3, "right side from-end"); + TEST5B(2, -8, -3, "both sides from-end"); + + /* 5 elements with increment 3 */ + + TEST5B(3, 2, 7, "regular"); + TEST5B(3, -8, 7, "left side from-end"); + TEST5B(3, 2, -3, "right side from-end"); + TEST5B(3, -8, -3, "both sides from-end"); +} + +MAIN(arrTest) +{ + char *dbd_file = const_cast(base_dbd); + char *db_file = const_cast(arr_db); + const chFilterPlugin *plug; + char arr[] = "arr"; + int status; + + testPlan(1272); + + /* Prepare the IOC */ + + epicsEnvSet("EPICS_CA_SERVER_PORT", server_port); + + testOk1(!(status = dbLoadDatabase(dbd_file, NULL, NULL))); + if (status) epicsExit(EXIT_FAILURE); + + (*pvar_func_arrInitialize)(); + arrRecord_registerRecordDeviceDriver(pdbbase); + registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine); + + testOk1(!(status = dbLoadRecords(db_file, NULL))); + if (status) epicsExit(EXIT_FAILURE); + + /* Start the IOC */ + + iocInit(); + db_init_events(); + epicsThreadSleep(0.2); + + testOk(!!(plug = dbFindFilter(arr, strlen(arr))), "plugin arr registered correctly"); + + check(DBR_LONG); + check(DBR_DOUBLE); + check(DBR_STRING); + + dbFreeBase(pdbbase); + + return testDone(); + + epicsExit(EXIT_SUCCESS); + /*Note that the following statement will never be executed*/ + return 0; +} diff --git a/src/ioc/db/filters/test/arrTest.db b/src/ioc/db/filters/test/arrTest.db new file mode 100644 index 000000000..467cf0d08 --- /dev/null +++ b/src/ioc/db/filters/test/arrTest.db @@ -0,0 +1,15 @@ +record(arr, "x") { + field(DESC, "test array record") + field(NELM, "10") + field(FTVL, "LONG") +} +record(arr, "y") { + field(DESC, "test array record") + field(NELM, "10") + field(FTVL, "DOUBLE") +} +record(arr, "z") { + field(DESC, "test array record") + field(NELM, "10") + field(FTVL, "STRING") +} diff --git a/src/ioc/db/filters/test/dbndTest.c b/src/ioc/db/filters/test/dbndTest.c index 727b02906..1d3f543c5 100644 --- a/src/ioc/db/filters/test/dbndTest.c +++ b/src/ioc/db/filters/test/dbndTest.c @@ -102,7 +102,7 @@ MAIN(dbndTest) db_field_log *pfl, *pfl2; db_field_log fl1; - testPlan(0); + testPlan(61); db_init_events(); diff --git a/src/ioc/db/filters/test/tsTest.c b/src/ioc/db/filters/test/tsTest.c index 6dda76a21..f02c5d441 100644 --- a/src/ioc/db/filters/test/tsTest.c +++ b/src/ioc/db/filters/test/tsTest.c @@ -48,7 +48,7 @@ MAIN(tsTest) db_field_log fl1; db_field_log *pfl2; - testPlan(0); + testPlan(14); db_init_events(); From ef2814ec441ae42d96a2516b26d5a845851430e4 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:25:13 -0400 Subject: [PATCH 60/81] Fix bug in arr plugin (for incr <= 0), add tests for invalid and out-of-bound parameters --- src/ioc/db/filters/arr.c | 3 ++ src/ioc/db/filters/test/arrTest.cpp | 57 +++++++++++++++++++++-------- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/ioc/db/filters/arr.c b/src/ioc/db/filters/arr.c index 07e1309a5..b37f393ff 100644 --- a/src/ioc/db/filters/arr.c +++ b/src/ioc/db/filters/arr.c @@ -56,6 +56,9 @@ static void freePvt(void *pvt) static int parse_ok(void *pvt) { + myStruct *my = (myStruct*) pvt; + + if (my->incr <= 0) my->incr = 1; return 0; } diff --git a/src/ioc/db/filters/test/arrTest.cpp b/src/ioc/db/filters/test/arrTest.cpp index 690bd555c..4eb6daed8 100644 --- a/src/ioc/db/filters/test/arrTest.cpp +++ b/src/ioc/db/filters/test/arrTest.cpp @@ -121,13 +121,25 @@ static void testHead (const char *title, const char *typ = "") { testDiag(line); } +#define TEST1(Size, Offset, Incr, Text) \ + testDiag("Offset: %d (%s)", Offset, Text); \ + off = Offset; \ + status = dbPutField(&offaddr, DBR_LONG, &off, 1); \ + pfl = db_create_read_log(pch); \ + testOk(pfl->type == dbfl_type_rec, "original field log has type rec"); \ + 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"); \ + testOk(fl_equals_array(dbr_type, pfl2, ar##Size##_##Offset##_##Incr), "array data correct"); \ + db_delete_field_log(pfl); + static void check(short dbr_type) { dbChannel *pch; db_field_log *pfl, *pfl2; int status; dbAddr valaddr; dbAddr offaddr; - char *offname, *valname, *typname; + const char *offname = NULL, *valname = NULL, *typname = NULL; long ar[10] = {10,11,12,13,14,15,16,17,18,19}; long *ar10_0_1 = ar; long ar10_4_1[10] = {14,15,16,17,18,19,10,11,12,13}; @@ -170,29 +182,42 @@ static void check(short dbr_type) { status = dbNameToAddr(valname, &valaddr); status = dbPutField(&valaddr, DBR_LONG, ar, 10); -#define TEST1(Size, Offset, Incr, Text) \ - testDiag("Offset: %d (%s)", Offset, Text); \ - off = Offset; \ - status = dbPutField(&offaddr, DBR_LONG, &off, 1); \ - pfl = db_create_read_log(pch); \ - testOk(pfl->type == dbfl_type_rec, "original field log has type rec"); \ - 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"); \ - testOk(fl_equals_array(dbr_type, pfl2, ar##Size##_##Offset##_##Incr), "array data correct"); \ - db_delete_field_log(pfl); - /* Default: should not change anything */ testHead("Ten %s elements from rec, increment 1, full size (default)", typname); createAndOpen(valname, "{\"arr\":{}}", "(default)", &pch, 1); - testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type); testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld)", valaddr.no_elements); - TEST1(10, 0, 1, "no offset"); TEST1(10, 4, 1, "wrapped"); + dbChannelDelete(pch); + testHead("Ten %s elements from rec, increment 1, out-of-bound start parameter", typname); + createAndOpen(valname, "{\"arr\":{\"s\":-500}}", "out-of-bound start", &pch, 1); + testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type); + testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld)", valaddr.no_elements); + TEST1(10, 4, 1, "wrapped"); + dbChannelDelete(pch); + + testHead("Ten %s elements from rec, increment 1, out-of-bound end parameter", typname); + createAndOpen(valname, "{\"arr\":{\"e\":500}}", "out-of-bound end", &pch, 1); + testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type); + testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld)", valaddr.no_elements); + TEST1(10, 4, 1, "wrapped"); + dbChannelDelete(pch); + + testHead("Ten %s elements from rec, increment 1, zero increment parameter", typname); + createAndOpen(valname, "{\"arr\":{\"i\":0}}", "zero increment", &pch, 1); + testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type); + testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld)", valaddr.no_elements); + TEST1(10, 4, 1, "wrapped"); + dbChannelDelete(pch); + + testHead("Ten %s elements from rec, increment 1, invalid increment parameter", typname); + createAndOpen(valname, "{\"arr\":{\"i\":-30}}", "invalid increment", &pch, 1); + testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type); + testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld)", valaddr.no_elements); + TEST1(10, 4, 1, "wrapped"); dbChannelDelete(pch); #define TEST5(Incr, Left, Right, Type) \ @@ -269,7 +294,7 @@ MAIN(arrTest) char arr[] = "arr"; int status; - testPlan(1272); + testPlan(1404); /* Prepare the IOC */ From 0d9d9927171781f0255c817258213856071f73b1 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:25:14 -0400 Subject: [PATCH 61/81] Add [s:i:e] sub-array shorthand parsing and unit test --- src/ioc/db/dbChannel.c | 92 ++++++++++++++++++++ src/ioc/db/test/Makefile | 5 ++ src/ioc/db/test/arrShorthandTest.c | 130 +++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+) create mode 100644 src/ioc/db/test/arrShorthandTest.c diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index f635db5dd..ba7ba259f 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -324,6 +324,53 @@ long dbChannelTest(const char *name) return status; } +#define TRY(Func, Arg) \ +if (Func) { \ + result = Func Arg; \ + if (result != parse_continue) goto failure; \ +} + +static void insertArrPlugin(dbChannel* chan, epicsInt32 start, epicsInt32 incr, epicsInt32 end) { + chFilter *filter; + const chFilterPlugin *plug; + parse_result result; + + plug = dbFindFilter("arr", 3); + if (!plug) { + printf("dbChannelCreate: Required channel filter 'arr' not found\n"); + return; + } + + /* FIXME: Use a free-list */ + filter = (chFilter *) callocMustSucceed(1, sizeof(*filter), "Creating 'arr' dbChannel filter"); + filter->chan = chan; + filter->plug = plug; + filter->puser = NULL; + + TRY(filter->plug->fif->parse_start, (filter)); + TRY(filter->plug->fif->parse_start_map, (filter)); + if (start) { + TRY(filter->plug->fif->parse_map_key, (filter, "s", 1)); + TRY(filter->plug->fif->parse_integer, (filter, start)); + } + if (incr) { + TRY(filter->plug->fif->parse_map_key, (filter, "i", 1)); + TRY(filter->plug->fif->parse_integer, (filter, incr)); + } + if (end) { + TRY(filter->plug->fif->parse_map_key, (filter, "e", 1)); + TRY(filter->plug->fif->parse_integer, (filter, end)); + } + TRY(filter->plug->fif->parse_end_map, (filter)); + TRY(filter->plug->fif->parse_end, (filter)); + + ellAdd(&chan->filters, &filter->list_node); + return; + + failure: + free(filter); // FIXME: Use free-list +} + /* Stolen from dbAccess.c: */ static short mapDBFToDBR[DBF_NTYPES] = { @@ -402,6 +449,51 @@ dbChannel * dbChannelCreate(const char *name) pname++; } + if (*pname == '[') { + /* Array subset modifier that calls the arr plugin */ + epicsInt32 start = 0; + epicsInt32 end = 0; + epicsInt32 incr = 0; + char * pnext; + short exist; + + pname++; + start = strtol(pname, &pnext, 0); + exist = pnext - pname; + pname = pnext; + if (*pname == ']' && exist) { + end = start + 1; + goto insertplug; + } + if (*pname != ':') { + status = S_dbLib_fieldNotFound; + goto finish; + } + pname++; + incr = strtol(pname, &pnext, 0); + pname = pnext; + if (*pname == ']') { + end = incr; + incr = 0; + goto insertplug; + } + if (*pname != ':') { + status = S_dbLib_fieldNotFound; + goto finish; + } + pname++; + end = strtol(pname, &pnext, 0); + pname = pnext; + if (*pname != ']') { + status = S_dbLib_fieldNotFound; + goto finish; + } + insertplug: + insertArrPlugin(chan, start, incr, end); + + pname++; + } + /* JSON may follow a $ */ if (*pname == '{') { status = chf_parse(chan, &pname); diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index bed566e824..b0e3fab58 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -30,6 +30,11 @@ chfPluginTest_SRCS += chfPluginTest.c OBJS_IOC_vxWorks += chfPluginTest TESTS += chfPluginTest +TESTPROD_HOST += arrShorthandTest +arrShorthandTest_SRCS += arrShorthandTest.c +OBJS_IOC_vxWorks += arrShorthandTest +TESTS += arrShorthandTest + TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES diff --git a/src/ioc/db/test/arrShorthandTest.c b/src/ioc/db/test/arrShorthandTest.c new file mode 100644 index 000000000..95504a858 --- /dev/null +++ b/src/ioc/db/test/arrShorthandTest.c @@ -0,0 +1,130 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + + /* + * Test the shorthand array notation [ start : incr : end ] + * by registering a thin fake arr plugin + * and checking if values are forwarded correctly + */ + +#include + +#include "chfPlugin.h" +#include "dbStaticLib.h" +#include "dbAccessDefs.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +typedef struct myStruct { + epicsInt32 start; + epicsInt32 incr; + epicsInt32 end; +} myStruct; + +static const +chfPluginArgDef opts[] = { + chfInt32 (myStruct, start, "s", 0, 1), + chfInt32 (myStruct, incr, "i", 0, 1), + chfInt32 (myStruct, end, "e", 0, 1), + chfPluginArgEnd +}; + +static myStruct my; + +static void * allocPvt(void) +{ + my.start = 0; + my.incr = 1; + my.end = 0; + return &my; +} + +static int parse_ok(void *user) +{ + return parse_continue; +} + +static chfPluginIf myPif = { + allocPvt, + NULL, /* freePvt, */ + + NULL, /* parse_error, */ + parse_ok, + + NULL, /* channel_open, */ + NULL, /* channelRegisterPre, */ + NULL, /* channelRegisterPost, */ + NULL, /* channel_report, */ + NULL /* channel_close */ +}; + +static int checkValues(epicsUInt32 s, epicsUInt32 i, epicsUInt32 e) { + if (s == my.start && i == my.incr && e == my.end) + return 1; + else + return 0; +} + +static void testHead (char* title) { + testDiag("--------------------------------------------------------"); + testDiag(title); + testDiag("--------------------------------------------------------"); +} + +MAIN(chfPluginTest) +{ + dbChannel *pch; + + testPlan(28); + + db_init_events(); + + testHead("Set up database"); + testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.dbx", ".:..", NULL)); + testOk(!!pdbbase, "pdbbase was set"); + + testHead("Register plugin"); + testOk(!chfPluginRegister("arr", &myPif, opts), "register fake arr plugin"); + +#define TESTBAD(Title, Expr) \ + testDiag(Title); \ + testOk(!(pch = dbChannelCreate("x." Expr)), "dbChannelCreate (" Expr ") fails"); \ + if (pch) dbChannelDelete(pch); + +#define TESTGOOD(Title, Expr, Start, Incr, End) \ + testDiag(Title); \ + testOk(!!(pch = dbChannelCreate("x." Expr)), "dbChannelCreate (" Expr ")"); \ + testOk(checkValues(Start, Incr, End), "parameters set correctly: s=%d i=%d e=%d", Start, Incr, End); \ + if (pch) dbChannelDelete(pch); + + TESTBAD("no parameters []", "[]"); + TESTBAD("invalid char at beginning [x", "[x"); + TESTBAD("invalid char after 1st arg [2x", "[2x"); + TESTBAD("invalid char after 2nd arg [2:3x", "[2:3x"); + TESTBAD("invalid char after 3rd arg [2:3:4x", "[2:3:4x"); + + TESTGOOD("one element [index]", "[2]", 2, 1, 3); + TESTGOOD("to end [s:]", "[2:]", 2, 1, 0); + TESTGOOD("to end [s::]", "[2::]", 2, 1, 0); + TESTGOOD("to end with incr [s:i:]", "[2:3:]", 2, 3, 0); + TESTGOOD("from beginning [:e]", "[:2]", 0, 1, 2); + TESTGOOD("from beginning [::e]", "[::2]", 0, 1, 2); + TESTGOOD("from begin with incr [:i:e]", "[:3:2]", 0, 3, 2); + TESTGOOD("range [s:e]", "[2:4]", 2, 1, 4); + TESTGOOD("range [s::e]", "[2::4]", 2, 1, 4); + TESTGOOD("range with incr [s:i:e]", "[2:3:4]", 2, 3, 4); + + dbFreeBase(pdbbase); + + return testDone(); +} + From 77eba532227842e00d6463afd9f6fd1ea20c487b Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:25:14 -0400 Subject: [PATCH 62/81] Change arr plugin behaviour from exclusive to inclusive range end specifier --- src/ioc/db/dbChannel.c | 113 ++++++++++++++++------------ src/ioc/db/filters/arr.c | 9 ++- src/ioc/db/filters/test/arrTest.cpp | 90 ++++++++++++---------- src/ioc/db/test/arrShorthandTest.c | 17 ++--- 4 files changed, 126 insertions(+), 103 deletions(-) diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index ba7ba259f..6385c8801 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -330,15 +330,64 @@ if (Func) { \ if (result != parse_continue) goto failure; \ } -static void insertArrPlugin(dbChannel* chan, epicsInt32 start, epicsInt32 incr, epicsInt32 end) { +static long parseArrayRange(dbChannel* chan, const char *pname, const char **ppnext) { + epicsInt32 start = 0; + epicsInt32 end = -1; + epicsInt32 incr = 1; + epicsInt32 l; + char *pnext; + short exist; chFilter *filter; const chFilterPlugin *plug; parse_result result; + long status = 0; + + /* If no number is present, strtol() returns 0 and sets pnext=pname, + else pnext points to the first char after the number */ + pname++; + l = strtol(pname, &pnext, 0); + exist = pnext - pname; + if (exist) start = l; + pname = pnext; + if (*pname == ']' && exist) { + end = start; + goto insertplug; + } + if (*pname != ':') { + status = S_dbLib_fieldNotFound; + goto finish; + } + pname++; + l = strtol(pname, &pnext, 0); + exist = pnext - pname; + pname = pnext; + if (*pname == ']') { + if (exist) end = l; + goto insertplug; + } + if (exist) incr = l; + if (*pname != ':') { + status = S_dbLib_fieldNotFound; + goto finish; + } + pname++; + l = strtol(pname, &pnext, 0); + exist = pnext - pname; + if (exist) end = l; + pname = pnext; + if (*pname != ']') { + status = S_dbLib_fieldNotFound; + goto finish; + } + + insertplug: + pname++; + *ppnext = pname; plug = dbFindFilter("arr", 3); if (!plug) { - printf("dbChannelCreate: Required channel filter 'arr' not found\n"); - return; + status = S_dbLib_fieldNotFound; + goto finish; } /* FIXME: Use a free-list */ @@ -349,15 +398,15 @@ static void insertArrPlugin(dbChannel* chan, epicsInt32 start, epicsInt32 incr, TRY(filter->plug->fif->parse_start, (filter)); TRY(filter->plug->fif->parse_start_map, (filter)); - if (start) { + if (start != 0) { TRY(filter->plug->fif->parse_map_key, (filter, "s", 1)); TRY(filter->plug->fif->parse_integer, (filter, start)); } - if (incr) { + if (incr != 1) { TRY(filter->plug->fif->parse_map_key, (filter, "i", 1)); TRY(filter->plug->fif->parse_integer, (filter, incr)); } - if (end) { + if (end != -1) { TRY(filter->plug->fif->parse_map_key, (filter, "e", 1)); TRY(filter->plug->fif->parse_integer, (filter, end)); } @@ -365,10 +414,14 @@ static void insertArrPlugin(dbChannel* chan, epicsInt32 start, epicsInt32 incr, TRY(filter->plug->fif->parse_end, (filter)); ellAdd(&chan->filters, &filter->list_node); - return; + return 0; failure: free(filter); // FIXME: Use free-list + status = S_dbLib_fieldNotFound; + + finish: + return status; } /* Stolen from dbAccess.c: */ @@ -450,51 +503,11 @@ dbChannel * dbChannelCreate(const char *name) } if (*pname == '[') { - /* Array subset modifier that calls the arr plugin */ - epicsInt32 start = 0; - epicsInt32 end = 0; - epicsInt32 incr = 0; - char * pnext; - short exist; - - pname++; - start = strtol(pname, &pnext, 0); - exist = pnext - pname; - pname = pnext; - if (*pname == ']' && exist) { - end = start + 1; - goto insertplug; - } - if (*pname != ':') { - status = S_dbLib_fieldNotFound; - goto finish; - } - pname++; - incr = strtol(pname, &pnext, 0); - pname = pnext; - if (*pname == ']') { - end = incr; - incr = 0; - goto insertplug; - } - if (*pname != ':') { - status = S_dbLib_fieldNotFound; - goto finish; - } - pname++; - end = strtol(pname, &pnext, 0); - pname = pnext; - if (*pname != ']') { - status = S_dbLib_fieldNotFound; - goto finish; - } - insertplug: - insertArrPlugin(chan, start, incr, end); - - pname++; + status = parseArrayRange(chan, pname, &pname); + if (status) goto finish; } - /* JSON may follow a $ */ + /* JSON may follow */ if (*pname == '{') { status = chf_parse(chan, &pname); if (status) goto finish; diff --git a/src/ioc/db/filters/arr.c b/src/ioc/db/filters/arr.c index b37f393ff..7fa392a08 100644 --- a/src/ioc/db/filters/arr.c +++ b/src/ioc/db/filters/arr.c @@ -43,6 +43,7 @@ static void * allocPvt(void) { myStruct *my = (myStruct*) freeListCalloc(myStructFreeList); my->incr = 1; + my->end = -1; return (void *) my; } @@ -75,11 +76,11 @@ static long wrapArrayIndices(long *start, const long increment, long *end, const if (*start < 0) *start = 0; if (*start > no_elements) *start = no_elements; - if (*end <= 0) *end = no_elements + *end; - if (*end <= 0) *end = 0; - if (*end > no_elements) *end = no_elements; + if (*end < 0) *end = no_elements + *end; + if (*end < 0) *end = 0; + if (*end >= no_elements) *end = no_elements - 1; - if (*end - *start >= 1) len = 1 + (*end - *start - 1 ) / increment; + if (*end - *start >= 0) len = 1 + (*end - *start) / increment; return len; } diff --git a/src/ioc/db/filters/test/arrTest.cpp b/src/ioc/db/filters/test/arrTest.cpp index 4eb6daed8..76918ac30 100644 --- a/src/ioc/db/filters/test/arrTest.cpp +++ b/src/ioc/db/filters/test/arrTest.cpp @@ -186,37 +186,47 @@ static void check(short dbr_type) { testHead("Ten %s elements from rec, increment 1, full size (default)", typname); createAndOpen(valname, "{\"arr\":{}}", "(default)", &pch, 1); - testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type); - testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld)", valaddr.no_elements); + testOk(pch->final_type == valaddr.field_type, + "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); + testOk(pch->final_no_elements == valaddr.no_elements, + "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); TEST1(10, 0, 1, "no offset"); TEST1(10, 4, 1, "wrapped"); dbChannelDelete(pch); testHead("Ten %s elements from rec, increment 1, out-of-bound start parameter", typname); createAndOpen(valname, "{\"arr\":{\"s\":-500}}", "out-of-bound start", &pch, 1); - testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type); - testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld)", valaddr.no_elements); + testOk(pch->final_type == valaddr.field_type, + "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); + testOk(pch->final_no_elements == valaddr.no_elements, + "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); TEST1(10, 4, 1, "wrapped"); dbChannelDelete(pch); testHead("Ten %s elements from rec, increment 1, out-of-bound end parameter", typname); createAndOpen(valname, "{\"arr\":{\"e\":500}}", "out-of-bound end", &pch, 1); - testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type); - testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld)", valaddr.no_elements); + testOk(pch->final_type == valaddr.field_type, + "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); + testOk(pch->final_no_elements == valaddr.no_elements, + "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); TEST1(10, 4, 1, "wrapped"); dbChannelDelete(pch); testHead("Ten %s elements from rec, increment 1, zero increment parameter", typname); createAndOpen(valname, "{\"arr\":{\"i\":0}}", "zero increment", &pch, 1); - testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type); - testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld)", valaddr.no_elements); + testOk(pch->final_type == valaddr.field_type, + "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); + testOk(pch->final_no_elements == valaddr.no_elements, + "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); TEST1(10, 4, 1, "wrapped"); dbChannelDelete(pch); testHead("Ten %s elements from rec, increment 1, invalid increment parameter", typname); createAndOpen(valname, "{\"arr\":{\"i\":-30}}", "invalid increment", &pch, 1); - testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type); - testOk(pch->final_no_elements == valaddr.no_elements, "final no_elements unchanged (%ld)", valaddr.no_elements); + testOk(pch->final_type == valaddr.field_type, + "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); + testOk(pch->final_no_elements == valaddr.no_elements, + "final no_elements unchanged (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); TEST1(10, 4, 1, "wrapped"); dbChannelDelete(pch); @@ -224,8 +234,10 @@ static void check(short dbr_type) { testHead("Five %s elements from rec, increment " #Incr ", " Type " addressing", typname); \ createAndOpen(valname, "{\"arr\":{\"s\":" #Left ",\"e\":" #Right ",\"i\":" #Incr "}}", \ "(" #Left ":" #Incr ":" #Right ")", &pch, 1); \ - testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type); \ - testOk(pch->final_no_elements == 4 / Incr + 1, "final no_elements correct (%d)", 4 / Incr + 1); \ + testOk(pch->final_type == valaddr.field_type, \ + "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); \ + testOk(pch->final_no_elements == 4 / Incr + 1, \ + "final no_elements correct (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); \ TEST1(5, 0, Incr, "no offset"); \ TEST1(5, 3, Incr, "from upper block"); \ TEST1(5, 5, Incr, "wrapped"); \ @@ -234,24 +246,24 @@ static void check(short dbr_type) { /* Contiguous block of 5 */ - TEST5(1, 2, 7, "regular"); - TEST5(1, -8, 7, "left side from-end"); - TEST5(1, 2, -3, "right side from-end"); - TEST5(1, -8, -3, "both sides from-end"); + TEST5(1, 2, 6, "regular"); + TEST5(1, -8, 6, "left side from-end"); + TEST5(1, 2, -4, "right side from-end"); + TEST5(1, -8, -4, "both sides from-end"); /* 5 elements with increment 2 */ - TEST5(2, 2, 7, "regular"); - TEST5(2, -8, 7, "left side from-end"); - TEST5(2, 2, -3, "right side from-end"); - TEST5(2, -8, -3, "both sides from-end"); + TEST5(2, 2, 6, "regular"); + TEST5(2, -8, 6, "left side from-end"); + TEST5(2, 2, -4, "right side from-end"); + TEST5(2, -8, -4, "both sides from-end"); /* 5 elements with increment 3 */ - TEST5(3, 2, 7, "regular"); - TEST5(3, -8, 7, "left side from-end"); - TEST5(3, 2, -3, "right side from-end"); - TEST5(3, -8, -3, "both sides from-end"); + TEST5(3, 2, 6, "regular"); + TEST5(3, -8, 6, "left side from-end"); + TEST5(3, 2, -4, "right side from-end"); + TEST5(3, -8, -4, "both sides from-end"); /* From buffer (plugin chain) */ @@ -259,31 +271,33 @@ static void check(short dbr_type) { testHead("Five %s elements from buffer, increment " #Incr ", " Type " addressing", typname); \ createAndOpen(valname, "{\"arr\":{},\"arr\":{\"s\":" #Left ",\"e\":" #Right ",\"i\":" #Incr "}}", \ "(" #Left ":" #Incr ":" #Right ")", &pch, 2); \ - testOk(pch->final_type == valaddr.field_type, "final type unchanged (%d)", valaddr.field_type); \ - testOk(pch->final_no_elements == 4 / Incr + 1, "final no_elements correct (%d)", 4 / Incr + 1); \ + testOk(pch->final_type == valaddr.field_type, \ + "final type unchanged (%d->%d)", valaddr.field_type, pch->final_type); \ + testOk(pch->final_no_elements == 4 / Incr + 1, \ + "final no_elements correct (%ld->%ld)", valaddr.no_elements, pch->final_no_elements); \ TEST1(5, 0, Incr, "no offset"); \ dbChannelDelete(pch); /* Contiguous block of 5 */ - TEST5B(1, 2, 7, "regular"); - TEST5B(1, -8, 7, "left side from-end"); - TEST5B(1, 2, -3, "right side from-end"); - TEST5B(1, -8, -3, "both sides from-end"); + TEST5B(1, 2, 6, "regular"); + TEST5B(1, -8, 6, "left side from-end"); + TEST5B(1, 2, -4, "right side from-end"); + TEST5B(1, -8, -4, "both sides from-end"); /* 5 elements with increment 2 */ - TEST5B(2, 2, 7, "regular"); - TEST5B(2, -8, 7, "left side from-end"); - TEST5B(2, 2, -3, "right side from-end"); - TEST5B(2, -8, -3, "both sides from-end"); + TEST5B(2, 2, 6, "regular"); + TEST5B(2, -8, 6, "left side from-end"); + TEST5B(2, 2, -4, "right side from-end"); + TEST5B(2, -8, -4, "both sides from-end"); /* 5 elements with increment 3 */ - TEST5B(3, 2, 7, "regular"); - TEST5B(3, -8, 7, "left side from-end"); - TEST5B(3, 2, -3, "right side from-end"); - TEST5B(3, -8, -3, "both sides from-end"); + TEST5B(3, 2, 6, "regular"); + TEST5B(3, -8, 6, "left side from-end"); + TEST5B(3, 2, -4, "right side from-end"); + TEST5B(3, -8, -4, "both sides from-end"); } MAIN(arrTest) diff --git a/src/ioc/db/test/arrShorthandTest.c b/src/ioc/db/test/arrShorthandTest.c index 95504a858..307b73766 100644 --- a/src/ioc/db/test/arrShorthandTest.c +++ b/src/ioc/db/test/arrShorthandTest.c @@ -44,21 +44,16 @@ static void * allocPvt(void) { my.start = 0; my.incr = 1; - my.end = 0; + my.end = -1; return &my; } -static int parse_ok(void *user) -{ - return parse_continue; -} - static chfPluginIf myPif = { allocPvt, NULL, /* freePvt, */ NULL, /* parse_error, */ - parse_ok, + NULL, /* parse_ok, */ NULL, /* channel_open, */ NULL, /* channelRegisterPre, */ @@ -112,10 +107,10 @@ MAIN(chfPluginTest) TESTBAD("invalid char after 2nd arg [2:3x", "[2:3x"); TESTBAD("invalid char after 3rd arg [2:3:4x", "[2:3:4x"); - TESTGOOD("one element [index]", "[2]", 2, 1, 3); - TESTGOOD("to end [s:]", "[2:]", 2, 1, 0); - TESTGOOD("to end [s::]", "[2::]", 2, 1, 0); - TESTGOOD("to end with incr [s:i:]", "[2:3:]", 2, 3, 0); + TESTGOOD("one element [index]", "[2]", 2, 1, 2); + TESTGOOD("to end [s:]", "[2:]", 2, 1, -1); + TESTGOOD("to end [s::]", "[2::]", 2, 1, -1); + TESTGOOD("to end with incr [s:i:]", "[2:3:]", 2, 3, -1); TESTGOOD("from beginning [:e]", "[:2]", 0, 1, 2); TESTGOOD("from beginning [::e]", "[::2]", 0, 1, 2); TESTGOOD("from begin with incr [:i:e]", "[:3:2]", 0, 3, 2); From 4893d0ef8b11ecd20438a1c7080a53c55e01c70f Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:25:15 -0400 Subject: [PATCH 63/81] Add dbState library that provides a means of synchronizing plugins to events. --- src/ioc/db/Makefile | 8 ++- src/ioc/db/dbIocRegister.c | 52 +++++++++++++++++ src/ioc/db/dbState.c | 101 ++++++++++++++++++++++++++++++++++ src/ioc/db/dbState.h | 92 +++++++++++++++++++++++++++++++ src/ioc/db/test/Makefile | 5 ++ src/ioc/db/test/dbStateTest.c | 56 +++++++++++++++++++ 6 files changed, 311 insertions(+), 3 deletions(-) create mode 100644 src/ioc/db/dbState.c create mode 100644 src/ioc/db/dbState.h create mode 100644 src/ioc/db/test/dbStateTest.c diff --git a/src/ioc/db/Makefile b/src/ioc/db/Makefile index 7e3c2526b..c18b605b4 100644 --- a/src/ioc/db/Makefile +++ b/src/ioc/db/Makefile @@ -34,6 +34,7 @@ INC += initHooks.h INC += recGbl.h INC += dbIocRegister.h INC += chfPlugin.h +INC += dbState.h INC += db_access_routines.h INC += db_convert.h @@ -85,8 +86,9 @@ dbCore_SRCS += dbContextReadNotifyCache.cpp dbCore_SRCS += templateInstances.cpp dbCore_SRCS += dbIocRegister.c dbCore_SRCS += chfPlugin.c +dbCore_SRCS += dbState.c -LIB_SRCS += ts.c -LIB_SRCS += dbnd.c -LIB_SRCS += arr.c +dbCore_SRCS += ts.c +dbCore_SRCS += dbnd.c +dbCore_SRCS += arr.c diff --git a/src/ioc/db/dbIocRegister.c b/src/ioc/db/dbIocRegister.c index 60965f200..c4fa3aedf 100644 --- a/src/ioc/db/dbIocRegister.c +++ b/src/ioc/db/dbIocRegister.c @@ -21,6 +21,7 @@ #include "dbNotify.h" #include "callback.h" #include "dbIocRegister.h" +#include "dbState.h" /* dbLoadDatabase */ static const iocshArg dbLoadDatabaseArg0 = { "file name",iocshArgString}; @@ -297,6 +298,51 @@ static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args) callbackSetQueueSize(args[0].ival); } +/* dbStateCreate */ +static const iocshArg dbStateCreateArg0 = { "name", iocshArgString }; +static const iocshArg * const dbStateCreateArgs[] = { &dbStateCreateArg0 }; +static const iocshFuncDef dbStateCreateFuncDef = { "dbStateCreate", 1, dbStateCreateArgs }; +static void dbStateCreateCallFunc (const iocshArgBuf *args) +{ + dbStateCreate(args[0].sval); +} + +/* dbStateSet */ +static const iocshArg dbStateSetArg0 = { "id", iocshArgInt }; +static const iocshArg * const dbStateSetArgs[] = { &dbStateSetArg0 }; +static const iocshFuncDef dbStateSetFuncDef = { "dbStateSet", 1, dbStateSetArgs }; +static void dbStateSetCallFunc (const iocshArgBuf *args) +{ + dbStateSet((dbStateId) args[0].ival); +} + +/* dbStateClear */ +static const iocshArg dbStateClearArg0 = { "id", iocshArgInt }; +static const iocshArg * const dbStateClearArgs[] = { &dbStateClearArg0 }; +static const iocshFuncDef dbStateClearFuncDef = { "dbStateClear", 1, dbStateClearArgs }; +static void dbStateClearCallFunc (const iocshArgBuf *args) +{ + dbStateClear((dbStateId) args[0].ival); +} + +/* dbStateShow */ +static const iocshArg dbStateShowArg0 = { "id", iocshArgInt }; +static const iocshArg dbStateShowArg1 = { "level", iocshArgInt }; +static const iocshArg * const dbStateShowArgs[] = { &dbStateShowArg0, &dbStateShowArg1 }; +static const iocshFuncDef dbStateShowFuncDef = { "dbStateShow", 2, dbStateShowArgs }; +static void dbStateShowCallFunc (const iocshArgBuf *args) +{ + dbStateShow((dbStateId) args[0].ival, args[1].ival); +} + +/* dbStateShowAll */ +static const iocshArg dbStateShowAllArg0 = { "level", iocshArgInt }; +static const iocshArg * const dbStateShowAllArgs[] = { &dbStateShowAllArg0 }; +static const iocshFuncDef dbStateShowAllFuncDef = { "dbStateShowAll", 1, dbStateShowAllArgs }; +static void dbStateShowAllCallFunc (const iocshArgBuf *args) +{ + dbStateShowAll(args[0].ival); +} void epicsShareAPI dbIocRegister(void) { @@ -342,4 +388,10 @@ void epicsShareAPI dbIocRegister(void) iocshRegister(&scanpiolFuncDef,scanpiolCallFunc); iocshRegister(&callbackSetQueueSizeFuncDef,callbackSetQueueSizeCallFunc); + + iocshRegister(&dbStateCreateFuncDef, dbStateCreateCallFunc); + iocshRegister(&dbStateSetFuncDef, dbStateSetCallFunc); + iocshRegister(&dbStateClearFuncDef, dbStateClearCallFunc); + iocshRegister(&dbStateShowFuncDef, dbStateShowCallFunc); + iocshRegister(&dbStateShowAllFuncDef, dbStateShowAllCallFunc); } diff --git a/src/ioc/db/dbState.c b/src/ioc/db/dbState.c new file mode 100644 index 000000000..11ce519d6 --- /dev/null +++ b/src/ioc/db/dbState.c @@ -0,0 +1,101 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include +#include + +#include "iocsh.h" +#include "epicsMutex.h" +#include "ellLib.h" +#include "dbStaticLib.h" +#include "dbDefs.h" + +#include "dbState.h" + +static ELLLIST states = ELLLIST_INIT; + +typedef struct dbState { + ELLNODE node; + int status; + char *name; + epicsMutexId lock; /* FIXME: Use atomic operations instead */ +} dbState; + +dbStateId dbStateFind(const char *name) +{ + ELLNODE *node; + dbStateId id; + + for (node = ellFirst(&states); node; node = ellNext(node)) { + id = CONTAINER(node, dbState, node); + if (strcmp(id->name, name) == 0) + return id; + } + return NULL; +} + +dbStateId dbStateCreate(const char *name) +{ + dbStateId id; + + if ((id = dbStateFind(name))) + return id; + + id = callocMustSucceed(1, sizeof(dbState), "createDbState"); + id->name = strdup(name); + id->lock = epicsMutexMustCreate(); + ellAdd(&states, &id->node); + + return id; +} + +void dbStateSet(dbStateId id) +{ + epicsMutexMustLock(id->lock); + id->status = 1; + epicsMutexUnlock(id->lock); +} + +void dbStateClear(dbStateId id) +{ + epicsMutexMustLock(id->lock); + id->status = 0; + epicsMutexUnlock(id->lock); +} + +int dbStateGet(dbStateId id) +{ + int status; + + epicsMutexMustLock(id->lock); + status = id->status; + epicsMutexUnlock(id->lock); + return status; +} + +void dbStateShow(dbStateId id, unsigned int level) +{ + if (level >=1) + printf("id %#x '%s' : ", (unsigned int) id, id->name); + printf("%s\n", dbStateGet(id) ? "TRUE" : "FALSE"); +} + +void dbStateShowAll(unsigned int level) +{ + ELLNODE *node; + dbStateId id; + + for (node = ellFirst(&states); node; node = ellNext(node)) { + id = CONTAINER(node, dbState, node); + dbStateShow(id, level+1); + } +} diff --git a/src/ioc/db/dbState.h b/src/ioc/db/dbState.h new file mode 100644 index 000000000..abd23259e --- /dev/null +++ b/src/ioc/db/dbState.h @@ -0,0 +1,92 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#ifndef INCdbStateH +#define INCdbStateH + +#include "shareLib.h" + +/** @file dbState.h + * @brief Generic IOC state facility + * + * This library provides a simple global flag facility that can be used to + * synchronize e.g. plugins with IOC-wide states, that may be derived from + * events (either soft events or hard events coming from specialized timing + * and event hardware). + * + * A subset of this API is provided as IOC Shell commands to allow + * command line debugging. + * + */ + +typedef struct dbState *dbStateId; + +/** @brief Create db state. + * + * Creates a new db state with the specified 'name', returning the new id. + * If state with that name already exists, the existing state's id is returned. + * + * Also provided as an IOC Shell command. + * + * @param name Db state name. + * @return Id of db state, NULL for failure. + */ +epicsShareFunc dbStateId dbStateCreate(const char *name); + +/** @brief Find db state. + * + * @param name Db state name. + * @return Id of db state, NULL if not found. + */ +epicsShareFunc dbStateId dbStateFind(const char *name); + +/** @brief Set db state to TRUE. + * + * Also provided as an IOC Shell command. + * + * @param id Db state id. + */ +epicsShareFunc void dbStateSet(dbStateId id); + +/** @brief Set db state to FALSE. + * + * Also provided as an IOC Shell command. + * + * @param id Db state id. + */ +epicsShareFunc void dbStateClear(dbStateId id); + +/** @brief Get db state. + * + * @param id Db state id. + * @return Current db state (0|1). + */ +epicsShareFunc int dbStateGet(dbStateId id); + +/** @brief Print info about db state. + * + * Also provided as an IOC Shell command. + * + * @param id Db state id. + * @param level Interest level. + */ +epicsShareFunc void dbStateShow(dbStateId id, unsigned int level); + +/** @brief Print info about all db states. + * + * Also provided as an IOC Shell command. + * + * @param level Interest level. + */ +epicsShareFunc void dbStateShowAll(unsigned int level); + +#endif // INCdbStateH diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index b0e3fab58..1e0522426 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -35,6 +35,11 @@ arrShorthandTest_SRCS += arrShorthandTest.c OBJS_IOC_vxWorks += arrShorthandTest TESTS += arrShorthandTest +TESTPROD_HOST += dbStateTest +dbStateTest_SRCS += dbStateTest.c +OBJS_IOC_vxWorks += dbStateTest +TESTS += dbStateTest + TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES diff --git a/src/ioc/db/test/dbStateTest.c b/src/ioc/db/test/dbStateTest.c new file mode 100644 index 000000000..ae7b6512c --- /dev/null +++ b/src/ioc/db/test/dbStateTest.c @@ -0,0 +1,56 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#include "dbState.h" +#include "epicsUnitTest.h" +#include "testMain.h" + +MAIN(dbStateTest) +{ + dbStateId red, red2, blue, blue2; + int i; + + testPlan(20); + + testOk(!dbStateFind("y"), "Finding nonexisting state fails"); + + testOk(!!(red = dbStateCreate("red")), "Create state 'red'"); + testOk((red2 = dbStateFind("red")) == red, "Find 'red' returns correct id"); + testOk((red2 = dbStateCreate("red")) == red, "Create existing 'red' returns correct id"); + testOk(!dbStateFind("y"), "Finding nonexisting state still fails"); + + testOk(!!(blue = dbStateCreate("blue")), "Create state 'blue'"); + testOk((blue2 = dbStateFind("blue")) == blue, "Find 'blue' returns correct id"); + testOk((blue2 = dbStateCreate("blue")) == blue, "Create existing 'blue' returns correct id"); + testOk(!dbStateFind("y"), "Finding nonexisting state still fails"); + + testOk((i = dbStateGet(red)) == 0, "Default 'red' state is 0"); + testOk((i = dbStateGet(blue)) == 0, "Default 'blue' state is 0"); + dbStateSet(red); + testOk((i = dbStateGet(red)) == 1, "After setting, 'red' state is 1"); + testOk((i = dbStateGet(blue)) == 0, "'blue' state is 0"); + dbStateSet(blue); + testOk((i = dbStateGet(blue)) == 1, "After setting, 'blue' state is 1"); + testOk((i = dbStateGet(red)) == 1, "'red' state is 1"); + dbStateClear(blue); + testOk((i = dbStateGet(blue)) == 0, "After clearing, 'blue' state is 0"); + testOk((i = dbStateGet(red)) == 1, "'red' state is 1"); + dbStateClear(red); + testOk((i = dbStateGet(red)) == 0, "After clearing, 'red' state is 0"); + testOk((i = dbStateGet(blue)) == 0, "'red' state is 0"); + + testOk(!dbStateFind("y"), "Finding nonexisting state still fails"); + + return testDone(); +} From c4b54c6d4d4efe3a53861459b1de4e1912353a89 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:25:16 -0400 Subject: [PATCH 64/81] Fix compiler warnings and minor issues. --- src/ioc/db/filters/test/dbndTest.c | 4 ++-- src/ioc/db/filters/test/tsTest.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ioc/db/filters/test/dbndTest.c b/src/ioc/db/filters/test/dbndTest.c index 1d3f543c5..437528695 100644 --- a/src/ioc/db/filters/test/dbndTest.c +++ b/src/ioc/db/filters/test/dbndTest.c @@ -51,7 +51,7 @@ static void fl_setup(dbChannel *chan, db_field_log *pfl) { dbChannelFieldSize(chan)); } -static changeValue(db_field_log *pfl2, long val) { +static void changeValue(db_field_log *pfl2, long val) { pfl2->u.v.field.dbf_long = val; testDiag("new value: %ld", val); } @@ -99,7 +99,7 @@ MAIN(dbndTest) ELLNODE *node; chPostEventFunc *cb_out = NULL; void *arg_out = NULL; - db_field_log *pfl, *pfl2; + db_field_log *pfl2; db_field_log fl1; testPlan(61); diff --git a/src/ioc/db/filters/test/tsTest.c b/src/ioc/db/filters/test/tsTest.c index f02c5d441..806ba864d 100644 --- a/src/ioc/db/filters/test/tsTest.c +++ b/src/ioc/db/filters/test/tsTest.c @@ -83,7 +83,7 @@ MAIN(tsTest) testOk(pfl2 == &fl1, "ts filter does not drop or replace field_log"); testOk(fl_equal_ex_ts(&fl1, pfl2), "ts filter does not change field_log data"); - testOk(pfl2 = db_create_read_log(pch), "create field log from channel"); + testOk(!!(pfl2 = db_create_read_log(pch)), "create field log from channel"); epicsTimeStamp stamp = pfl2->time; pfl2 = dbChannelRunPreChain(pch, &fl1); epicsTimeStamp now; epicsTimeGetCurrent(&now); From 95881525e5af771c45a14f50e86e68ee5840586a Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:25:16 -0400 Subject: [PATCH 65/81] Add sync plugin that uses the db state --- src/ioc/db/Makefile | 1 + src/ioc/db/filters/filters.dbd | 1 + src/ioc/db/filters/sync.c | 170 +++++++++++++++++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 src/ioc/db/filters/sync.c diff --git a/src/ioc/db/Makefile b/src/ioc/db/Makefile index c18b605b4..4db814b4a 100644 --- a/src/ioc/db/Makefile +++ b/src/ioc/db/Makefile @@ -91,4 +91,5 @@ dbCore_SRCS += dbState.c dbCore_SRCS += ts.c dbCore_SRCS += dbnd.c dbCore_SRCS += arr.c +dbCore_SRCS += sync.c diff --git a/src/ioc/db/filters/filters.dbd b/src/ioc/db/filters/filters.dbd index 48cd94cd3..7fb0c5133 100644 --- a/src/ioc/db/filters/filters.dbd +++ b/src/ioc/db/filters/filters.dbd @@ -1,3 +1,4 @@ registrar(tsInitialize) registrar(dbndInitialize) registrar(arrInitialize) +registrar(syncInitialize) diff --git a/src/ioc/db/filters/sync.c b/src/ioc/db/filters/sync.c new file mode 100644 index 000000000..33b059151 --- /dev/null +++ b/src/ioc/db/filters/sync.c @@ -0,0 +1,170 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#include "epicsExport.h" +#include "freeList.h" +#include "db_field_log.h" +#include "chfPlugin.h" +#include "dbState.h" + +#define STATE_NAME_LENGTH 20 + +static const +chfPluginEnumType modeEnum[] = { {"before", 0}, {"first", 1}, + {"last", 2}, {"after", 3}, + {"while", 4}, {"unless", 5}, + {NULL,0} }; +typedef enum syncMode { + syncModeBefore=0, + syncModeFirst=1, + syncModeLast=2, + syncModeAfter=3, + syncModeWhile=4, + syncModeUnless=5 +} syncMode; + +typedef struct myStruct { + syncMode mode; + char state[STATE_NAME_LENGTH]; + dbStateId id; + db_field_log *lastfl; + int laststate:1; +} myStruct; + +static void *myStructFreeList; + +static const +chfPluginArgDef opts[] = { + chfEnum (myStruct, mode, "m", 1, 1, modeEnum), + chfString (myStruct, state, "s", 1, 0), + chfPluginArgEnd +}; + +static void * allocPvt(void) +{ + myStruct *my = (myStruct*) freeListCalloc(myStructFreeList); + return (void *) my; +} + +static void freePvt(void *pvt) +{ + freeListFree(myStructFreeList, pvt); +} + +static int parse_ok(void *pvt) +{ + myStruct *my = (myStruct*) pvt; + + if (!(my->id = dbStateFind(my->state))) + return -1; + + return 0; +} + +static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { + if (pfl->ctx == dbfl_context_read) + return pfl; + + db_field_log *passfl = NULL; + myStruct *my = (myStruct*) pvt; + int actstate = dbStateGet(my->id); + + switch (my->mode) { + case syncModeBefore: + if (actstate && !my->laststate) { + passfl = my->lastfl; + my->lastfl = NULL; + } + break; + case syncModeFirst: + if (actstate && !my->laststate) { + passfl = pfl; + pfl = NULL; + } + break; + case syncModeLast: + if (!actstate && my->laststate) { + passfl = my->lastfl; + my->lastfl = NULL; + } + break; + case syncModeAfter: + if (!actstate && my->laststate) { + passfl = pfl; + pfl = NULL; + } + break; + case syncModeWhile: + if (actstate) { + passfl = pfl; + } + goto no_shift; + case syncModeUnless: + if (!actstate) { + passfl = pfl; + } + goto no_shift; + } + + if (my->lastfl) + db_delete_field_log(my->lastfl); + my->lastfl = pfl; + my->laststate = actstate; + + no_shift: + return passfl; +} + +static void channelRegisterPre(dbChannel *chan, void *pvt, + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) +{ + *cb_out = filter; + *arg_out = pvt; +} + +static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent) +{ + myStruct *my = (myStruct*) pvt; + printf("%*s plugin sync, mode=%s, state=%s\n", indent, "", + chfPluginEnumString(modeEnum, my->mode, "n/a"), my->state); +} + +static chfPluginIf pif = { + allocPvt, + freePvt, + + NULL, /* parse_error, */ + parse_ok, + + NULL, /* channel_open, */ + channelRegisterPre, + NULL, /* channelRegisterPost, */ + channel_report, + NULL /* channel_close */ +}; + +static void syncInitialize(void) +{ + static int firstTime = 1; + + if (!firstTime) return; + firstTime = 0; + + if (!myStructFreeList) + freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64); + + chfPluginRegister("sync", &pif, opts); +} + +epicsExportRegistrar(syncInitialize); From 83586b49d04ee3c21bde75a4229a441d8398a7c3 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:25:17 -0400 Subject: [PATCH 66/81] Add test code for sync plugin. --- src/ioc/db/filters/test/Makefile | 6 + src/ioc/db/filters/test/syncTest.c | 356 +++++++++++++++++++++++++++++ 2 files changed, 362 insertions(+) create mode 100644 src/ioc/db/filters/test/syncTest.c diff --git a/src/ioc/db/filters/test/Makefile b/src/ioc/db/filters/test/Makefile index 23ebe705e..3ce50d9f2 100644 --- a/src/ioc/db/filters/test/Makefile +++ b/src/ioc/db/filters/test/Makefile @@ -33,6 +33,12 @@ arrTest_LIBS += dbStaticIoc OBJS_IOC_vxWorks += arrTest TESTS += arrTest +TESTPROD_HOST += syncTest +syncTest_SRCS += syncTest.c +syncTest_LIBS += dbStaticHost +OBJS_IOC_vxWorks += syncTest +TESTS += syncTest + TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES diff --git a/src/ioc/db/filters/test/syncTest.c b/src/ioc/db/filters/test/syncTest.c new file mode 100644 index 000000000..11ecbe04e --- /dev/null +++ b/src/ioc/db/filters/test/syncTest.c @@ -0,0 +1,356 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include + +#include "dbStaticLib.h" +#include "dbAccessDefs.h" +#include "db_field_log.h" +#include "dbCommon.h" +#include "chfPlugin.h" +#include "epicsUnitTest.h" +#include "epicsTime.h" +#include "dbState.h" +#include "testMain.h" + +#define PATTERN 0x55 + +epicsShareExtern void (*pvar_func_syncInitialize)(void); + +static db_field_log fl; +static dbStateId red; + +static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) { + return !(memcmp(pfl1, pfl2, sizeof(db_field_log))); +} + +static void fl_setup(dbChannel *chan, db_field_log *pfl, long val) { + struct dbCommon *prec = dbChannelRecord(chan); + + pfl->ctx = dbfl_context_event; + pfl->type = dbfl_type_val; + pfl->stat = prec->stat; + pfl->sevr = prec->sevr; + pfl->time = prec->time; + pfl->field_type = DBF_LONG; + pfl->no_elements = 1; + /* + * use memcpy to avoid a bus error on + * union copy of char in the db at an odd + * address + */ + memcpy(&pfl->u.v.field, + dbChannelField(chan), + dbChannelFieldSize(chan)); + pfl->u.v.field.dbf_long = val; +} + +static void changeValue(db_field_log *pfl2, long val) { + pfl2->u.v.field.dbf_long = val; +} + +static void testHead (char* title) { + testDiag("--------------------------------------------------------"); + testDiag(title); + testDiag("--------------------------------------------------------"); +} + +static void mustDrop(dbChannel *pch, db_field_log *pfl2, char* m) { + db_field_log *pfl = dbChannelRunPreChain(pch, pfl2); + testOk(NULL == pfl, "filter drops field_log (%s)", m); +} + +static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m) { + testDiag("%s: filter must pass twice", m); + db_field_log *pfl = dbChannelRunPreChain(pch, pfl2); + testOk(pfl2 == pfl, "call 1 does not drop or replace field_log"); + pfl = dbChannelRunPreChain(pch, pfl2); + testOk(pfl2 == pfl, "call 2 does not drop or replace field_log"); +} + +static void mustPassOld(dbChannel *pch, db_field_log *old, db_field_log *cur, char* m) { + db_field_log *pfl = dbChannelRunPreChain(pch, cur); + testOk(old == pfl, "filter passes previous field log (%s)", m); +} + +static void mustPass(dbChannel *pch, db_field_log *cur, char* m) { + db_field_log *pfl = dbChannelRunPreChain(pch, cur); + testOk(cur == pfl, "filter passes field_log (%s)", m); +} + +static void checkCtxRead(dbChannel *pch, dbStateId id) { + fl.ctx = dbfl_context_read; + dbStateClear(id); + mustPassTwice(pch, &fl, "ctx='read', state=FALSE"); + dbStateSet(id); + mustPassTwice(pch, &fl, "ctx='read', state=TRUE"); + dbStateClear(id); + mustPassTwice(pch, &fl, "ctx='read', state=FALSE"); + fl.ctx = dbfl_context_event; +} + +static void checkAndOpenChannel(dbChannel *pch, const chFilterPlugin *plug) { + ELLNODE *node; + chFilter *filter; + chPostEventFunc *cb_out = NULL; + void *arg_out = NULL; + + testDiag("Test filter structure and open channel"); + + testOk((ellCount(&pch->filters) == 1), "channel has one plugin"); + + db_field_log fl1 = fl; + node = ellFirst(&pch->filters); + filter = CONTAINER(node, chFilter, list_node); + plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1); + testOk(!!(cb_out) && !!(arg_out), "register_pre registers one filter with argument"); + testOk(fl_equal(&fl1, &fl), "register_pre does not change field_log data type"); + + testOk(!(dbChannelOpen(pch)), "dbChannel with plugin sync opened"); + node = ellFirst(&pch->pre_chain); + filter = CONTAINER(node, chFilter, pre_node); + testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg != NULL), + "sync has one filter with argument in pre chain"); + testOk((ellCount(&pch->post_chain) == 0), "sync has no filter in post chain"); + + checkCtxRead(pch, red); +} + +//MAIN(syncTest) +main() +{ + dbChannel *pch; + chFilter *filter; + const chFilterPlugin *plug; + char myname[] = "sync"; + ELLNODE *node; + chPostEventFunc *cb_out = NULL; + void *arg_out = NULL; + db_field_log *pfl[10]; + db_field_log fl1; + int i; + + testPlan(0); + + db_init_events(); + + testOk1(!dbReadDatabase(&pdbbase, "filterTest.dbx", ".:..", NULL)); + testOk(!!pdbbase, "pdbbase was set"); + + (*pvar_func_syncInitialize)(); /* manually initialize plugin */ + + testOk(!!(plug = dbFindFilter(myname, strlen(myname))), "plugin %s registered correctly", myname); + testOk(!!(red = dbStateCreate("red")), "state 'red' created successfully"); + + /* nonexisting state */ + testOk(!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\",\"s\":\"blue\"}}")), + "dbChannel with sync (m='while' s='blue') (nonex state) failed"); + /* missing state */ + testOk(!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\"}}")), + "dbChannel with sync (m='while') (no state) failed"); + /* missing mode */ + testOk(!(pch = dbChannelCreate("x.VAL{\"sync\":{\"s\":\"red\"}}")), + "dbChannel with sync (s='red') (no mode) failed"); + + /* mode WHILE */ + + testHead("Mode WHILE (m='while', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='while' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustDrop(pch, pfl[0], "state=FALSE, log0"); + mustDrop(pch, pfl[1], "state=FALSE, log1"); + mustDrop(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustPass(pch, pfl[3], "state=TRUE, log3"); + mustPass(pch, pfl[4], "state=TRUE, log4"); + mustPass(pch, pfl[5], "state=TRUE, log5"); + dbStateClear(red); + mustDrop(pch, pfl[6], "state=FALSE, log6"); + mustDrop(pch, pfl[7], "state=FALSE, log7"); + mustDrop(pch, pfl[8], "state=FALSE, log8"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + /* mode UNLESS */ + + testHead("Mode UNLESS (m='unless', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"unless\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='unless' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustPass(pch, pfl[0], "state=FALSE, log0"); + mustPass(pch, pfl[1], "state=FALSE, log1"); + mustPass(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustDrop(pch, pfl[3], "state=TRUE, log3"); + mustDrop(pch, pfl[4], "state=TRUE, log4"); + mustDrop(pch, pfl[5], "state=TRUE, log5"); + dbStateClear(red); + mustPass(pch, pfl[6], "state=FALSE, log6"); + mustPass(pch, pfl[7], "state=FALSE, log7"); + mustPass(pch, pfl[8], "state=FALSE, log8"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + /* mode BEFORE */ + + testHead("Mode BEFORE (m='before', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"before\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='before' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustDrop(pch, pfl[0], "state=FALSE, log0"); + mustDrop(pch, pfl[1], "state=FALSE, log1"); + mustDrop(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustPassOld(pch, pfl[2], pfl[3], "state=TRUE, log3, pass=log2"); + mustDrop(pch, pfl[4], "state=TRUE, log4"); + mustDrop(pch, pfl[5], "state=TRUE, log5"); + mustDrop(pch, pfl[6], "state=TRUE, log6"); + dbStateClear(red); + mustDrop(pch, pfl[7], "state=FALSE, log7"); + mustDrop(pch, pfl[8], "state=FALSE, log8"); + mustDrop(pch, pfl[9], "state=FALSE, log9"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + /* mode FIRST */ + + testHead("Mode FIRST (m='first', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"first\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='first' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustDrop(pch, pfl[0], "state=FALSE, log0"); + mustDrop(pch, pfl[1], "state=FALSE, log1"); + mustDrop(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustPass(pch, pfl[3], "state=TRUE, log3"); + mustDrop(pch, pfl[4], "state=TRUE, log4"); + mustDrop(pch, pfl[5], "state=TRUE, log5"); + dbStateClear(red); + mustDrop(pch, pfl[6], "state=FALSE, log6"); + mustDrop(pch, pfl[7], "state=FALSE, log7"); + mustDrop(pch, pfl[8], "state=FALSE, log8"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + /* mode LAST */ + + testHead("Mode LAST (m='last', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"last\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='last' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustDrop(pch, pfl[0], "state=FALSE, log0"); + mustDrop(pch, pfl[1], "state=FALSE, log1"); + mustDrop(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustDrop(pch, pfl[3], "state=TRUE, log3"); + mustDrop(pch, pfl[4], "state=TRUE, log4"); + mustDrop(pch, pfl[5], "state=TRUE, log5"); + dbStateClear(red); + mustPassOld(pch, pfl[5], pfl[6], "state=TRUE, log6, pass=log5"); + mustDrop(pch, pfl[7], "state=FALSE, log7"); + mustDrop(pch, pfl[8], "state=FALSE, log8"); + mustDrop(pch, pfl[9], "state=FALSE, log9"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + /* mode AFTER */ + + testHead("Mode AFTER (m='after', s='red')"); + testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"after\",\"s\":\"red\"}}")), + "dbChannel with plugin sync (m='after' s='red') created"); + + checkAndOpenChannel(pch, plug); + + for (i = 0; i < 10; i++) { + pfl[i] = db_create_read_log(pch); + fl_setup(pch, pfl[i], 120 + i); + } + + testDiag("Test event stream"); + + dbStateClear(red); + mustDrop(pch, pfl[0], "state=FALSE, log0"); + mustDrop(pch, pfl[1], "state=FALSE, log1"); + mustDrop(pch, pfl[2], "state=FALSE, log2"); + dbStateSet(red); + mustDrop(pch, pfl[3], "state=TRUE, log3"); + mustDrop(pch, pfl[4], "state=TRUE, log4"); + mustDrop(pch, pfl[5], "state=TRUE, log5"); + dbStateClear(red); + mustPass(pch, pfl[6], "state=FALSE, log6"); + mustDrop(pch, pfl[7], "state=FALSE, log7"); + mustDrop(pch, pfl[8], "state=FALSE, log8"); + + for (i = 0; i < 10; i++) + db_delete_field_log(pfl[i]); + + dbChannelDelete(pch); + dbFreeBase(pdbbase); + + return testDone(); +} From 03a30f3b8ffa18a668912febedcace61abcf7f13 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:25:18 -0400 Subject: [PATCH 67/81] Add DbState soft support for bi/bo records. --- src/std/dev/Makefile | 2 + src/std/dev/devBiDbState.c | 92 ++++++++++++++++++++++++++++++++++++++ src/std/dev/devBoDbState.c | 86 +++++++++++++++++++++++++++++++++++ src/std/dev/devSoft.dbd | 3 ++ 4 files changed, 183 insertions(+) create mode 100644 src/std/dev/devBiDbState.c create mode 100644 src/std/dev/devBoDbState.c diff --git a/src/std/dev/Makefile b/src/std/dev/Makefile index a118f7057..79eb6cb0c 100644 --- a/src/std/dev/Makefile +++ b/src/std/dev/Makefile @@ -21,8 +21,10 @@ dbRecStd_SRCS += devAoSoft.c dbRecStd_SRCS += devAoSoftRaw.c dbRecStd_SRCS += devBiSoft.c dbRecStd_SRCS += devBiSoftRaw.c +dbRecStd_SRCS += devBiDbState.c dbRecStd_SRCS += devBoSoft.c dbRecStd_SRCS += devBoSoftRaw.c +dbRecStd_SRCS += devBoDbState.c dbRecStd_SRCS += devCalcoutSoft.c dbRecStd_SRCS += devEventSoft.c dbRecStd_SRCS += devHistogramSoft.c diff --git a/src/std/dev/devBiDbState.c b/src/std/dev/devBiDbState.c new file mode 100644 index 000000000..54d929373 --- /dev/null +++ b/src/std/dev/devBiDbState.c @@ -0,0 +1,92 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include "errlog.h" +#include "dbState.h" +#include "devSup.h" +#include "recGbl.h" +#include "dbLink.h" +#include "dbAccessDefs.h" +#include "epicsExport.h" +#include "biRecord.h" + +#define DEVSUPNAME "devBiDbState" + +static long add_record (struct dbCommon *pdbc) +{ + biRecord *prec = (biRecord *) pdbc; + + if (INST_IO != prec->inp.type) { + recGblRecordError(S_db_badField, (void *) prec, DEVSUPNAME ": Illegal INP field"); + return(S_db_badField); + } + + if (!(prec->dpvt = dbStateFind(prec->inp.value.instio.string)) && + prec->inp.value.instio.string && + '\0' != *prec->inp.value.instio.string) { + errlogSevPrintf(errlogInfo, DEVSUPNAME ": Creating new db state '%s'\n", + prec->inp.value.instio.string); + prec->dpvt = dbStateCreate(prec->inp.value.instio.string); + } + return 0; +} + +static long del_record (struct dbCommon *pdbc) +{ + biRecord *prec = (biRecord *) pdbc; + prec->dpvt = NULL; + return 0; +} + +static struct dsxt myDsxt = { + add_record, + del_record +}; + +static long init(int pass) +{ + if (pass == 0) + devExtend(&myDsxt); + return 0; +} + +static long read_bi(biRecord *prec) +{ + if (prec->dpvt) { + prec->val = dbStateGet(prec->dpvt); + prec->udf = FALSE; + } + + if (prec->tsel.type == CONSTANT && + prec->tse == epicsTimeEventDeviceTime) + dbGetTimeStamp(&prec->inp, &prec->time); + + return 2; +} + +static struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN read_bi; +} devBiDbState = { + 5, + NULL, + init, + NULL, + NULL, + read_bi +}; + +epicsExportAddress(dset, devBiDbState); diff --git a/src/std/dev/devBoDbState.c b/src/std/dev/devBoDbState.c new file mode 100644 index 000000000..b2e8c493f --- /dev/null +++ b/src/std/dev/devBoDbState.c @@ -0,0 +1,86 @@ +/*************************************************************************\ +* Copyright (c) 2010 Brookhaven National Laboratory. +* Copyright (c) 2010 Helmholtz-Zentrum Berlin +* fuer Materialien und Energie GmbH. +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +/* + * Author: Ralph Lange + */ + +#include "errlog.h" +#include "dbState.h" +#include "devSup.h" +#include "recGbl.h" +#include "dbAccessDefs.h" +#include "epicsExport.h" +#include "boRecord.h" + +#define DEVSUPNAME "devBoDbState" + +static long add_record (struct dbCommon *pdbc) +{ + boRecord *prec = (boRecord *) pdbc; + + if (INST_IO != prec->out.type) { + recGblRecordError(S_db_badField, (void *) prec, DEVSUPNAME ": Illegal OUT field"); + return(S_db_badField); + } + + if (!(prec->dpvt = dbStateFind(prec->out.value.instio.string)) && + prec->out.value.instio.string && + '\0' != *prec->out.value.instio.string) { + errlogSevPrintf(errlogInfo, DEVSUPNAME ": Creating new db state '%s'\n", + prec->out.value.instio.string); + prec->dpvt = dbStateCreate(prec->out.value.instio.string); + } + return 0; +} + +static long del_record (struct dbCommon *pdbc) +{ + boRecord *prec = (boRecord *) pdbc; + prec->dpvt = NULL; + return 0; +} + +static struct dsxt myDsxt = { + add_record, + del_record +}; + +static long init(int pass) +{ + if (pass == 0) + devExtend(&myDsxt); + return 0; +} + +static long write_bo(boRecord *prec) +{ + if (prec->val) + dbStateSet(prec->dpvt); + else + dbStateClear(prec->dpvt); + return 0; +} + +static struct { + long number; + DEVSUPFUN report; + DEVSUPFUN init; + DEVSUPFUN init_record; + DEVSUPFUN get_ioint_info; + DEVSUPFUN write_bo; +} devBoDbState = { + 5, + NULL, + init, + NULL, + NULL, + write_bo +}; + +epicsExportAddress(dset, devBoDbState); diff --git a/src/std/dev/devSoft.dbd b/src/std/dev/devSoft.dbd index 5f90aba86..8178bfef1 100644 --- a/src/std/dev/devSoft.dbd +++ b/src/std/dev/devSoft.dbd @@ -44,3 +44,6 @@ device(longin, INST_IO,devLiGeneralTime,"General Time") device(stringin,INST_IO,devSiGeneralTime,"General Time") device(stringout,INST_IO,devSoStdio,"stdio") + +device(bi, INST_IO, devBiDbState, "Db State") +device(bo, INST_IO, devBoDbState, "Db State") From 57fc84fef35b7f44b989e2a7049052a7c01d74e2 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:25:18 -0400 Subject: [PATCH 68/81] dbState: Avoid segfaults when called with NULL pointer --- src/ioc/db/dbState.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ioc/db/dbState.c b/src/ioc/db/dbState.c index 11ce519d6..f7d577038 100644 --- a/src/ioc/db/dbState.c +++ b/src/ioc/db/dbState.c @@ -60,6 +60,8 @@ dbStateId dbStateCreate(const char *name) void dbStateSet(dbStateId id) { + if (!id) + return; epicsMutexMustLock(id->lock); id->status = 1; epicsMutexUnlock(id->lock); @@ -67,6 +69,8 @@ void dbStateSet(dbStateId id) void dbStateClear(dbStateId id) { + if (!id) + return; epicsMutexMustLock(id->lock); id->status = 0; epicsMutexUnlock(id->lock); @@ -76,6 +80,8 @@ int dbStateGet(dbStateId id) { int status; + if (!id) + return 0; epicsMutexMustLock(id->lock); status = id->status; epicsMutexUnlock(id->lock); From 948a3feba7773c2dbe026e1187b581a49152e5e6 Mon Sep 17 00:00:00 2001 From: Ralph Lange Date: Fri, 27 Apr 2012 13:25:19 -0400 Subject: [PATCH 69/81] Fix in dbLink.h: Include all used headers --- src/ioc/db/dbLink.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ioc/db/dbLink.h b/src/ioc/db/dbLink.h index 55aa0098f..aee2a848d 100644 --- a/src/ioc/db/dbLink.h +++ b/src/ioc/db/dbLink.h @@ -18,6 +18,8 @@ #include "link.h" #include "shareLib.h" +#include "epicsTypes.h" +#include "epicsTime.h" typedef struct lset { long (*initLink)(struct link *plink, short dbfType); From ceabe746532ee49564dc1e4263af336e38cd4801 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:25:25 -0400 Subject: [PATCH 70/81] fix test build --- src/ioc/db/test/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 1e0522426..04e670d75 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -22,21 +22,25 @@ TESTSPEC_vxWorks = callbackTest.munch; callbackTest TESTPROD_HOST += dbChannelTest dbChannelTest_SRCS += dbChannelTest.c +dbChannelTest_LIBS += dbIoc dbStaticHost OBJS_IOC_vxWorks += dbChannelTest TESTS += dbChannelTest TESTPROD_HOST += chfPluginTest chfPluginTest_SRCS += chfPluginTest.c +chfPluginTest_LIBS += dbIoc dbStaticHost OBJS_IOC_vxWorks += chfPluginTest TESTS += chfPluginTest TESTPROD_HOST += arrShorthandTest arrShorthandTest_SRCS += arrShorthandTest.c +arrShorthandTest_LIBS += dbIoc dbStaticHost OBJS_IOC_vxWorks += arrShorthandTest TESTS += arrShorthandTest TESTPROD_HOST += dbStateTest dbStateTest_SRCS += dbStateTest.c +dbStateTest_LIBS += dbIoc dbStaticHost OBJS_IOC_vxWorks += dbStateTest TESTS += dbStateTest From cb175516d678b93a29ede56afa4a099adfecf80c Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:25:25 -0400 Subject: [PATCH 71/81] fix build after reorg --- src/ioc/db/Makefile | 4 +++- src/ioc/db/filters/test/Makefile | 4 ++-- src/ioc/db/test/Makefile | 8 ++++---- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/ioc/db/Makefile b/src/ioc/db/Makefile index 4db814b4a..227786ec3 100644 --- a/src/ioc/db/Makefile +++ b/src/ioc/db/Makefile @@ -55,7 +55,7 @@ menuGlobal_DBD += menuSimm.dbd DBDINC += $(basename $(menuGlobal_DBD)) DBDINC += dbCommon -DBD_INSTALLS += filters.dbd +DBD_INSTALLS += ../db/filters/filters.dbd dbCore_SRCS += dbLock.c @@ -88,6 +88,8 @@ dbCore_SRCS += dbIocRegister.c dbCore_SRCS += chfPlugin.c dbCore_SRCS += dbState.c +SRC_DIRS += $(IOCDIR)/db/filters + dbCore_SRCS += ts.c dbCore_SRCS += dbnd.c dbCore_SRCS += arr.c diff --git a/src/ioc/db/filters/test/Makefile b/src/ioc/db/filters/test/Makefile index 3ce50d9f2..ef8628dc5 100644 --- a/src/ioc/db/filters/test/Makefile +++ b/src/ioc/db/filters/test/Makefile @@ -6,11 +6,11 @@ # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* -TOP=../../../.. +TOP=../../../../.. include $(TOP)/configure/CONFIG -PROD_LIBS += dbIoc miscIoc registryIoc ca Com +PROD_LIBS += dbCore ca Com TESTPROD_HOST += tsTest tsTest_SRCS += tsTest.c diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 04e670d75..43ea497ed 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -22,25 +22,25 @@ TESTSPEC_vxWorks = callbackTest.munch; callbackTest TESTPROD_HOST += dbChannelTest dbChannelTest_SRCS += dbChannelTest.c -dbChannelTest_LIBS += dbIoc dbStaticHost +dbChannelTest_LIBS += dbCore OBJS_IOC_vxWorks += dbChannelTest TESTS += dbChannelTest TESTPROD_HOST += chfPluginTest chfPluginTest_SRCS += chfPluginTest.c -chfPluginTest_LIBS += dbIoc dbStaticHost +chfPluginTest_LIBS += dbCore OBJS_IOC_vxWorks += chfPluginTest TESTS += chfPluginTest TESTPROD_HOST += arrShorthandTest arrShorthandTest_SRCS += arrShorthandTest.c -arrShorthandTest_LIBS += dbIoc dbStaticHost +arrShorthandTest_LIBS += dbCore OBJS_IOC_vxWorks += arrShorthandTest TESTS += arrShorthandTest TESTPROD_HOST += dbStateTest dbStateTest_SRCS += dbStateTest.c -dbStateTest_LIBS += dbIoc dbStaticHost +dbStateTest_LIBS += dbCore OBJS_IOC_vxWorks += dbStateTest TESTS += dbStateTest From 773c277d20a50a28cc4359dfae1ee876b9118bb7 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:25:26 -0400 Subject: [PATCH 72/81] ugly hack to make unit test run --- src/ioc/db/test/Makefile | 3 +++ src/ioc/db/test/dbChannelTest.c | 14 +++++++++++++- src/ioc/db/test/dbChannelTest.db | 2 ++ src/ioc/db/test/{dbChannelTest.dbx => xRecord.dbd} | 1 - 4 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 src/ioc/db/test/dbChannelTest.db rename src/ioc/db/test/{dbChannelTest.dbx => xRecord.dbd} (92%) diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 43ea497ed..9b7a62fca 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -20,8 +20,11 @@ TESTS += callbackTest # When we add more test programs here, this must become a vxTestHarness TESTSPEC_vxWorks = callbackTest.munch; callbackTest +DBDINC += xRecord + TESTPROD_HOST += dbChannelTest dbChannelTest_SRCS += dbChannelTest.c +dbChannelTest_SRCS += xRecord_registerRecordDeviceDriver.cpp dbChannelTest_LIBS += dbCore OBJS_IOC_vxWorks += dbChannelTest TESTS += dbChannelTest diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index 1653fbe69..ef37041d1 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -16,9 +16,13 @@ #include "dbChannel.h" #include "dbStaticLib.h" #include "dbAccessDefs.h" +#include "recSup.h" #include "epicsUnitTest.h" #include "testMain.h" +#define GEN_SIZE_OFFSET +#include "xRecord.h" + /* Expected call bit definitions */ #define e_start 0x00000001 #define e_abort 0x00000002 @@ -152,7 +156,9 @@ MAIN(dbChannelTest) testPlan(68); - testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.dbx", ".:..", NULL)); + testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:..", NULL)); + xRecord_registerRecordDeviceDriver(pdbbase); + testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:..", NULL)); testOk(!!pdbbase, "pdbbase was set"); r = e = 0; @@ -243,3 +249,9 @@ MAIN(dbChannelTest) return testDone(); } + +#include + +static +rset xRSET; +epicsExportAddress(rset,xRSET); diff --git a/src/ioc/db/test/dbChannelTest.db b/src/ioc/db/test/dbChannelTest.db new file mode 100644 index 000000000..a6fa08e40 --- /dev/null +++ b/src/ioc/db/test/dbChannelTest.db @@ -0,0 +1,2 @@ +record(x, x) {} + diff --git a/src/ioc/db/test/dbChannelTest.dbx b/src/ioc/db/test/xRecord.dbd similarity index 92% rename from src/ioc/db/test/dbChannelTest.dbx rename to src/ioc/db/test/xRecord.dbd index b743359c1..4837871a4 100644 --- a/src/ioc/db/test/dbChannelTest.dbx +++ b/src/ioc/db/test/xRecord.dbd @@ -10,4 +10,3 @@ recordtype(x) { prompt("Value") } } -record(x, x) {} From 60692fd9d25de03fc806a0ccb390d3fab7513b9b Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:25:27 -0400 Subject: [PATCH 73/81] clean all --- src/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Makefile b/src/Makefile index ad00bc90b..1c38e51f3 100644 --- a/src/Makefile +++ b/src/Makefile @@ -64,6 +64,9 @@ ioc_DEPEND_DIRS = libCom ca/client DIRS += ioc/db/test ioc/db/test_DEPEND_DIRS = ioc libCom/RTEMS +DIRS += ioc/db/filters/test +ioc/db/filters/test_DEPEND_DIRS = ioc libCom/RTEMS + # PDB Standard Record Definitions DIRS += std From c1c7698a9854ae7d156ecab8d1fac4e73ca5c137 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:25:27 -0400 Subject: [PATCH 74/81] disable filter tests until fixed... --- src/Makefile | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Makefile b/src/Makefile index 1c38e51f3..8e82138f0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -64,14 +64,15 @@ ioc_DEPEND_DIRS = libCom ca/client DIRS += ioc/db/test ioc/db/test_DEPEND_DIRS = ioc libCom/RTEMS -DIRS += ioc/db/filters/test -ioc/db/filters/test_DEPEND_DIRS = ioc libCom/RTEMS - # PDB Standard Record Definitions DIRS += std std_DEPEND_DIRS = ioc libCom/RTEMS +#DIRS += ioc/db/filters/test +#ioc/db/filters/test_DEPEND_DIRS = ioc std libCom/RTEMS + + include $(TOP)/configure/RULES_DIRS From 8b2c08f2f66537a73a48296bc77838fcb3706588 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 27 Apr 2012 13:25:27 -0400 Subject: [PATCH 75/81] fix build --- src/ioc/db/dbEvent.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index d73a19a7c..8c59fda8a 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -404,7 +404,7 @@ dbEventSubscription epicsShareAPI db_add_event ( epicsMutexUnlock ( evUser->lock ); if ( ! ev_que ) { - freeListFree ( dbevEventBlockFreeList, pevent ); + freeListFree ( dbevEventSubscriptionFreeList, pevent ); return NULL; } From 386a2f73a7033958ad90e3476fc3a2ba68748b25 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 24 May 2012 14:11:08 -0400 Subject: [PATCH 76/81] don't destroy locked mutex --- src/ioc/dbStatic/dbPvdLib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ioc/dbStatic/dbPvdLib.c b/src/ioc/dbStatic/dbPvdLib.c index 43f9cd40f..49060366b 100644 --- a/src/ioc/dbStatic/dbPvdLib.c +++ b/src/ioc/dbStatic/dbPvdLib.c @@ -181,6 +181,7 @@ void dbPvdFreeMem(dbBase *pdbbase) ellDelete(&pbucket->list, (ELLNODE *)ppvdNode); free(ppvdNode); } + epicsMutexUnlock(pbucket->lock); epicsMutexDestroy(pbucket->lock); free(pbucket); } From 434f6346faac7bcb70d30b4e2b64ea9eede24138 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 24 May 2012 14:11:09 -0400 Subject: [PATCH 77/81] registerRecordDeviceDriver needs DBD search path --- configure/RULES.Db | 4 ++-- src/ioc/registry/registerRecordDeviceDriver.pl | 8 +++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/configure/RULES.Db b/configure/RULES.Db index 19b2df211..670aee4c5 100644 --- a/configure/RULES.Db +++ b/configure/RULES.Db @@ -398,12 +398,12 @@ IOC_INST_TOP := $(firstword $(IOCS_APPL_TOP) \ %_registerRecordDeviceDriver.cpp: $(COMMON_DIR)/%.dbd @$(RM) $@ $*.tmp - $(REGISTERRECORDDEVICEDRIVER) $< $(basename $@) $(IOC_INST_TOP) > $*.tmp + $(REGISTERRECORDDEVICEDRIVER) $(DBDFLAGS) $< $(basename $@) $(IOC_INST_TOP) > $*.tmp $(MV) $*.tmp $@ %_registerRecordDeviceDriver.cpp: %.dbd @$(RM) $@ $*.tmp - $(REGISTERRECORDDEVICEDRIVER) $< $(basename $@) $(IOC_INST_TOP) > $*.tmp + $(REGISTERRECORDDEVICEDRIVER) $(DBDFLAGS) $< $(basename $@) $(IOC_INST_TOP) > $*.tmp $(MV) $*.tmp $@ .PRECIOUS: %_registerRecordDeviceDriver.cpp diff --git a/src/ioc/registry/registerRecordDeviceDriver.pl b/src/ioc/registry/registerRecordDeviceDriver.pl index 3adda373a..5ab5c14d3 100755 --- a/src/ioc/registry/registerRecordDeviceDriver.pl +++ b/src/ioc/registry/registerRecordDeviceDriver.pl @@ -18,12 +18,18 @@ use DBD; use DBD::Parser; use EPICS::Readfile; use EPICS::Path; +use EPICS::Getopts; use Text::Wrap; +getopts('I@') or + die "Usage: registerRecordDeviceDriver [-I dir] in.dbd subroutinename [TOP]"; + +my @path = map { split /[:;]/ } @EPICS::Getopts::opt_I; # FIXME: Broken on Win32? + my ($file, $subname, $bldTop) = @ARGV; my $dbd = DBD->new(); -&ParseDBD($dbd, &Readfile($file)); +&ParseDBD($dbd, &Readfile($file, "", \@path)); $Text::Wrap::columns = 75; From 20d60bef81a2e046aaa0b8cead85c5902fe25768 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 24 May 2012 14:11:09 -0400 Subject: [PATCH 78/81] fix db plugin tests Avoid installing xRecord.dbd. This would otherwise be installed by the DBDINC. Also must split out recordtype definition from record instance definition and run the registrar function between the two loads. This is due to new sanity checks in the loading process --- src/ioc/db/test/Makefile | 17 +++++++---------- src/ioc/db/test/arrShorthandTest.c | 18 ++++++++++++++++-- src/ioc/db/test/chfPluginTest.c | 18 ++++++++++++++++-- src/ioc/db/test/dbChannelTest.c | 15 +++++++++------ 4 files changed, 48 insertions(+), 20 deletions(-) diff --git a/src/ioc/db/test/Makefile b/src/ioc/db/test/Makefile index 9b7a62fca..0664454e7 100644 --- a/src/ioc/db/test/Makefile +++ b/src/ioc/db/test/Makefile @@ -20,30 +20,23 @@ TESTS += callbackTest # When we add more test programs here, this must become a vxTestHarness TESTSPEC_vxWorks = callbackTest.munch; callbackTest -DBDINC += xRecord - TESTPROD_HOST += dbChannelTest -dbChannelTest_SRCS += dbChannelTest.c -dbChannelTest_SRCS += xRecord_registerRecordDeviceDriver.cpp -dbChannelTest_LIBS += dbCore +dbChannelTest_SRCS += dbChannelTest.c xRecord_registerRecordDeviceDriver.cpp OBJS_IOC_vxWorks += dbChannelTest TESTS += dbChannelTest TESTPROD_HOST += chfPluginTest -chfPluginTest_SRCS += chfPluginTest.c -chfPluginTest_LIBS += dbCore +chfPluginTest_SRCS += chfPluginTest.c xRecord_registerRecordDeviceDriver.cpp OBJS_IOC_vxWorks += chfPluginTest TESTS += chfPluginTest TESTPROD_HOST += arrShorthandTest -arrShorthandTest_SRCS += arrShorthandTest.c -arrShorthandTest_LIBS += dbCore +arrShorthandTest_SRCS += arrShorthandTest.c xRecord_registerRecordDeviceDriver.cpp OBJS_IOC_vxWorks += arrShorthandTest TESTS += arrShorthandTest TESTPROD_HOST += dbStateTest dbStateTest_SRCS += dbStateTest.c -dbStateTest_LIBS += dbCore OBJS_IOC_vxWorks += dbStateTest TESTS += dbStateTest @@ -51,3 +44,7 @@ TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES +dbChannelTest$(OBJ): $(COMMON_DIR)/xRecord.h +chfPluginTest$(OBJ): $(COMMON_DIR)/xRecord.h +arrShorthandTest$(OBJ): $(COMMON_DIR)/xRecord.h +dbStateTest$(OBJ): $(COMMON_DIR)/xRecord.h diff --git a/src/ioc/db/test/arrShorthandTest.c b/src/ioc/db/test/arrShorthandTest.c index 307b73766..e64d7801d 100644 --- a/src/ioc/db/test/arrShorthandTest.c +++ b/src/ioc/db/test/arrShorthandTest.c @@ -75,16 +75,21 @@ static void testHead (char* title) { testDiag("--------------------------------------------------------"); } +void xRecord_registerRecordDeviceDriver(struct dbBase *); + MAIN(chfPluginTest) { dbChannel *pch; - testPlan(28); + testPlan(29); db_init_events(); testHead("Set up database"); - testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.dbx", ".:..", NULL)); + testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:..", NULL)); + + xRecord_registerRecordDeviceDriver(pdbbase); + testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:..", NULL)); testOk(!!pdbbase, "pdbbase was set"); testHead("Register plugin"); @@ -123,3 +128,12 @@ MAIN(chfPluginTest) return testDone(); } + +#define GEN_SIZE_OFFSET +#include "xRecord.h" + +#include +#include + +static rset xRSET; +epicsExportAddress(rset,xRSET); diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index e3c8604e7..eaaafe226 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -432,12 +432,14 @@ static void testHead (char* title) { testDiag("--------------------------------------------------------"); } +void xRecord_registerRecordDeviceDriver(struct dbBase *); + MAIN(chfPluginTest) { dbChannel *pch; db_field_log *pfl; - testPlan(1754); + testPlan(1755); db_init_events(); @@ -449,7 +451,10 @@ MAIN(chfPluginTest) testOk(strcmp(chfPluginEnumString(colorEnum, 3, "-"), "-") == 0, "Enum to string: invalid index"); testHead("Set up database"); - testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.dbx", ".:..", NULL)); + testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:..", NULL)); + + xRecord_registerRecordDeviceDriver(pdbbase); + testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:..", NULL)); testOk(!!pdbbase, "pdbbase was set"); testHead("Try to register buggy plugins"); @@ -730,3 +735,12 @@ MAIN(chfPluginTest) return testDone(); } + +#define GEN_SIZE_OFFSET +#include "xRecord.h" + +#include +#include + +static rset xRSET; +epicsExportAddress(rset,xRSET); diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index ef37041d1..ad007d812 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -20,9 +20,6 @@ #include "epicsUnitTest.h" #include "testMain.h" -#define GEN_SIZE_OFFSET -#include "xRecord.h" - /* Expected call bit definitions */ #define e_start 0x00000001 #define e_abort 0x00000002 @@ -150,13 +147,16 @@ chFilterIf testIf = p_string, p_start_map, p_map_key, p_end_map, p_start_array, p_end_array, c_open, c_reg_pre, c_reg_post, c_report, c_close }; +void xRecord_registerRecordDeviceDriver(struct dbBase *); + MAIN(dbChannelTest) { dbChannel *pch; - testPlan(68); + testPlan(69); testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:..", NULL)); + xRecord_registerRecordDeviceDriver(pdbbase); testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:..", NULL)); testOk(!!pdbbase, "pdbbase was set"); @@ -250,8 +250,11 @@ MAIN(dbChannelTest) return testDone(); } +#define GEN_SIZE_OFFSET +#include "xRecord.h" + +#include #include -static -rset xRSET; +static rset xRSET; epicsExportAddress(rset,xRSET); From 34800b071fdfd9091fa102538f7c8adeee365365 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 24 May 2012 14:11:10 -0400 Subject: [PATCH 79/81] fix arrTest Don't use compiled in paths include menuConvert as this is now required at runtime --- src/Makefile | 4 ++-- src/ioc/db/filters/test/Makefile | 4 ---- src/ioc/db/filters/test/arrRecord.dbd | 1 + src/ioc/db/filters/test/arrTest.cpp | 13 ++----------- 4 files changed, 5 insertions(+), 17 deletions(-) diff --git a/src/Makefile b/src/Makefile index 8e82138f0..db71a79e9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -69,8 +69,8 @@ ioc/db/test_DEPEND_DIRS = ioc libCom/RTEMS DIRS += std std_DEPEND_DIRS = ioc libCom/RTEMS -#DIRS += ioc/db/filters/test -#ioc/db/filters/test_DEPEND_DIRS = ioc std libCom/RTEMS +DIRS += ioc/db/filters/test +ioc/db/filters/test_DEPEND_DIRS = ioc std libCom/RTEMS diff --git a/src/ioc/db/filters/test/Makefile b/src/ioc/db/filters/test/Makefile index ef8628dc5..f2cada142 100644 --- a/src/ioc/db/filters/test/Makefile +++ b/src/ioc/db/filters/test/Makefile @@ -14,13 +14,11 @@ PROD_LIBS += dbCore ca Com TESTPROD_HOST += tsTest tsTest_SRCS += tsTest.c -tsTest_LIBS += dbStaticHost OBJS_IOC_vxWorks += tsTest TESTS += tsTest TESTPROD_HOST += dbndTest dbndTest_SRCS += dbndTest.c -dbndTest_LIBS += dbStaticHost OBJS_IOC_vxWorks += dbndTest TESTS += dbndTest @@ -29,13 +27,11 @@ TARGETS += $(COMMON_DIR)/arrTest.dbd arrTest_DBD += arrRecord.dbd TESTPROD_HOST += arrTest arrTest_SRCS += arrTest.cpp arrRecord.c arrRecord_registerRecordDeviceDriver.cpp -arrTest_LIBS += dbStaticIoc OBJS_IOC_vxWorks += arrTest TESTS += arrTest TESTPROD_HOST += syncTest syncTest_SRCS += syncTest.c -syncTest_LIBS += dbStaticHost OBJS_IOC_vxWorks += syncTest TESTS += syncTest diff --git a/src/ioc/db/filters/test/arrRecord.dbd b/src/ioc/db/filters/test/arrRecord.dbd index 25a45351d..782a03e9d 100644 --- a/src/ioc/db/filters/test/arrRecord.dbd +++ b/src/ioc/db/filters/test/arrRecord.dbd @@ -1,4 +1,5 @@ include "menuGlobal.dbd" +include "menuConvert.dbd" recordtype(arr) { include "dbCommon.dbd" field(VAL, DBF_NOACCESS) { diff --git a/src/ioc/db/filters/test/arrTest.cpp b/src/ioc/db/filters/test/arrTest.cpp index 76918ac30..7a504c5fb 100644 --- a/src/ioc/db/filters/test/arrTest.cpp +++ b/src/ioc/db/filters/test/arrTest.cpp @@ -35,7 +35,6 @@ #include "iocInit.h" #include "iocsh.h" #include "dbChannel.h" -#include "epicsInstallDir.h" #include "epicsUnitTest.h" #include "testMain.h" @@ -44,16 +43,10 @@ extern "C" int arrRecord_registerRecordDeviceDriver(struct dbBase *pdbbase); extern "C" void (*pvar_func_arrInitialize)(void); -#define DBD_FILE EPICS_BASE "/src/db/filters/test/O.Common/arrTest.dbd" -#define DB_FILE EPICS_BASE "/src/db/filters/test/arrTest.db" -#define EXIT_FILE EPICS_BASE "/db/softIocExit.db" #define CA_SERVER_PORT "65535" #define PATTERN 0x55 -const char *base_dbd = DBD_FILE; -const char *arr_db = DB_FILE; -const char *exit_db = EXIT_FILE; const char *server_port = CA_SERVER_PORT; static void exitSubroutine(subRecord *precord) { @@ -302,8 +295,6 @@ static void check(short dbr_type) { MAIN(arrTest) { - char *dbd_file = const_cast(base_dbd); - char *db_file = const_cast(arr_db); const chFilterPlugin *plug; char arr[] = "arr"; int status; @@ -314,14 +305,14 @@ MAIN(arrTest) epicsEnvSet("EPICS_CA_SERVER_PORT", server_port); - testOk1(!(status = dbLoadDatabase(dbd_file, NULL, NULL))); + testOk1(!(status=dbReadDatabase(&pdbbase, "arrRecord.dbd", ".:..:../../../../../../dbd", NULL))); if (status) epicsExit(EXIT_FAILURE); (*pvar_func_arrInitialize)(); arrRecord_registerRecordDeviceDriver(pdbbase); registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine); - testOk1(!(status = dbLoadRecords(db_file, NULL))); + testOk1(!(status=dbReadDatabase(&pdbbase, "arrTest.db", ".:..", NULL))); if (status) epicsExit(EXIT_FAILURE); /* Start the IOC */ From 458cc17eab53af51e2aab8c2ae295a82ce64e5fd Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Thu, 24 May 2012 14:11:10 -0400 Subject: [PATCH 80/81] fix filter plugin tests as well --- src/ioc/db/filters/test/Makefile | 14 ++++++++++---- src/ioc/db/filters/test/dbndTest.c | 18 ++++++++++++++++-- src/ioc/db/filters/test/filterTest.dbx | 13 ------------- src/ioc/db/filters/test/syncTest.c | 19 +++++++++++++++++-- src/ioc/db/filters/test/tsTest.c | 18 ++++++++++++++++-- 5 files changed, 59 insertions(+), 23 deletions(-) delete mode 100644 src/ioc/db/filters/test/filterTest.dbx diff --git a/src/ioc/db/filters/test/Makefile b/src/ioc/db/filters/test/Makefile index f2cada142..c2b965af4 100644 --- a/src/ioc/db/filters/test/Makefile +++ b/src/ioc/db/filters/test/Makefile @@ -13,16 +13,15 @@ include $(TOP)/configure/CONFIG PROD_LIBS += dbCore ca Com TESTPROD_HOST += tsTest -tsTest_SRCS += tsTest.c +tsTest_SRCS += tsTest.c xRecord_registerRecordDeviceDriver.cpp OBJS_IOC_vxWorks += tsTest TESTS += tsTest TESTPROD_HOST += dbndTest -dbndTest_SRCS += dbndTest.c +dbndTest_SRCS += dbndTest.c xRecord_registerRecordDeviceDriver.cpp OBJS_IOC_vxWorks += dbndTest TESTS += dbndTest -DBDINC += arrRecord TARGETS += $(COMMON_DIR)/arrTest.dbd arrTest_DBD += arrRecord.dbd TESTPROD_HOST += arrTest @@ -31,7 +30,7 @@ OBJS_IOC_vxWorks += arrTest TESTS += arrTest TESTPROD_HOST += syncTest -syncTest_SRCS += syncTest.c +syncTest_SRCS += syncTest.c xRecord_registerRecordDeviceDriver.cpp OBJS_IOC_vxWorks += syncTest TESTS += syncTest @@ -39,3 +38,10 @@ TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES +$(COMMON_DIR)/xRecord.dbd: ../../../test/xRecord.dbd + $(INSTALL) -d $< $(@D) + +tsTest$(OBJ): $(COMMON_DIR)/xRecord.h +dbndTest$(OBJ): $(COMMON_DIR)/xRecord.h +syncTest$(OBJ): $(COMMON_DIR)/xRecord.h +arrTest$(OBJ): $(COMMON_DIR)/arrRecord.h diff --git a/src/ioc/db/filters/test/dbndTest.c b/src/ioc/db/filters/test/dbndTest.c index 437528695..5cb092cdf 100644 --- a/src/ioc/db/filters/test/dbndTest.c +++ b/src/ioc/db/filters/test/dbndTest.c @@ -90,6 +90,8 @@ static void testHead (char* title) { testDiag("--------------------------------------------------------"); } +void xRecord_registerRecordDeviceDriver(struct dbBase *); + MAIN(dbndTest) { dbChannel *pch; @@ -102,15 +104,18 @@ MAIN(dbndTest) db_field_log *pfl2; db_field_log fl1; - testPlan(61); + testPlan(62); db_init_events(); - testOk1(!dbReadDatabase(&pdbbase, "filterTest.dbx", ".:..", NULL)); + testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:../../../test", NULL)); testOk(!!pdbbase, "pdbbase was set"); (*pvar_func_dbndInitialize)(); /* manually initialize plugin */ + xRecord_registerRecordDeviceDriver(pdbbase); + testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:../../../test", NULL)); + testOk(!!(plug = dbFindFilter(dbnd, strlen(dbnd))), "plugin dbnd registered correctly"); testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{}}")), "dbChannel with plugin dbnd (delta=0) created"); @@ -212,3 +217,12 @@ MAIN(dbndTest) return testDone(); } + +#define GEN_SIZE_OFFSET +#include "xRecord.h" + +#include +#include + +static rset xRSET; +epicsExportAddress(rset,xRSET); diff --git a/src/ioc/db/filters/test/filterTest.dbx b/src/ioc/db/filters/test/filterTest.dbx deleted file mode 100644 index b743359c1..000000000 --- a/src/ioc/db/filters/test/filterTest.dbx +++ /dev/null @@ -1,13 +0,0 @@ -# This is a combined minimal DBD and DB file - -recordtype(x) { - field(NAME, DBF_STRING) { - prompt("Record Name") - special(SPC_NOMOD) - size(61) - } - field(VAL, DBF_LONG) { - prompt("Value") - } -} -record(x, x) {} diff --git a/src/ioc/db/filters/test/syncTest.c b/src/ioc/db/filters/test/syncTest.c index 11ecbe04e..4d1fd3159 100644 --- a/src/ioc/db/filters/test/syncTest.c +++ b/src/ioc/db/filters/test/syncTest.c @@ -16,6 +16,7 @@ #include "dbAccessDefs.h" #include "db_field_log.h" #include "dbCommon.h" +#include "dbChannel.h" #include "chfPlugin.h" #include "epicsUnitTest.h" #include "epicsTime.h" @@ -125,8 +126,10 @@ static void checkAndOpenChannel(dbChannel *pch, const chFilterPlugin *plug) { checkCtxRead(pch, red); } +void xRecord_registerRecordDeviceDriver(struct dbBase *); + //MAIN(syncTest) -main() +int main() { dbChannel *pch; chFilter *filter; @@ -143,11 +146,14 @@ main() db_init_events(); - testOk1(!dbReadDatabase(&pdbbase, "filterTest.dbx", ".:..", NULL)); + testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:../../../test", NULL)); testOk(!!pdbbase, "pdbbase was set"); (*pvar_func_syncInitialize)(); /* manually initialize plugin */ + xRecord_registerRecordDeviceDriver(pdbbase); + testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:../../../test", NULL)); + testOk(!!(plug = dbFindFilter(myname, strlen(myname))), "plugin %s registered correctly", myname); testOk(!!(red = dbStateCreate("red")), "state 'red' created successfully"); @@ -354,3 +360,12 @@ main() return testDone(); } + +#define GEN_SIZE_OFFSET +#include "xRecord.h" + +#include +#include + +static rset xRSET; +epicsExportAddress(rset,xRSET); diff --git a/src/ioc/db/filters/test/tsTest.c b/src/ioc/db/filters/test/tsTest.c index 806ba864d..3a039afe8 100644 --- a/src/ioc/db/filters/test/tsTest.c +++ b/src/ioc/db/filters/test/tsTest.c @@ -36,6 +36,8 @@ static int fl_equal_ex_ts(const db_field_log *pfl1, const db_field_log *pfl2) { return fl_equal(&fl1, pfl2); } +void xRecord_registerRecordDeviceDriver(struct dbBase *); + MAIN(tsTest) { dbChannel *pch; @@ -48,15 +50,18 @@ MAIN(tsTest) db_field_log fl1; db_field_log *pfl2; - testPlan(14); + testPlan(15); db_init_events(); - testOk1(!dbReadDatabase(&pdbbase, "filterTest.dbx", ".:..", NULL)); + testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:../../../test", NULL)); testOk(!!pdbbase, "pdbbase was set"); (*pvar_func_tsInitialize)(); /* manually initialize plugin */ + xRecord_registerRecordDeviceDriver(pdbbase); + testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:../../../test", NULL)); + testOk(!!(plug = dbFindFilter(ts, strlen(ts))), "plugin ts registered correctly"); testOk(!!(pch = dbChannelCreate("x.VAL{\"ts\":{}}")), "dbChannel with plugin ts created"); @@ -95,3 +100,12 @@ MAIN(tsTest) return testDone(); } + +#define GEN_SIZE_OFFSET +#include "xRecord.h" + +#include +#include + +static rset xRSET; +epicsExportAddress(rset,xRSET); From e1e8f56d27197203bc9796af55abbed9c89b9101 Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Thu, 21 Jun 2012 15:27:32 -0500 Subject: [PATCH 81/81] Moved filter plugins, fixed portability issues. The filter plugins are now in src/std/filters and their tests in the test subdirectory. Various portability issues found and fixed: * Variable declarations after statements in a block * Code assuming that epicsInt32 = long * // comments in C files Removed a number of tests that weren't really tests; preconditions for future tests that don't actually check the code being tested should call testAbort() if the condition fails, which tells the test harness that you meant to die at that point. Split up lots of very long source lines. Probably made a few other changes that I forget about now. --- src/Makefile | 4 +- src/ioc/db/Makefile | 9 - src/ioc/db/chfPlugin.c | 46 +-- src/ioc/db/chfPlugin.h | 4 +- src/ioc/db/dbChannel.c | 23 +- src/ioc/db/dbEvent.c | 5 +- src/ioc/db/dbIocRegister.c | 28 +- src/ioc/db/dbNotify.c | 3 +- src/ioc/db/dbState.c | 5 +- src/ioc/db/db_access.c | 1 - src/ioc/db/db_test.c | 6 +- src/ioc/db/test/arrShorthandTest.c | 10 +- src/ioc/db/test/chfPluginTest.c | 280 ++++++++++++------ src/ioc/db/test/dbChannelTest.c | 11 +- src/std/Makefile | 1 + src/std/filters/Makefile | 18 ++ src/{ioc/db => std}/filters/arr.c | 8 +- src/{ioc/db => std}/filters/dbnd.c | 0 src/{ioc/db => std}/filters/filters.dbd | 0 src/{ioc/db => std}/filters/sync.c | 8 +- src/{ioc/db => std}/filters/test/Makefile | 16 +- src/{ioc/db => std}/filters/test/arrRecord.c | 0 .../db => std}/filters/test/arrRecord.dbd | 0 src/{ioc/db => std}/filters/test/arrTest.cpp | 44 +-- src/{ioc/db => std}/filters/test/arrTest.db | 0 src/std/filters/test/dbChannelTest.db | 2 + src/{ioc/db => std}/filters/test/dbndTest.c | 23 +- src/{ioc/db => std}/filters/test/syncTest.c | 30 +- src/{ioc/db => std}/filters/test/tsTest.c | 16 +- src/std/filters/test/xRecord.dbd | 12 + src/{ioc/db => std}/filters/ts.c | 0 31 files changed, 375 insertions(+), 238 deletions(-) create mode 100644 src/std/filters/Makefile rename src/{ioc/db => std}/filters/arr.c (99%) rename src/{ioc/db => std}/filters/dbnd.c (100%) rename src/{ioc/db => std}/filters/filters.dbd (100%) rename src/{ioc/db => std}/filters/sync.c (98%) rename src/{ioc/db => std}/filters/test/Makefile (81%) rename src/{ioc/db => std}/filters/test/arrRecord.c (100%) rename src/{ioc/db => std}/filters/test/arrRecord.dbd (100%) rename src/{ioc/db => std}/filters/test/arrTest.cpp (92%) rename src/{ioc/db => std}/filters/test/arrTest.db (100%) create mode 100644 src/std/filters/test/dbChannelTest.db rename src/{ioc/db => std}/filters/test/dbndTest.c (94%) rename src/{ioc/db => std}/filters/test/syncTest.c (95%) rename src/{ioc/db => std}/filters/test/tsTest.c (90%) create mode 100644 src/std/filters/test/xRecord.dbd rename src/{ioc/db => std}/filters/ts.c (100%) diff --git a/src/Makefile b/src/Makefile index db71a79e9..5921f3d3c 100644 --- a/src/Makefile +++ b/src/Makefile @@ -69,8 +69,8 @@ ioc/db/test_DEPEND_DIRS = ioc libCom/RTEMS DIRS += std std_DEPEND_DIRS = ioc libCom/RTEMS -DIRS += ioc/db/filters/test -ioc/db/filters/test_DEPEND_DIRS = ioc std libCom/RTEMS +DIRS += std/filters/test +std/filters/test_DEPEND_DIRS = std diff --git a/src/ioc/db/Makefile b/src/ioc/db/Makefile index 227786ec3..cd015040e 100644 --- a/src/ioc/db/Makefile +++ b/src/ioc/db/Makefile @@ -55,8 +55,6 @@ menuGlobal_DBD += menuSimm.dbd DBDINC += $(basename $(menuGlobal_DBD)) DBDINC += dbCommon -DBD_INSTALLS += ../db/filters/filters.dbd - dbCore_SRCS += dbLock.c dbCore_SRCS += dbAccess.c @@ -88,10 +86,3 @@ dbCore_SRCS += dbIocRegister.c dbCore_SRCS += chfPlugin.c dbCore_SRCS += dbState.c -SRC_DIRS += $(IOCDIR)/db/filters - -dbCore_SRCS += ts.c -dbCore_SRCS += dbnd.c -dbCore_SRCS += arr.c -dbCore_SRCS += sync.c - diff --git a/src/ioc/db/chfPlugin.c b/src/ioc/db/chfPlugin.c index cb91b0ef6..4516950e0 100644 --- a/src/ioc/db/chfPlugin.c +++ b/src/ioc/db/chfPlugin.c @@ -12,7 +12,6 @@ /* Based on the linkoptions utility by Michael Davidsaver (BNL) */ -#include #include #include #include @@ -22,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -70,7 +70,7 @@ typedef enum chfPluginType { */ static int store_integer_value(const chfPluginArgDef *opt, void *user, long val) { - long *ival; + epicsInt32 *ival; int *eval; const chfPluginEnumType *emap; double *dval; @@ -85,7 +85,7 @@ static int store_integer_value(const chfPluginArgDef *opt, void *user, long val) switch (opt->optType) { case chfPluginArgInt32: - ival = (long*) ((char*)user + opt->offset); + ival = (epicsInt32 *) ((char *)user + opt->offset); *ival = val; break; case chfPluginArgBoolean: @@ -129,7 +129,7 @@ static int store_integer_value(const chfPluginArgDef *opt, void *user, long val) */ static int store_boolean_value(const chfPluginArgDef *opt, void *user, int val) { - long *ival; + epicsInt32 *ival; int *eval; double *dval; char *sval; @@ -142,7 +142,7 @@ static int store_boolean_value(const chfPluginArgDef *opt, void *user, int val) switch (opt->optType) { case chfPluginArgInt32: - ival = (long*) ((char*)user + opt->offset); + ival = (epicsInt32 *) ((char*)user + opt->offset); *ival = val; break; case chfPluginArgBoolean: @@ -174,7 +174,7 @@ static int store_boolean_value(const chfPluginArgDef *opt, void *user, int val) */ static int store_double_value(const chfPluginArgDef *opt, void *user, double val) { - long *ival; + epicsInt32 *ival; int *eval; double *dval; char *sval; @@ -190,11 +190,13 @@ static int store_double_value(const chfPluginArgDef *opt, void *user, double val switch (opt->optType) { case chfPluginArgInt32: - if (val < LONG_MIN || val > LONG_MAX) { +#if (LONG_MAX > 0x7fffffff) + if (val < -0x80000000L || val > 0x7fffffff) { return -1; } - ival = (long*) ((char*)user + opt->offset); - *ival = (long) val; +#endif + ival = (epicsInt32 *) ((char*)user + opt->offset); + *ival = val; break; case chfPluginArgBoolean: eval = (int*) ((char*)user + opt->offset); @@ -209,7 +211,7 @@ static int store_double_value(const chfPluginArgDef *opt, void *user, double val if (opt->size <= 8) { /* Play it safe: 3 exp + 2 sign + 'e' + '.' */ return -1; } - i = snprintf(sval, opt->size, "%.*g", opt->size - 7, val); + i = epicsSnprintf(sval, opt->size, "%.*g", (int) opt->size - 7, val); if (i >= opt->size) { return -1; } @@ -227,7 +229,8 @@ static int store_double_value(const chfPluginArgDef *opt, void *user, double val */ static int store_string_value(const chfPluginArgDef *opt, void *user, const char *val, size_t len) { - long *ival; + epicsInt32 *ival; + long lval; int *eval; const chfPluginEnumType *emap; double *dval; @@ -243,18 +246,23 @@ static int store_string_value(const chfPluginArgDef *opt, void *user, const char switch (opt->optType) { case chfPluginArgInt32: - ival = (long*) ((char*)user + opt->offset); - *ival = strtol(val, &end, 0); + lval = strtol(val, &end, 0); /* test for the myriad error conditions which strtol may use */ - if (*ival == LONG_MAX || *ival == LONG_MIN || end == val) { + if (lval == LONG_MAX || lval == LONG_MIN +#if (LONG_MAX > 0x7fffffff) + || lval < -0x80000000L || lval > 0x7fffffff +#endif + || end == val) { return -1; } + ival = (epicsInt32 *) ((char*)user + opt->offset); + *ival = lval; break; case chfPluginArgBoolean: eval = (int*) ((char*)user + opt->offset); - if (strncasecmp(val, "true", len) == 0) { + if (epicsStrnCaseCmp(val, "true", len) == 0) { *eval = 1; - } else if (strncasecmp(val, "false", len) == 0) { + } else if (epicsStrnCaseCmp(val, "false", len) == 0) { *eval = 0; } else { i = strtol(val, &end, 0); @@ -567,10 +575,10 @@ int chfPluginRegister(const char* key, const chfPluginIf *pif, const chfPluginAr for (i = 0, cur = opts; cur && cur->name; i++, cur++) { switch(cur->optType) { case chfPluginArgInt32: - if (cur->size < sizeof(long)) { - errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for long (%lu)\n", + if (cur->size < sizeof(epicsInt32)) { + errlogPrintf("Plugin %s: provided storage (%d bytes) for %s is too small for epicsInt32 (%lu)\n", key, cur->size, cur->name, - (unsigned long) sizeof(long)); + (unsigned long) sizeof(epicsInt32)); return -1; } break; diff --git a/src/ioc/db/chfPlugin.h b/src/ioc/db/chfPlugin.h index 9b31654db..7d9745d10 100644 --- a/src/ioc/db/chfPlugin.h +++ b/src/ioc/db/chfPlugin.h @@ -229,8 +229,8 @@ typedef struct chfPluginEnumType { typedef struct chfPluginArgDef { const char * name; chfPluginArg optType; - int required:1; - int convert:1; + unsigned int required:1; + unsigned int convert:1; epicsUInt32 offset; epicsUInt32 size; const chfPluginEnumType *enums; diff --git a/src/ioc/db/dbChannel.c b/src/ioc/db/dbChannel.c index 6385c8801..9305f7da4 100644 --- a/src/ioc/db/dbChannel.c +++ b/src/ioc/db/dbChannel.c @@ -27,6 +27,7 @@ #include "dbLock.h" #include "dbStaticLib.h" #include "epicsAssert.h" +#include "epicsString.h" #include "errlog.h" #include "gpHash.h" #include "recSup.h" @@ -65,7 +66,7 @@ static void chf_value(parseContext *parser, parse_result *presult) if (filter->plug->fif->parse_end(filter) == parse_continue) { ellAdd(&parser->chan->filters, &filter->list_node); } else { - free(filter); // FIXME: Use free-list + free(filter); /* FIXME: Use free-list */ *presult = parse_stop; } } @@ -161,7 +162,7 @@ static int chf_map_key(void * ctx, const unsigned char * key, assert(parser->depth == 0); plug = dbFindFilter((const char *) key, stringLen); if (!plug) { - printf("dbChannelCreate: Channel filter '%.*s' not found\n", stringLen, key); + printf("dbChannelCreate: Channel filter '%.*s' not found\n", (int) stringLen, key); return parse_stop; } @@ -175,7 +176,7 @@ static int chf_map_key(void * ctx, const unsigned char * key, if (result == parse_continue) { parser->filter = filter; } else { - free(filter); // FIXME: Use free-list + free(filter); /* FIXME: Use free-list */ } return result; } @@ -417,7 +418,7 @@ static long parseArrayRange(dbChannel* chan, const char *pname, const char **ppn return 0; failure: - free(filter); // FIXME: Use free-list + free(filter); /* FIXME: Use free-list */ status = S_dbLib_fieldNotFound; finish: @@ -463,7 +464,7 @@ dbChannel * dbChannelCreate(const char *name) /* FIXME: Use free-list */ chan = (dbChannel *) callocMustSucceed(1, sizeof(*chan), "dbChannelCreate"); - chan->name = strdup(name); /* FIXME: free-list */ + chan->name = epicsStrDup(name); /* FIXME: free-list */ ellInit(&chan->filters); ellInit(&chan->pre_chain); ellInit(&chan->post_chain); @@ -570,6 +571,8 @@ long dbChannelOpen(dbChannel *chan) void *arg; long status; ELLNODE *node; + db_field_log probe; + db_field_log p; for (node = ellFirst(&chan->filters); node; node = ellNext(node)) { filter = CONTAINER(node, chFilter, list_node); @@ -581,8 +584,6 @@ long dbChannelOpen(dbChannel *chan) } /* Set up type probe */ - db_field_log probe; - db_field_log p; probe.field_type = dbChannelFieldType(chan); probe.no_elements = dbChannelElements(chan); probe.field_size = dbChannelFieldSize(chan); @@ -784,8 +785,8 @@ void dbChannelDelete(dbChannel *chan) filter->plug->fif->channel_close(filter); free(filter); } - free((char *) chan->name); // FIXME: Use free-list - free(chan); // FIXME: Use free-list + free((char *) chan->name); /* FIXME: Use free-list */ + free(chan); /* FIXME: Use free-list */ } static void freeArray(db_field_log *pfl) { @@ -799,10 +800,10 @@ static void freeArray(db_field_log *pfl) { void dbChannelMakeArrayCopy(void *pvt, db_field_log *pfl, dbChannel *chan) { void *p; + struct dbCommon *prec = dbChannelRecord(chan); if (!pfl->type == dbfl_type_rec) return; - struct dbCommon *prec = dbChannelRecord(chan); pfl->type = dbfl_type_ref; pfl->stat = prec->stat; pfl->sevr = prec->sevr; @@ -838,7 +839,7 @@ void dbRegisterFilter(const char *name, const chFilterIf *fif, void *puser) return; pfilt = dbCalloc(1, sizeof(chFilterPlugin)); - pfilt->name = strdup(name); + pfilt->name = epicsStrDup(name); pfilt->fif = fif; pfilt->puser = puser; diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c index 8c59fda8a..6fbb9a03a 100644 --- a/src/ioc/db/dbEvent.c +++ b/src/ioc/db/dbEvent.c @@ -818,10 +818,11 @@ void epicsShareAPI db_post_single_event (dbEventSubscription event) { struct evSubscrip * const pevent = (struct evSubscrip *) event; struct dbCommon * const prec = dbChannelRecord(pevent->chan); + db_field_log *pLog; dbScanLock (prec); - db_field_log *pLog = db_create_event_log(pevent); + pLog = db_create_event_log(pevent); pLog = dbChannelRunPreChain(pevent->chan, pLog); db_queue_event_log(pevent, pLog); @@ -854,9 +855,9 @@ static int event_read ( struct event_que *ev_que ) } while ( ev_que->evque[ev_que->getix] != EVENTQEMPTY ) { - pfl = ev_que->valque[ev_que->getix]; struct evSubscrip *pevent = ev_que->evque[ev_que->getix]; + pfl = ev_que->valque[ev_que->getix]; if ( pevent == &canceledEvent ) { ev_que->evque[ev_que->getix] = EVENTQEMPTY; if (ev_que->valque[ev_que->getix]) { diff --git a/src/ioc/db/dbIocRegister.c b/src/ioc/db/dbIocRegister.c index c4fa3aedf..58172bbb1 100644 --- a/src/ioc/db/dbIocRegister.c +++ b/src/ioc/db/dbIocRegister.c @@ -299,8 +299,8 @@ static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args) } /* dbStateCreate */ -static const iocshArg dbStateCreateArg0 = { "name", iocshArgString }; -static const iocshArg * const dbStateCreateArgs[] = { &dbStateCreateArg0 }; +static const iocshArg dbStateArgName = { "name", iocshArgString }; +static const iocshArg * const dbStateCreateArgs[] = { &dbStateArgName }; static const iocshFuncDef dbStateCreateFuncDef = { "dbStateCreate", 1, dbStateCreateArgs }; static void dbStateCreateCallFunc (const iocshArgBuf *args) { @@ -308,31 +308,37 @@ static void dbStateCreateCallFunc (const iocshArgBuf *args) } /* dbStateSet */ -static const iocshArg dbStateSetArg0 = { "id", iocshArgInt }; -static const iocshArg * const dbStateSetArgs[] = { &dbStateSetArg0 }; +static const iocshArg * const dbStateSetArgs[] = { &dbStateArgName }; static const iocshFuncDef dbStateSetFuncDef = { "dbStateSet", 1, dbStateSetArgs }; static void dbStateSetCallFunc (const iocshArgBuf *args) { - dbStateSet((dbStateId) args[0].ival); + dbStateId sid = dbStateFind(args[0].sval); + + if (sid) + dbStateSet(sid); } /* dbStateClear */ -static const iocshArg dbStateClearArg0 = { "id", iocshArgInt }; -static const iocshArg * const dbStateClearArgs[] = { &dbStateClearArg0 }; +static const iocshArg * const dbStateClearArgs[] = { &dbStateArgName }; static const iocshFuncDef dbStateClearFuncDef = { "dbStateClear", 1, dbStateClearArgs }; static void dbStateClearCallFunc (const iocshArgBuf *args) { - dbStateClear((dbStateId) args[0].ival); + dbStateId sid = dbStateFind(args[0].sval); + + if (sid) + dbStateClear(sid); } /* dbStateShow */ -static const iocshArg dbStateShowArg0 = { "id", iocshArgInt }; static const iocshArg dbStateShowArg1 = { "level", iocshArgInt }; -static const iocshArg * const dbStateShowArgs[] = { &dbStateShowArg0, &dbStateShowArg1 }; +static const iocshArg * const dbStateShowArgs[] = { &dbStateArgName, &dbStateShowArg1 }; static const iocshFuncDef dbStateShowFuncDef = { "dbStateShow", 2, dbStateShowArgs }; static void dbStateShowCallFunc (const iocshArgBuf *args) { - dbStateShow((dbStateId) args[0].ival, args[1].ival); + dbStateId sid = dbStateFind(args[0].sval); + + if (sid) + dbStateShow(sid, args[1].ival); } /* dbStateShowAll */ diff --git a/src/ioc/db/dbNotify.c b/src/ioc/db/dbNotify.c index 401bc371b..66974fa77 100644 --- a/src/ioc/db/dbNotify.c +++ b/src/ioc/db/dbNotify.c @@ -26,6 +26,7 @@ #include "epicsAssert.h" #include "epicsEvent.h" #include "epicsMutex.h" +#include "epicsString.h" #include "epicsThread.h" #include "errlog.h" #include "errMdef.h" @@ -498,7 +499,7 @@ long epicsShareAPI dbtpn(char *pname, char *pvalue) putNotify *ppn; char *pbuffer; - pbuffer = strdup(pvalue); + pbuffer = epicsStrDup(pvalue); chan = dbChannelCreate(pname); if (!chan) { printf("dbtpn: No such channel"); diff --git a/src/ioc/db/dbState.c b/src/ioc/db/dbState.c index f7d577038..1d452a04f 100644 --- a/src/ioc/db/dbState.c +++ b/src/ioc/db/dbState.c @@ -14,6 +14,7 @@ #include #include "iocsh.h" +#include "epicsString.h" #include "epicsMutex.h" #include "ellLib.h" #include "dbStaticLib.h" @@ -51,7 +52,7 @@ dbStateId dbStateCreate(const char *name) return id; id = callocMustSucceed(1, sizeof(dbState), "createDbState"); - id->name = strdup(name); + id->name = epicsStrDup(name); id->lock = epicsMutexMustCreate(); ellAdd(&states, &id->node); @@ -91,7 +92,7 @@ int dbStateGet(dbStateId id) void dbStateShow(dbStateId id, unsigned int level) { if (level >=1) - printf("id %#x '%s' : ", (unsigned int) id, id->name); + printf("id %p '%s' : ", id, id->name); printf("%s\n", dbStateGet(id) ? "TRUE" : "FALSE"); } diff --git a/src/ioc/db/db_access.c b/src/ioc/db/db_access.c index 792469676..99663773c 100644 --- a/src/ioc/db/db_access.c +++ b/src/ioc/db/db_access.c @@ -153,7 +153,6 @@ int epicsShareAPI dbChannel_get_count( struct dbChannel *chan, int buffer_type, void *pbuffer, long *nRequest, void *pfl) { - dbAddr *paddr=&chan->addr; long status; long options; long i; diff --git a/src/ioc/db/db_test.c b/src/ioc/db/db_test.c index 81a4f3d4b..d3ad405b5 100644 --- a/src/ioc/db/db_test.c +++ b/src/ioc/db/db_test.c @@ -14,14 +14,16 @@ * Andrew Johnson */ #include -#include #include #include #include "dbDefs.h" #include "errlog.h" #include "cadef.h" +#include "epicsStdlib.h" #include "epicsStdioRedirect.h" +#include "epicsString.h" + #define epicsExportSharedSymbols #include "dbChannel.h" #include "db_access_routines.h" @@ -223,7 +225,7 @@ int epicsShareAPI tpn(char *pname, char *pvalue) return 1; } - pbuffer = strdup(pvalue); + pbuffer = epicsStrDup(pvalue); ppn = calloc(1, sizeof(putNotify)); if (!ppn) { printf("calloc failed\n"); diff --git a/src/ioc/db/test/arrShorthandTest.c b/src/ioc/db/test/arrShorthandTest.c index e64d7801d..669eab9ca 100644 --- a/src/ioc/db/test/arrShorthandTest.c +++ b/src/ioc/db/test/arrShorthandTest.c @@ -81,16 +81,16 @@ MAIN(chfPluginTest) { dbChannel *pch; - testPlan(29); + testPlan(26); db_init_events(); - testHead("Set up database"); - testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:..", NULL)); + if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + testAbort("Database description not loaded"); xRecord_registerRecordDeviceDriver(pdbbase); - testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:..", NULL)); - testOk(!!pdbbase, "pdbbase was set"); + if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + testAbort("Test database not loaded"); testHead("Register plugin"); testOk(!chfPluginRegister("arr", &myPif, opts), "register fake arr plugin"); diff --git a/src/ioc/db/test/chfPluginTest.c b/src/ioc/db/test/chfPluginTest.c index eaaafe226..20db71830 100644 --- a/src/ioc/db/test/chfPluginTest.c +++ b/src/ioc/db/test/chfPluginTest.c @@ -129,7 +129,8 @@ static void clearStruct(void *p) { if (!my) return; memset(my, 0, sizeof(myStruct)); - my->sent1 = my->sent2 = my->sent3 = my->sent4 = my->sent5 = my->sent6 = PATTERN; + my->sent1 = my->sent2 = my->sent3 = my->sent4 = + my->sent5 = my->sent6 = PATTERN; my->ival = 12; my->flag = 1; my->dval = 1.234e5; @@ -160,7 +161,6 @@ static void * allocPvt(void) static void freePvt(void *user) { - testOk(user == puser1 || user == puser2, "freePvt: user pointer valid"); if (user == puser1) { testOk(e1 & e_free, "freePvt (1) called"); c1 |= e_free; @@ -171,46 +171,46 @@ static void freePvt(void *user) c2 |= e_free; free(user); puser2 = NULL; - } + } else + testFail("freePvt: user pointer invalid"); } static void parse_error(void *user) { - testOk(user == puser1 || user == puser2, "parse_error: user pointer valid"); if (user == puser1) { testOk(e1 & e_error, "parse_error (1) called"); c1 |= e_error; } else if (user == puser2) { testOk(e2 & e_error, "parse_error (2) called"); c2 |= e_error; - } + } else + testFail("parse_error: user pointer invalid"); } static int parse_ok(void *user) { - testOk(user == puser1 || user == puser2, "parse_ok: user pointer valid"); if (user == puser1) { testOk(e1 & e_ok, "parse_ok (1) called"); c1 |= e_ok; } else if (user == puser2) { testOk(e2 & e_ok, "parse_ok (2) called"); c2 |= e_ok; - - } + } else + testFail("parse_ok: user pointer invalid"); return p_ok_return; } static long channel_open(dbChannel *chan, void *user) { - testOk(user == puser1 || user == puser2, "channel_open: user pointer valid"); if (user == puser1) { testOk(e1 & e_open, "channel_open (1) called"); c1 |= e_open; } else if (user == puser2) { testOk(e2 & e_open, "channel_open (2) called"); c2 |= e_open; - } + } else + testFail("channel_open: user pointer invalid"); return c_open_return; } @@ -233,26 +233,34 @@ static db_field_log * pre(void *user, dbChannel *chan, db_field_log *pLog) { myStruct *my = (myStruct*)user; dbfl_freeFunc *dtor = NULL; - testOk(user == puser1 || user == puser2, "pre: user pointer valid"); if (my == puser1) { testOk(e1 & e_pre, "pre (1) called"); - testOk(!(c2 & e_pre), "pre (2) has not been called before pre (1)"); + testOk(!(c2 & e_pre), + "pre (2) was not called before pre (1)"); c1 |= e_pre; dtor = dbfl_free1; } else if (my == puser2) { testOk(e2 & e_pre, "pre (2) called"); - testOk(!(e1 & e_pre) || c1 & e_pre, "pre (1) has been called before pre (2)"); + testOk(!(e1 & e_pre) || c1 & e_pre, + "pre (1) was called before pre (2)"); c2 |= e_pre; dtor = dbfl_free2; + } else { + testFail("pre: user pointer invalid"); + testSkip(1, "Can't check order of pre(1)/pre(2)"); } - testOk(!(c1 & e_post), "post (1) has not been called before pre (%c)", inst(user)); - testOk(!(c2 & e_post), "post (2) has not been called before pre (%c)", inst(user)); + testOk(!(c1 & e_post), + "post (1) was not called before pre (%c)", inst(user)); + testOk(!(c2 & e_post), + "post (2) was not called before pre (%c)", inst(user)); - if (!testOk(pLog->field_type == TYPE_START + my->offpre, "pre (%c) got field log of expected type", inst(user))) - testDiag("expected: %d, got %d", TYPE_START + my->offpre, pLog->field_type); + if (!testOk(pLog->field_type == TYPE_START + my->offpre, + "pre (%c) got field log of expected type", inst(user))) + testDiag("expected: %d, got %d", + TYPE_START + my->offpre, pLog->field_type); pLog->field_type++; - if (my->offpre == 0) { /* The first one registers a dtor and saves pfl */ + if (my->offpre == 0) { /* The first one registers a dtor and saves pfl */ pLog->u.r.dtor = dtor; dtorpfl = pLog; } @@ -268,26 +276,34 @@ static db_field_log * post(void *user, dbChannel *chan, db_field_log *pLog) { myStruct *my = (myStruct*)user; dbfl_freeFunc *dtor = NULL; - testOk(user == puser1 || user == puser2, "post: user pointer valid"); if (my == puser1) { testOk(e1 & e_post, "post (1) called"); - testOk(!(c2 & e_post), "post (2) has not been called before post (1)"); + testOk(!(c2 & e_post), + "post (2) was not called before post (1)"); c1 |= e_post; dtor = dbfl_free1; } else if (my == puser2) { testOk(e2 & e_post, "post (2) called"); - testOk(!(e1 & e_post) || c1 & e_post, "post (1) has been called before post (2)"); + testOk(!(e1 & e_post) || c1 & e_post, + "post (1) was called before post (2)"); c2 |= e_post; dtor = dbfl_free2; + } else { + testFail("post: user pointer invalid"); + testSkip(1, "Can't check order of post(1)/post(2)"); } - testOk(!(e1 & e_pre) || c1 & e_pre, "pre (1) has been called before post (%c)", inst(user)); - testOk(!(e2 & e_pre) || c2 & e_pre, "pre (2) has been called before post (%c)", inst(user)); + testOk(!(e1 & e_pre) || c1 & e_pre, + "pre (1) was called before post (%c)", inst(user)); + testOk(!(e2 & e_pre) || c2 & e_pre, + "pre (2) was called before post (%c)", inst(user)); - if (!testOk(pLog->field_type == TYPE_START + my->offpost, "post (%c) got field log of expected type", inst(user))) - testDiag("expected: %d, got %d", TYPE_START + my->offpost, pLog->field_type); + if (!testOk(pLog->field_type == TYPE_START + my->offpost, + "post (%c) got field log of expected type", inst(user))) + testDiag("expected: %d, got %d", + TYPE_START + my->offpost, pLog->field_type); pLog->field_type++; - if (my->offpost == 0) { /* The first one registers a dtor and remembers pfl */ + if (my->offpost == 0) { /* The first one registers a dtor and saves pfl */ pLog->u.r.dtor = dtor; dtorpfl = pLog; } @@ -300,22 +316,28 @@ static db_field_log * post(void *user, dbChannel *chan, db_field_log *pLog) { } static void channelRegisterPre(dbChannel *chan, void *user, - chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { myStruct *my = (myStruct*)user; - testOk(user == puser1 || user == puser2, "register_pre: user pointer valid"); if (my == puser1) { testOk(e1 & e_reg_pre, "register_pre (1) called"); - testOk(!(c2 & e_reg_pre), "register_pre (2) has not been called before register_pre (1)"); + testOk(!(c2 & e_reg_pre), + "register_pre (2) was not called before register_pre (1)"); c1 |= e_reg_pre; } else if (my == puser2) { testOk(e2 & e_reg_pre, "register_pre (2) called"); - testOk(!(e1 & e_reg_pre) || c1 & e_reg_pre, "register_pre (1) has been called before register_pre (2)"); + testOk(!(e1 & e_reg_pre) || c1 & e_reg_pre, + "register_pre (1) was called before register_pre (2)"); c2 |= e_reg_pre; + } else { + testFail("register_pre: user pointer invalid"); + testSkip(1, "Can't check order of register_pre(1)/register_pre(2)"); } - testOk(!(c1 & e_reg_post), "register_post (1) has not been called before register_pre (%c)", inst(user)); - testOk(!(c2 & e_reg_post), "register_post (2) has not been called before register_pre (%c)", inst(user)); + testOk(!(c1 & e_reg_post), + "register_post (1) was not called before register_pre (%c)", inst(user)); + testOk(!(c2 & e_reg_post), + "register_post (2) was not called before register_pre (%c)", inst(user)); my->offpre = offset++; probe->field_type++; @@ -324,22 +346,28 @@ static void channelRegisterPre(dbChannel *chan, void *user, } static void channelRegisterPost(dbChannel *chan, void *user, - chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) { myStruct *my = (myStruct*)user; - testOk(user == puser1 || user == puser2, "register_post: user pointer valid"); if (my == puser1) { testOk(e1 & e_reg_post, "register_post (1) called"); - testOk(!(c2 & e_reg_post), "register_post (2) has not been called before register_post (1)"); + testOk(!(c2 & e_reg_post), + "register_post (2) was not called before register_post (1)"); c1 |= e_reg_post; } else if (my == puser2) { testOk(e2 & e_reg_post, "register_post (2) called"); - testOk(!(e1 & e_reg_post) || c1 & e_reg_post, "register_post (1) has been called before register_post (2)"); + testOk(!(e1 & e_reg_post) || c1 & e_reg_post, + "register_post (1) was called before register_post (2)"); c2 |= e_reg_post; + } else { + testFail("register_post: user pointer invalid"); + testSkip(1, "Can't check order of register_post(1)/register_post(2)"); } - testOk(!(e1 & e_reg_pre) || c1 & e_reg_pre, "register_pre (1) has been called before register_post (%c)", inst(user)); - testOk(!(e2 & e_reg_pre) || c2 & e_reg_pre, "register_pre (2) has been called before register_post (%c)", inst(user)); + testOk(!(e1 & e_reg_pre) || c1 & e_reg_pre, + "register_pre (1) was called before register_post (%c)", inst(user)); + testOk(!(e2 & e_reg_pre) || c2 & e_reg_pre, + "register_pre (2) was called before register_post (%c)", inst(user)); my->offpost = offset++; probe->field_type++; @@ -347,9 +375,9 @@ static void channelRegisterPost(dbChannel *chan, void *user, *arg_out = user; } -static void channel_report(dbChannel *chan, void *user, int level, const unsigned short indent) +static void channel_report(dbChannel *chan, void *user, int level, + const unsigned short indent) { - testOk(user == puser1 || user == puser2, "channel_report: user pointer valid"); testOk(level == R_LEVEL - 1, "channel_report: level correct"); if (user == puser1) { testOk(e1 & e_report, "channel_report (1) called"); @@ -357,19 +385,20 @@ static void channel_report(dbChannel *chan, void *user, int level, const unsigne } else if (user == puser2) { testOk(e2 & e_report, "channel_report (2) called"); c2 |= e_report; - } + } else + testFail("channel_report: user pointer invalid"); } static void channel_close(dbChannel *chan, void *user) { - testOk(user == puser1 || user == puser2, "channel_close: user pointer valid"); if (user == puser1) { testOk(e1 & e_close, "channel_close (1) called"); c1 |= e_close; } else if (user == puser2) { testOk(e2 & e_close, "channel_close (2) called"); c2 |= e_close; - } + } else + testFail("channel_close: user pointer invalid"); } static chfPluginIf myPif = { @@ -414,7 +443,8 @@ static chfPluginIf postPif = { channel_close }; -static int checkValues(myStruct *my, epicsUInt32 i, int f, double d, char *s, int c) { +static int checkValues(myStruct *my, + epicsUInt32 i, int f, double d, char *s, int c) { if (!my) return 0; if (my->sent1 == PATTERN && my->sent2 == PATTERN && my->sent3 == PATTERN && my->sent4 == PATTERN && my->sent5 == PATTERN && my->sent6 == PATTERN @@ -439,36 +469,49 @@ MAIN(chfPluginTest) dbChannel *pch; db_field_log *pfl; - testPlan(1755); + testPlan(1351); db_init_events(); /* Enum to string conversion */ testHead("Enum to string conversion"); - testOk(strcmp(chfPluginEnumString(colorEnum, 1, "-"), "R") == 0, "Enum to string: R"); - testOk(strcmp(chfPluginEnumString(colorEnum, 2, "-"), "G") == 0, "Enum to string: G"); - testOk(strcmp(chfPluginEnumString(colorEnum, 4, "-"), "B") == 0, "Enum to string: B"); - testOk(strcmp(chfPluginEnumString(colorEnum, 3, "-"), "-") == 0, "Enum to string: invalid index"); + testOk(strcmp(chfPluginEnumString(colorEnum, 1, "-"), "R") == 0, + "Enum to string: R"); + testOk(strcmp(chfPluginEnumString(colorEnum, 2, "-"), "G") == 0, + "Enum to string: G"); + testOk(strcmp(chfPluginEnumString(colorEnum, 4, "-"), "B") == 0, + "Enum to string: B"); + testOk(strcmp(chfPluginEnumString(colorEnum, 3, "-"), "-") == 0, + "Enum to string: invalid index"); - testHead("Set up database"); - testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:..", NULL)); + if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + testAbort("Database description not loaded"); xRecord_registerRecordDeviceDriver(pdbbase); - testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:..", NULL)); - testOk(!!pdbbase, "pdbbase was set"); + if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + testAbort("Test database not loaded"); testHead("Try to register buggy plugins"); - testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts1), "not enough storage for integer"); - testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts2), "not enough storage for double"); - testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts3), "not enough storage for string"); - testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts4), "not enough storage for enum"); + testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts1), + "not enough storage for integer"); + testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts2), + "not enough storage for double"); + testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts3), + "not enough storage for string"); + testOk(!!chfPluginRegister("buggy", &myPif, brokenOpts4), + "not enough storage for enum"); testHead("Register plugins"); - testOk(!chfPluginRegister("strict", &myPif, strictOpts), "register plugin strict"); - testOk(!chfPluginRegister("noconv", &myPif, noconvOpts), "register plugin noconv"); - testOk(!chfPluginRegister("sloppy", &myPif, sloppyOpts), "register plugin sloppy"); - testOk(!chfPluginRegister("pre", &prePif, sloppyOpts), "register plugin pre"); - testOk(!chfPluginRegister("post", &postPif, sloppyOpts), "register plugin post"); + testOk(!chfPluginRegister("strict", &myPif, strictOpts), + "register plugin strict"); + testOk(!chfPluginRegister("noconv", &myPif, noconvOpts), + "register plugin noconv"); + testOk(!chfPluginRegister("sloppy", &myPif, sloppyOpts), + "register plugin sloppy"); + testOk(!chfPluginRegister("pre", &prePif, sloppyOpts), + "register plugin pre"); + testOk(!chfPluginRegister("post", &postPif, sloppyOpts), + "register plugin post"); /* STRICT parsing: mandatory, no conversion */ @@ -476,72 +519,109 @@ MAIN(chfPluginTest) testHead("STRICT parsing: all ok"); e1 = e_alloc | e_ok; c1 = 0; testOk(!!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "strict parsing: JSON correct"); - testOk(checkValues(puser1, 1, 0, 1.2e15, "bar", 1), "guards intact, values correct"); - if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + testOk(checkValues(puser1, 1, 0, 1.2e15, "bar", 1), + "guards intact, values correct"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); e1 = e_close | e_free; c1 = 0; if (pch) dbChannelDelete(pch); testOk(!puser1, "user part cleaned up"); - if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); /* Any one missing must fail */ testHead("STRICT parsing: any missing parameter must fail"); e1 = e_alloc | e_error | e_free; c1 = 0; - testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), "strict parsing: c missing"); + testOk(!(pch = dbChannelCreate( + "x.{\"strict\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), + "strict parsing: c missing"); testOk(!puser1, "user part cleaned up"); - if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); e1 = e_alloc | e_error | e_free; c1 = 0; - testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"f\":false,\"i\":1,\"d\":1.2e15,\"c\":\"R\"}}")), "strict parsing: s missing"); + testOk(!(pch = dbChannelCreate( + "x.{\"strict\":{\"f\":false,\"i\":1,\"d\":1.2e15,\"c\":\"R\"}}")), + "strict parsing: s missing"); testOk(!puser1, "user part cleaned up"); - if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); e1 = e_alloc | e_error | e_free; c1 = 0; - testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"i\":1,\"c\":\"R\",\"f\":false,\"s\":\"bar\"}}")), "strict parsing: d missing"); + testOk(!(pch = dbChannelCreate( + "x.{\"strict\":{\"i\":1,\"c\":\"R\",\"f\":false,\"s\":\"bar\"}}")), + "strict parsing: d missing"); testOk(!puser1, "user part cleaned up"); - if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); e1 = e_alloc | e_error | e_free; c1 = 0; - testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"d\":1.2e15,\"c\":\"R\",\"i\":1,\"s\":\"bar\"}}")), "strict parsing: f missing"); + testOk(!(pch = dbChannelCreate( + "x.{\"strict\":{\"d\":1.2e15,\"c\":\"R\",\"i\":1,\"s\":\"bar\"}}")), + "strict parsing: f missing"); testOk(!puser1, "user part cleaned up"); - if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); e1 = e_alloc | e_error | e_free; c1 = 0; - testOk(!(pch = dbChannelCreate("x.{\"strict\":{\"c\":\"R\",\"s\":\"bar\",\"f\":false,\"d\":1.2e15}}")), "strict parsing: i missing"); + testOk(!(pch = dbChannelCreate( + "x.{\"strict\":{\"c\":\"R\",\"s\":\"bar\",\"f\":false,\"d\":1.2e15}}")), + "strict parsing: i missing"); testOk(!puser1, "user part cleaned up"); - if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); /* NOCONV parsing: optional, no conversion */ /* Any one missing must leave the default intact */ - testHead("NOCONV parsing: any missing parameter must fall back to default value"); + testHead("NOCONV parsing: missing parameters get default value"); e1 = e_alloc | e_ok; c1 = 0; - testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), "noconv parsing: c missing"); - testOk(checkValues(puser1, 1, 0, 1.2e15, "bar", 4), "guards intact, values correct"); - if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + testOk(!!(pch = dbChannelCreate( + "x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"s\":\"bar\"}}")), + "noconv parsing: c missing"); + testOk(checkValues(puser1, 1, 0, 1.2e15, "bar", 4), + "guards intact, values correct"); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); e1 = e_close | e_free; c1 = 0; if (pch) dbChannelDelete(pch); testOk(!puser1, "user part cleaned up"); - if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + if (!testOk(c1 == e1, "all expected calls happened")) + testDiag("expected %#x - called %#x", e1, c1); e1 = e_any; - testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"c\":\"R\"}}")), "noconv parsing: s missing"); - testOk(checkValues(puser1, 1, 0, 1.2e15, "hello", 1), "guards intact, values correct"); + testOk(!!(pch = dbChannelCreate( + "x.{\"noconv\":{\"i\":1,\"f\":false,\"d\":1.2e15,\"c\":\"R\"}}")), + "noconv parsing: s missing"); + testOk(checkValues(puser1, 1, 0, 1.2e15, "hello", 1), + "guards intact, values correct"); if (pch) dbChannelDelete(pch); - testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"f\":false,\"s\":\"bar\",\"c\":\"R\"}}")), "noconv parsing: d missing"); - testOk(checkValues(puser1, 1, 0, 1.234e5, "bar", 1), "guards intact, values correct"); + testOk(!!(pch = dbChannelCreate( + "x.{\"noconv\":{\"i\":1,\"f\":false,\"s\":\"bar\",\"c\":\"R\"}}")), + "noconv parsing: d missing"); + testOk(checkValues(puser1, 1, 0, 1.234e5, "bar", 1), + "guards intact, values correct"); if (pch) dbChannelDelete(pch); - testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"i\":1,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "noconv parsing: f missing"); - testOk(checkValues(puser1, 1, 1, 1.2e15, "bar", 1), "guards intact, values correct"); + testOk(!!(pch = dbChannelCreate( + "x.{\"noconv\":{\"i\":1,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), + "noconv parsing: f missing"); + testOk(checkValues(puser1, 1, 1, 1.2e15, "bar", 1), + "guards intact, values correct"); if (pch) dbChannelDelete(pch); - testOk(!!(pch = dbChannelCreate("x.{\"noconv\":{\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), "noconv parsing: i missing"); - testOk(checkValues(puser1, 12, 0, 1.2e15, "bar", 1), "guards intact, values correct"); + testOk(!!(pch = dbChannelCreate( + "x.{\"noconv\":{\"f\":false,\"d\":1.2e15,\"s\":\"bar\",\"c\":\"R\"}}")), + "noconv parsing: i missing"); + testOk(checkValues(puser1, 12, 0, 1.2e15, "bar", 1), + "guards intact, values correct"); if (pch) dbChannelDelete(pch); /* Reject wrong types */ #define WRONGTYPETEST(Var, Val, Typ) \ e1 = e_alloc | e_error | e_free; c1 = 0; \ - testOk(!(pch = dbChannelCreate("x.{\"noconv\":{\""#Var"\":"#Val"}}")), "noconv parsing: wrong type "#Typ" for "#Var); \ + testOk(!(pch = dbChannelCreate("x.{\"noconv\":{\""#Var"\":"#Val"}}")), \ + "noconv parsing: wrong type "#Typ" for "#Var); \ testOk(!puser1, "user part cleaned up"); \ - if (!testOk(c1 == e1, "all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + if (!testOk(c1 == e1, "all expected calls happened")) \ + testDiag("expected %#x - called %#x", e1, c1); testHead("NOCONV parsing: rejection of wrong parameter types"); @@ -565,19 +645,25 @@ MAIN(chfPluginTest) #define CONVTESTGOOD(Var, Val, Typ, Ival, Fval, Dval, Sval, Cval) \ e1 = e_alloc | e_ok; c1 = 0; \ - testOk(!!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), "sloppy parsing: "#Typ" (good) for "#Var); \ - testOk(checkValues(puser1, Ival, Fval, Dval, Sval, Cval), "guards intact, values correct"); \ - if (!testOk(c1 == e1, "create channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); \ + testOk(!!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), \ + "sloppy parsing: "#Typ" (good) for "#Var); \ + testOk(checkValues(puser1, Ival, Fval, Dval, Sval, Cval), \ + "guards intact, values correct"); \ + if (!testOk(c1 == e1, "create channel: all expected calls happened")) \ + testDiag("expected %#x - called %#x", e1, c1); \ e1 = e_close | e_free; c1 = 0; \ if (pch) dbChannelDelete(pch); \ testOk(!puser1, "user part cleaned up"); \ - if (!testOk(c1 == e1, "delete channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + if (!testOk(c1 == e1, "delete channel: all expected calls happened")) \ + testDiag("expected %#x - called %#x", e1, c1); #define CONVTESTBAD(Var, Val, Typ) \ e1 = e_alloc | e_error | e_free; c1 = 0; \ - testOk(!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), "sloppy parsing: "#Typ" (bad) for "#Var); \ + testOk(!(pch = dbChannelCreate("x.{\"sloppy\":{\""#Var"\":"#Val"}}")), \ + "sloppy parsing: "#Typ" (bad) for "#Var); \ testOk(!puser1, "user part cleaned up"); \ - if (!testOk(c1 == e1, "create channel: all expected calls happened")) testDiag("expected %#x - called %#x", e1, c1); + if (!testOk(c1 == e1, "create channel: all expected calls happened")) \ + testDiag("expected %#x - called %#x", e1, c1); /* To integer */ testHead("SLOPPY parsing: conversion to integer"); @@ -594,7 +680,7 @@ MAIN(chfPluginTest) CONVTESTGOOD(f, "false", valid string, 12, 0, 1.234e5, "hello", 4); CONVTESTGOOD(f, "False", capital valid string, 12, 0, 1.234e5, "hello", 4); CONVTESTGOOD(f, "0", 0 string, 12, 0, 1.234e5, "hello", 4); - CONVTESTGOOD(f, "15", 15 string, 12, 1, 1.234e5, "hello", 4); /* Any one missing must leave the default intact */ + CONVTESTGOOD(f, "15", 15 string, 12, 1, 1.234e5, "hello", 4); CONVTESTBAD(f, ".4", invalid .4 string); CONVTESTBAD(f, "Flase", misspelled invalid string); CONVTESTGOOD(f, 0, zero integer, 12, 0, 1.234e5, "hello", 4); diff --git a/src/ioc/db/test/dbChannelTest.c b/src/ioc/db/test/dbChannelTest.c index ad007d812..94de616f7 100644 --- a/src/ioc/db/test/dbChannelTest.c +++ b/src/ioc/db/test/dbChannelTest.c @@ -149,17 +149,18 @@ chFilterIf testIf = void xRecord_registerRecordDeviceDriver(struct dbBase *); -MAIN(dbChannelTest) +MAIN(testDbChannel) /* dbChannelTest is an API routine... */ { dbChannel *pch; - testPlan(69); + testPlan(66); - testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:..", NULL)); + if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + testAbort("Database description not loaded"); xRecord_registerRecordDeviceDriver(pdbbase); - testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:..", NULL)); - testOk(!!pdbbase, "pdbbase was set"); + if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + testAbort("Test database not loaded"); r = e = 0; /* dbChannelTest() checks record and field names */ diff --git a/src/std/Makefile b/src/std/Makefile index 72dd2b2b0..6b63cc8f5 100644 --- a/src/std/Makefile +++ b/src/std/Makefile @@ -19,6 +19,7 @@ dbRecStd_RCS += dbRecStd.rc include $(STDDIR)/rec/Makefile include $(STDDIR)/dev/Makefile +include $(STDDIR)/filters/Makefile include $(STDDIR)/test/Makefile include $(STDDIR)/softIoc/Makefile diff --git a/src/std/filters/Makefile b/src/std/filters/Makefile new file mode 100644 index 000000000..7e33500d1 --- /dev/null +++ b/src/std/filters/Makefile @@ -0,0 +1,18 @@ +#************************************************************************* +# Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne +# National Laboratory. +# EPICS BASE is distributed subject to a Software License Agreement found +# in file LICENSE that is included with this distribution. +#************************************************************************* + +# This is a Makefile fragment, see src/std/Makefile. + +SRC_DIRS += $(STDDIR)/filters + +DBD_INSTALLS += filters.dbd + +dbRecStd_SRCS += ts.c +dbRecStd_SRCS += dbnd.c +dbRecStd_SRCS += arr.c +dbRecStd_SRCS += sync.c + diff --git a/src/ioc/db/filters/arr.c b/src/std/filters/arr.c similarity index 99% rename from src/ioc/db/filters/arr.c rename to src/std/filters/arr.c index 7fa392a08..bc0fb3609 100644 --- a/src/ioc/db/filters/arr.c +++ b/src/std/filters/arr.c @@ -116,9 +116,10 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { pfl->field_size = chan->addr.field_size; pfl->no_elements = nTarget; if (nTarget) { + void *pdst = freeListCalloc(my->arrayFreeList); + pfl->u.r.dtor = freeArray; pfl->u.r.pvt = my->arrayFreeList; - void *pdst = freeListCalloc(my->arrayFreeList); offset = (offset + start) % chan->addr.no_elements; dbExtractArrayFromRec(&chan->addr, pdst, nTarget, nSource, offset, my->incr); pfl->u.r.field = pdst; @@ -127,11 +128,12 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { /* Extract from buffer */ } else if (pfl->type == dbfl_type_ref) { + void *psrc = pfl->u.r.field; + void *pdst = NULL; + nSource = pfl->no_elements; nTarget = wrapArrayIndices(&start, my->incr, &end, nSource); pfl->no_elements = nTarget; - void *psrc = pfl->u.r.field; - void *pdst = NULL; if (nTarget) { /* Copy the data out */ pdst = freeListCalloc(my->arrayFreeList); offset = start; diff --git a/src/ioc/db/filters/dbnd.c b/src/std/filters/dbnd.c similarity index 100% rename from src/ioc/db/filters/dbnd.c rename to src/std/filters/dbnd.c diff --git a/src/ioc/db/filters/filters.dbd b/src/std/filters/filters.dbd similarity index 100% rename from src/ioc/db/filters/filters.dbd rename to src/std/filters/filters.dbd diff --git a/src/ioc/db/filters/sync.c b/src/std/filters/sync.c similarity index 98% rename from src/ioc/db/filters/sync.c rename to src/std/filters/sync.c index 33b059151..15c27b267 100644 --- a/src/ioc/db/filters/sync.c +++ b/src/std/filters/sync.c @@ -73,12 +73,14 @@ static int parse_ok(void *pvt) } static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { + db_field_log *passfl = NULL; + myStruct *my = (myStruct*) pvt; + int actstate; + if (pfl->ctx == dbfl_context_read) return pfl; - db_field_log *passfl = NULL; - myStruct *my = (myStruct*) pvt; - int actstate = dbStateGet(my->id); + actstate = dbStateGet(my->id); switch (my->mode) { case syncModeBefore: diff --git a/src/ioc/db/filters/test/Makefile b/src/std/filters/test/Makefile similarity index 81% rename from src/ioc/db/filters/test/Makefile rename to src/std/filters/test/Makefile index c2b965af4..4eb0b1cd6 100644 --- a/src/ioc/db/filters/test/Makefile +++ b/src/std/filters/test/Makefile @@ -6,11 +6,11 @@ # EPICS BASE is distributed subject to a Software License Agreement found # in the file LICENSE that is included with this distribution. #************************************************************************* -TOP=../../../../.. +TOP=../../../.. include $(TOP)/configure/CONFIG -PROD_LIBS += dbCore ca Com +PROD_LIBS += dbRecStd dbCore ca Com TESTPROD_HOST += tsTest tsTest_SRCS += tsTest.c xRecord_registerRecordDeviceDriver.cpp @@ -38,10 +38,8 @@ TESTSCRIPTS_HOST += $(TESTS:%=%.t) include $(TOP)/configure/RULES -$(COMMON_DIR)/xRecord.dbd: ../../../test/xRecord.dbd - $(INSTALL) -d $< $(@D) - -tsTest$(OBJ): $(COMMON_DIR)/xRecord.h -dbndTest$(OBJ): $(COMMON_DIR)/xRecord.h -syncTest$(OBJ): $(COMMON_DIR)/xRecord.h -arrTest$(OBJ): $(COMMON_DIR)/arrRecord.h +tsTest$(DEP): $(COMMON_DIR)/xRecord.h +dbndTest$(DEP): $(COMMON_DIR)/xRecord.h +syncTest$(DEP): $(COMMON_DIR)/xRecord.h +arrRecord$(DEP): $(COMMON_DIR)/arrRecord.h +arrTest$(DEP): $(COMMON_DIR)/arrRecord.h diff --git a/src/ioc/db/filters/test/arrRecord.c b/src/std/filters/test/arrRecord.c similarity index 100% rename from src/ioc/db/filters/test/arrRecord.c rename to src/std/filters/test/arrRecord.c diff --git a/src/ioc/db/filters/test/arrRecord.dbd b/src/std/filters/test/arrRecord.dbd similarity index 100% rename from src/ioc/db/filters/test/arrRecord.dbd rename to src/std/filters/test/arrRecord.dbd diff --git a/src/ioc/db/filters/test/arrTest.cpp b/src/std/filters/test/arrTest.cpp similarity index 92% rename from src/ioc/db/filters/test/arrTest.cpp rename to src/std/filters/test/arrTest.cpp index 7a504c5fb..389b1fc9a 100644 --- a/src/ioc/db/filters/test/arrTest.cpp +++ b/src/std/filters/test/arrTest.cpp @@ -133,22 +133,22 @@ static void check(short dbr_type) { dbAddr valaddr; dbAddr offaddr; const char *offname = NULL, *valname = NULL, *typname = NULL; - long ar[10] = {10,11,12,13,14,15,16,17,18,19}; - long *ar10_0_1 = ar; - long ar10_4_1[10] = {14,15,16,17,18,19,10,11,12,13}; - long ar5_0_1[10] = {12,13,14,15,16}; - long ar5_3_1[10] = {15,16,17,18,19}; - long ar5_5_1[10] = {17,18,19,10,11}; - long ar5_9_1[10] = {11,12,13,14,15}; - long ar5_0_2[10] = {12,14,16}; - long ar5_3_2[10] = {15,17,19}; - long ar5_5_2[10] = {17,19,11}; - long ar5_9_2[10] = {11,13,15}; - long ar5_0_3[10] = {12,15}; - long ar5_3_3[10] = {15,18}; - long ar5_5_3[10] = {17,10}; - long ar5_9_3[10] = {11,14}; - long off = 0; + epicsInt32 ar[10] = {10,11,12,13,14,15,16,17,18,19}; + epicsInt32 *ar10_0_1 = ar; + epicsInt32 ar10_4_1[10] = {14,15,16,17,18,19,10,11,12,13}; + epicsInt32 ar5_0_1[10] = {12,13,14,15,16}; + epicsInt32 ar5_3_1[10] = {15,16,17,18,19}; + epicsInt32 ar5_5_1[10] = {17,18,19,10,11}; + epicsInt32 ar5_9_1[10] = {11,12,13,14,15}; + epicsInt32 ar5_0_2[10] = {12,14,16}; + epicsInt32 ar5_3_2[10] = {15,17,19}; + epicsInt32 ar5_5_2[10] = {17,19,11}; + epicsInt32 ar5_9_2[10] = {11,13,15}; + epicsInt32 ar5_0_3[10] = {12,15}; + epicsInt32 ar5_3_3[10] = {15,18}; + epicsInt32 ar5_5_3[10] = {17,10}; + epicsInt32 ar5_9_3[10] = {11,14}; + epicsInt32 off = 0; switch (dbr_type) { case DBR_LONG: @@ -297,23 +297,23 @@ MAIN(arrTest) { const chFilterPlugin *plug; char arr[] = "arr"; - int status; - testPlan(1404); + testPlan(1402); /* Prepare the IOC */ epicsEnvSet("EPICS_CA_SERVER_PORT", server_port); - testOk1(!(status=dbReadDatabase(&pdbbase, "arrRecord.dbd", ".:..:../../../../../../dbd", NULL))); - if (status) epicsExit(EXIT_FAILURE); + if (dbReadDatabase(&pdbbase, "arrRecord.dbd", + "..:../../../../../dbd", NULL)) + testAbort("Database description not loaded"); (*pvar_func_arrInitialize)(); arrRecord_registerRecordDeviceDriver(pdbbase); registryFunctionAdd("exit", (REGISTRYFUNCTION) exitSubroutine); - testOk1(!(status=dbReadDatabase(&pdbbase, "arrTest.db", ".:..", NULL))); - if (status) epicsExit(EXIT_FAILURE); + if (dbReadDatabase(&pdbbase, "arrTest.db", "..", NULL)) + testAbort("Test database not loaded"); /* Start the IOC */ diff --git a/src/ioc/db/filters/test/arrTest.db b/src/std/filters/test/arrTest.db similarity index 100% rename from src/ioc/db/filters/test/arrTest.db rename to src/std/filters/test/arrTest.db diff --git a/src/std/filters/test/dbChannelTest.db b/src/std/filters/test/dbChannelTest.db new file mode 100644 index 000000000..a6fa08e40 --- /dev/null +++ b/src/std/filters/test/dbChannelTest.db @@ -0,0 +1,2 @@ +record(x, x) {} + diff --git a/src/ioc/db/filters/test/dbndTest.c b/src/std/filters/test/dbndTest.c similarity index 94% rename from src/ioc/db/filters/test/dbndTest.c rename to src/std/filters/test/dbndTest.c index 5cb092cdf..18cccc361 100644 --- a/src/ioc/db/filters/test/dbndTest.c +++ b/src/std/filters/test/dbndTest.c @@ -57,9 +57,11 @@ static void changeValue(db_field_log *pfl2, long val) { } static void mustPassOnce(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) { + db_field_log *pfl; + changeValue(pfl2, val); testDiag("mode=%s delta=%g filter must pass once", m, d); - db_field_log *pfl = dbChannelRunPreChain(pch, pfl2); + pfl = dbChannelRunPreChain(pch, pfl2); testOk(pfl2 == pfl, "call 1 does not drop or replace field_log"); testOk(fl_equal(pfl, pfl2), "call 1 does not change field_log data"); pfl = dbChannelRunPreChain(pch, pfl2); @@ -67,16 +69,20 @@ static void mustPassOnce(dbChannel *pch, db_field_log *pfl2, char* m, double d, } static void mustDrop(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) { + db_field_log *pfl; + changeValue(pfl2, val); testDiag("mode=%s delta=%g filter must drop", m, d); - db_field_log *pfl = dbChannelRunPreChain(pch, pfl2); + pfl = dbChannelRunPreChain(pch, pfl2); testOk(NULL == pfl, "call 1 drops field_log"); } static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) { + db_field_log *pfl; + changeValue(pfl2, val); testDiag("mode=%s delta=%g filter must pass twice", m, d); - db_field_log *pfl = dbChannelRunPreChain(pch, pfl2); + pfl = dbChannelRunPreChain(pch, pfl2); testOk(pfl2 == pfl, "call 1 does not drop or replace field_log"); testOk(fl_equal(pfl, pfl2), "call 1 does not change field_log data"); pfl = dbChannelRunPreChain(pch, pfl2); @@ -104,17 +110,18 @@ MAIN(dbndTest) db_field_log *pfl2; db_field_log fl1; - testPlan(62); + testPlan(59); db_init_events(); - testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:../../../test", NULL)); - testOk(!!pdbbase, "pdbbase was set"); + if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + testAbort("Database description not loaded"); (*pvar_func_dbndInitialize)(); /* manually initialize plugin */ - xRecord_registerRecordDeviceDriver(pdbbase); - testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:../../../test", NULL)); + + if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + testAbort("Test database not loaded"); testOk(!!(plug = dbFindFilter(dbnd, strlen(dbnd))), "plugin dbnd registered correctly"); diff --git a/src/ioc/db/filters/test/syncTest.c b/src/std/filters/test/syncTest.c similarity index 95% rename from src/ioc/db/filters/test/syncTest.c rename to src/std/filters/test/syncTest.c index 4d1fd3159..11c772491 100644 --- a/src/ioc/db/filters/test/syncTest.c +++ b/src/std/filters/test/syncTest.c @@ -55,10 +55,6 @@ static void fl_setup(dbChannel *chan, db_field_log *pfl, long val) { pfl->u.v.field.dbf_long = val; } -static void changeValue(db_field_log *pfl2, long val) { - pfl2->u.v.field.dbf_long = val; -} - static void testHead (char* title) { testDiag("--------------------------------------------------------"); testDiag(title); @@ -71,8 +67,10 @@ static void mustDrop(dbChannel *pch, db_field_log *pfl2, char* m) { } static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m) { + db_field_log *pfl; + testDiag("%s: filter must pass twice", m); - db_field_log *pfl = dbChannelRunPreChain(pch, pfl2); + pfl = dbChannelRunPreChain(pch, pfl2); testOk(pfl2 == pfl, "call 1 does not drop or replace field_log"); pfl = dbChannelRunPreChain(pch, pfl2); testOk(pfl2 == pfl, "call 2 does not drop or replace field_log"); @@ -80,11 +78,13 @@ static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m) { static void mustPassOld(dbChannel *pch, db_field_log *old, db_field_log *cur, char* m) { db_field_log *pfl = dbChannelRunPreChain(pch, cur); + testOk(old == pfl, "filter passes previous field log (%s)", m); } static void mustPass(dbChannel *pch, db_field_log *cur, char* m) { db_field_log *pfl = dbChannelRunPreChain(pch, cur); + testOk(cur == pfl, "filter passes field_log (%s)", m); } @@ -104,12 +104,13 @@ static void checkAndOpenChannel(dbChannel *pch, const chFilterPlugin *plug) { chFilter *filter; chPostEventFunc *cb_out = NULL; void *arg_out = NULL; + db_field_log fl1; testDiag("Test filter structure and open channel"); testOk((ellCount(&pch->filters) == 1), "channel has one plugin"); - db_field_log fl1 = fl; + fl1 = fl; node = ellFirst(&pch->filters); filter = CONTAINER(node, chFilter, list_node); plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1); @@ -128,31 +129,26 @@ static void checkAndOpenChannel(dbChannel *pch, const chFilterPlugin *plug) { void xRecord_registerRecordDeviceDriver(struct dbBase *); -//MAIN(syncTest) -int main() +MAIN(syncTest) { dbChannel *pch; - chFilter *filter; const chFilterPlugin *plug; char myname[] = "sync"; - ELLNODE *node; - chPostEventFunc *cb_out = NULL; - void *arg_out = NULL; db_field_log *pfl[10]; - db_field_log fl1; int i; testPlan(0); db_init_events(); - testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:../../../test", NULL)); - testOk(!!pdbbase, "pdbbase was set"); + if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + testAbort("Database description not loaded"); (*pvar_func_syncInitialize)(); /* manually initialize plugin */ - xRecord_registerRecordDeviceDriver(pdbbase); - testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:../../../test", NULL)); + + if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + testAbort("Test database not loaded"); testOk(!!(plug = dbFindFilter(myname, strlen(myname))), "plugin %s registered correctly", myname); testOk(!!(red = dbStateCreate("red")), "state 'red' created successfully"); diff --git a/src/ioc/db/filters/test/tsTest.c b/src/std/filters/test/tsTest.c similarity index 90% rename from src/ioc/db/filters/test/tsTest.c rename to src/std/filters/test/tsTest.c index 3a039afe8..9c54063ce 100644 --- a/src/ioc/db/filters/test/tsTest.c +++ b/src/std/filters/test/tsTest.c @@ -49,18 +49,20 @@ MAIN(tsTest) void *arg_out = NULL; db_field_log fl1; db_field_log *pfl2; + epicsTimeStamp stamp, now; - testPlan(15); + testPlan(12); db_init_events(); - testOk1(!dbReadDatabase(&pdbbase, "xRecord.dbd", ".:../../../test", NULL)); - testOk(!!pdbbase, "pdbbase was set"); + if (dbReadDatabase(&pdbbase, "xRecord.dbd", "..", NULL)) + testAbort("Database description not loaded"); (*pvar_func_tsInitialize)(); /* manually initialize plugin */ - xRecord_registerRecordDeviceDriver(pdbbase); - testOk1(!dbReadDatabase(&pdbbase, "dbChannelTest.db", ".:../../../test", NULL)); + + if (dbReadDatabase(&pdbbase, "dbChannelTest.db", "..", NULL)) + testAbort("Test database not loaded"); testOk(!!(plug = dbFindFilter(ts, strlen(ts))), "plugin ts registered correctly"); @@ -89,9 +91,9 @@ MAIN(tsTest) testOk(fl_equal_ex_ts(&fl1, pfl2), "ts filter does not change field_log data"); testOk(!!(pfl2 = db_create_read_log(pch)), "create field log from channel"); - epicsTimeStamp stamp = pfl2->time; + stamp = pfl2->time; pfl2 = dbChannelRunPreChain(pch, &fl1); - epicsTimeStamp now; epicsTimeGetCurrent(&now); + epicsTimeGetCurrent(&now); testOk(epicsTimeDiffInSeconds(&pfl2->time, &stamp) > 0. && epicsTimeDiffInSeconds(&now, &pfl2->time) > 0., "ts filter sets time stamp to \"now\""); diff --git a/src/std/filters/test/xRecord.dbd b/src/std/filters/test/xRecord.dbd new file mode 100644 index 000000000..4837871a4 --- /dev/null +++ b/src/std/filters/test/xRecord.dbd @@ -0,0 +1,12 @@ +# This is a combined minimal DBD and DB file + +recordtype(x) { + field(NAME, DBF_STRING) { + prompt("Record Name") + special(SPC_NOMOD) + size(61) + } + field(VAL, DBF_LONG) { + prompt("Value") + } +} diff --git a/src/ioc/db/filters/ts.c b/src/std/filters/ts.c similarity index 100% rename from src/ioc/db/filters/ts.c rename to src/std/filters/ts.c