Files
epics-base/modules/database/src/ioc/db/dbChannel.h
Michael Davidsaver 9f788996dc db: lock record for db_create_read_log() and dbChannelGetField()
since 27fe3e4468 db_create_read_log()
accesses record fields.

remove now redundant db_create_read_log() calls.
2025-03-19 10:51:08 -05:00

594 lines
20 KiB
C

/*************************************************************************\
* 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.
* SPDX-License-Identifier: EPICS
* 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
/** \file dbChannel.h
*
* \author Andrew Johnson (ANL)
* \author Ralph Lange (BESSY)
*
* \brief The dbChannel API gives access to record fields.
*
* The dbChannel API is used internally by the IOC and by link types, device
* support and IOC servers (RSRV and QSRV) to access record fields, either
* directly or through one or more server-side filters as specified in the
* channel name used when creating the channel.
*/
#include "dbDefs.h"
#include "dbAddr.h"
#include "ellLib.h"
#include "epicsTypes.h"
#include "errMdef.h"
#include "db_field_log.h"
#include "dbEvent.h"
#include "dbCoreAPI.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* event subscription
*/
struct evSubscrip;
typedef struct evSubscrip evSubscrip;
#ifdef EPICS_PRIVATE_API
struct evSubscrip {
ELLNODE node;
struct dbChannel * chan;
/* user_sub==NULL used to indicate db_cancel_event() */
EVENTFUNC * user_sub;
void * user_arg;
/* associated queue, may be shared with other evSubscrip */
struct event_que * ev_que;
/* NULL if !npend. if npend!=0, pointer to last event added to event_que::valque */
db_field_log ** pLastLog;
/* n times this event is on the queue */
unsigned long npend;
/* n times replacing event on the queue */
unsigned long nreplace;
/* DBE mask */
unsigned char select;
/* if set, subscription will yield dbfl_type_val */
char useValque;
/* event_task is handling this subscription */
char callBackInProgress;
/* this node added to dbCommon::mlis */
char enabled;
};
#endif
typedef struct chFilter chFilter;
/** \brief A Database Channel object
*
* A dbChannel is created from a user-supplied channel name, and holds
* pointers to the record & field and information about any filters that
* were specified with it. The dbChannel macros defined in this header
* file should always be used to read data from a dbChannel object, the
* internal implementation may change without notice.
*/
typedef struct dbChannel {
const char *name; /**< Channel name */
dbAddr addr; /**< Pointers to record & field */
long final_no_elements; /**< Final number of array elements */
short final_field_size; /**< Final size of each element */
short final_type; /**< Final type of database field */
ELLLIST filters; /**< Filters used by dbChannel */
ELLLIST pre_chain; /**< Filters on pre-event-queue chain */
ELLLIST post_chain; /**< Filters on post-event-queue chain */
} dbChannel;
/** \brief Event filter function type
*
* Prototype for channel event filter functions.
*
* When these functions are called the scan lock for the record associated
* with \p chan _may_ already be locked, but they must use dbfl_has_copy()
* to determine whether the data in \p pLog belongs to the record. If that
* returns 0 the function must call dbScanLock() before accessing the data.
*
* A filter function owns the field log \p pLog when called. To discard an
* update it should free the field log using db_delete_field_log() and
* return NULL.
*/
typedef db_field_log* (chPostEventFunc)(void *pvt, dbChannel *chan, db_field_log *pLog);
/** \brief Result returned by chFilterIf parse routines.
*
* The parsing functions from a chFilterIf must return either \p parse_stop
* (in event of an error) or \p parse_continue.
*/
typedef enum {
parse_stop, parse_continue
} parse_result;
/** \brief Channel Filter Interface
*
* Routines to be implemented by each Channel Filter.
*/
typedef struct chFilterIf {
/** \brief Release private filter data.
*
* Called during database shutdown to release resources allocated by
* the filter.
* \param puser The user-pointer passed into dbRegisterFilter().
*/
void (* priv_free)(void *puser);
/** \name Parsing event handlers
*
* A filter that doesn't accept a particular JSON value type may use a
* \p NULL pointer to the parsing handler for that value type, which is
* equivalent to a routine that always returns \p parse_stop.
*/
/** \brief Create new filter instance.
*
* Called when a new filter instance is requested. Filter may allocate
* resources for this instance and store in \p filter->puser.
* If parse_start() returns \p parse_continue for a filter, one of
* parse_abort() or parse_end() will later be called for that same
* filter.
* \param filter Pointer to instance data.
* \returns \p parse_stop on error, or \p parse_continue
*/
parse_result (* parse_start)(chFilter *filter);
/** \brief Parsing of filter instance is being cancelled.
*
* This function should release any memory allocated for the given
* \p filter instance; no further parsing handlers will be called for it.
* \param filter Pointer to instance data.
*/
void (* parse_abort)(chFilter *filter);
/** \brief Parsing of filter instance has completed successfully.
*
* The parser has reached the end of this instance and no further parsing
* handlers will be called for it. The filter must check the instance
* data and indicate whether it was complete or not.
* \param filter Pointer to instance data.
* \returns \p parse_stop on error, or \p parse_continue
*/
parse_result (* parse_end)(chFilter *filter);
/** \brief Parser saw \p null value.
*
* Optional.
* Null values are rarely accepted by channel filters.
* \param filter Pointer to instance data.
* \returns \p parse_stop on error, or \p parse_continue
*/
parse_result (* parse_null)(chFilter *filter);
/** \brief Parser saw boolean value.
*
* Optional.
* \param filter Pointer to instance data.
* \param boolVal true/false Value.
* \returns \p parse_stop on error, or \p parse_continue
*/
parse_result (* parse_boolean)(chFilter *filter, int boolVal);
/** \brief Parser saw integer value.
*
* Optional.
* \param filter Pointer to instance data.
* \param integerVal Value.
* \returns \p parse_stop on error, or \p parse_continue
*/
parse_result (* parse_integer)(chFilter *filter, long integerVal);
/** \brief Parser saw double value.
*
* Optional.
* \param filter Pointer to instance data.
* \param doubleVal Value.
* \returns \p parse_stop on error, or \p parse_continue
*/
parse_result (* parse_double)(chFilter *filter, double doubleVal);
/** \brief Parser saw string value.
*
* Optional.
* \param filter Pointer to instance data.
* \param stringVal Value, not zero-terminated.
* \param stringLen Number of chars in \p stringVal.
* \returns \p parse_stop on error, or \p parse_continue
*/
parse_result (* parse_string)(chFilter *filter, const char *stringVal,
size_t stringLen);
/** \brief Parser saw start of a JSON map value.
*
* Optional.
* Inside a JSON map all data consists of key/value pairs.
* \param filter Pointer to instance data.
* \returns \p parse_stop on error, or \p parse_continue
*/
parse_result (* parse_start_map)(chFilter *filter);
/** \brief Parser saw a JSON map key.
*
* Optional.
* \param filter Pointer to instance data.
* \param key Value not zero-terminated.
* \param stringLen Number of chars in \p key
* \returns \p parse_stop on error, or \p parse_continue
*/
parse_result (* parse_map_key)(chFilter *filter, const char *key,
size_t stringLen);
/** \brief Parser saw end of a JSON map value.
*
* Optional.
* \param filter Pointer to instance data.
* \returns \p parse_stop on error, or \p parse_continue
*/
parse_result (* parse_end_map)(chFilter *filter);
/** \brief Parser saw start of a JSON array value.
*
* Optional.
* Data inside a JSON array doesn't have to be all of the same type.
* \param filter Pointer to instance data.
* \returns \p parse_stop on error, or \p parse_continue
*/
parse_result (* parse_start_array)(chFilter *filter);
/** \brief Parser saw end of a JSON array value.
*
* Optional.
* \param filter Pointer to instance data.
* \returns \p parse_stop on error, or \p parse_continue
*/
parse_result (* parse_end_array)(chFilter *filter);
/** \name Channel operations */
/** \brief Open filter on channel.
*
* Optional, initialize instance.
* \param filter Pointer to instance data.
* \returns 0, or an error status value.
*/
long (* channel_open)(chFilter *filter);
/** \brief Get pre-chain filter function.
*
* Optional.
* Returns pre-chain filter function and context.
* \param[in] filter Pointer to instance data.
* \param[out] cb_out Write filter function pointer here.
* \param[out] arg_out Write private data pointer here.
* \param[in,out] probe db_field_log with metadata for adjusting.
*/
void (* channel_register_pre) (chFilter *filter,
chPostEventFunc **cb_out, void **arg_out, db_field_log *probe);
/** \brief Get post-chain filter function.
*
* Optional, return post-chain filter function and context.
* \param[in] filter Pointer to instance data.
* \param[out] cb_out Write filter function pointer here.
* \param[out] arg_out Write private data pointer here.
* \param[in,out] probe db_field_log with metadata for adjusting.
*/
void (* channel_register_post)(chFilter *filter,
chPostEventFunc **cb_out, void **arg_out, db_field_log *probe);
/** \brief Print information about filter to stdout.
*
* Optional.
* \param filter Pointer to instance data.
* \param level Higher levels may provide more detail.
* \param indent Indent all lines by this many spaces.
*/
void (* channel_report)(chFilter *filter,
int level, const unsigned short indent);
/** \brief Close filter.
*
* Optional, releases resources allocated for this instance.
* \param filter Pointer to instance data.
*/
void (* channel_close)(chFilter *filter);
} chFilterIf;
/** \brief Filter plugin data
*
* A chFilterPlugin object holds data about a filter plugin.
*/
typedef struct chFilterPlugin {
ELLNODE node; /**< \brief List node (dbBase->filterList) */
const char *name; /**< \brief Filter name */
const chFilterIf *fif; /**< \brief Filter interface routines */
void *puser; /**< \brief For use by the plugin */
} chFilterPlugin;
/** \brief Filter instance data
*
* A chFilter holds data about a single filter instance.
*/
struct chFilter {
ELLNODE list_node; /**< \brief List node (dbChannel->filters) */
ELLNODE pre_node; /**< \brief List node (dbChannel->pre_chain) */
ELLNODE post_node; /**< \brief List node (dbChannel->post_chain) */
dbChannel *chan; /**< \brief The dbChannel we belong to */
const chFilterPlugin *plug; /**< \brief The plugin that created us */
chPostEventFunc *pre_func; /**< \brief pre-chain filter function */
void *pre_arg; /**< \brief pre-chain context pointer */
chPostEventFunc *post_func; /**< \brief post-chain filter function */
void *post_arg; /**< \brief post-chain context pointer */
void *puser; /**< \brief For use by the plugin */
};
struct dbCommon;
struct dbFldDes;
/** \brief Initialize the dbChannel subsystem. */
DBCORE_API void dbChannelInit(void);
/** \brief Cleanup the dbChannel subsystem. */
DBCORE_API void dbChannelExit(void);
/** \brief Test the given PV name for existance.
*
* This routine looks up the given record and field name, but does not check
* whether any field modifiers given after the field name are correct.
* This is sufficient for the correct server to quickly direct searches to the
* IOC that owns that PV name. Field modifiers will be checked when
* dbChannelCreate() is later called with the same name.
* \param name Channel name.
* \returns 0, or an error status value.
*/
DBCORE_API long dbChannelTest(const char *name);
/** \brief Create a dbChannel object for the given PV name.
*
* \param name Channel name.
* \return Pointer to dbChannel object, or NULL if invalid.
*/
DBCORE_API dbChannel * dbChannelCreate(const char *name);
/** \brief Open a dbChannel for doing I/O.
*
* \param chan Pointer to the dbChannel object.
* \returns 0, or an error status value.
*/
DBCORE_API long dbChannelOpen(dbChannel *chan);
/** \brief Request (DBR) type conversion array.
*
* This converter array is declared in db_convert.h but redeclared
* here as it is needed by the dbChannel...CAType macros defined here.
*/
DBCORE_API extern unsigned short dbDBRnewToDBRold[];
/** \name dbChannel Inspection Macros */
/** \brief Name that defined the channel.
*
* \param pChan Pointer to the dbChannel object.
* \returns const char*
*/
#define dbChannelName(pChan) ((pChan)->name)
/** \brief Record the channel connects to.
*
* \param pChan Pointer to the dbChannel object.
* \returns struct dbCommon*
*/
#define dbChannelRecord(pChan) ((pChan)->addr.precord)
/** \brief Field descriptor for the field pointed to.
*
* \param pChan Pointer to the dbChannel object.
* \returns struct dbFldDes*
*/
#define dbChannelFldDes(pChan) ((pChan)->addr.pfldDes)
/** \brief Number of array elements in the field.
*
* \param pChan Pointer to the dbChannel object.
* \returns long
*/
#define dbChannelElements(pChan) ((pChan)->addr.no_elements)
/** \brief Data type (DBF type) of the field.
*
* \param pChan Pointer to the dbChannel object.
* \returns short
*/
#define dbChannelFieldType(pChan) ((pChan)->addr.field_type)
/** \brief Request type (DBR type) of the field.
*
* \param pChan Pointer to the dbChannel object.
* \returns short
*/
#define dbChannelExportType(pChan) ((pChan)->addr.dbr_field_type)
/** \brief CA data type of the field.
*
* \param pChan Pointer to the dbChannel object.
* \returns short
*/
#define dbChannelExportCAType(pChan) (dbDBRnewToDBRold[dbChannelExportType(pChan)])
/** \brief Field (element if array) size in bytes.
*
* \param pChan Pointer to the dbChannel object.
* \returns short
*/
#define dbChannelFieldSize(pChan) ((pChan)->addr.field_size)
/** \brief Array length after filtering.
*
* \param pChan Pointer to the dbChannel object.
* \returns long
*/
#define dbChannelFinalElements(pChan) ((pChan)->final_no_elements)
/** \brief Data type after filtering.
*
* \param pChan Pointer to the dbChannel object.
* \returns short
*/
#define dbChannelFinalFieldType(pChan) ((pChan)->final_type)
/** \brief Channel CA data type after filtering.
*
* \param pChan Pointer to the dbChannel object.
* \returns short
*/
#define dbChannelFinalCAType(pChan) (dbDBRnewToDBRold[(pChan)->final_type])
/** \brief Field/element size after filtering, in bytes.
*
* \param pChan Pointer to the dbChannel object.
* \returns short */
#define dbChannelFinalFieldSize(pChan) ((pChan)->final_field_size)
/** \brief Field special attribute.
*
* \param pChan Pointer to the dbChannel object.
* \returns short
*/
#define dbChannelSpecial(pChan) ((pChan)->addr.special)
/** \brief Pointer to the 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.
* \param pChan Pointer to the dbChannel object.
* \returns void *
*/
#define dbChannelField(pChan) ((pChan)->addr.pfield)
/** \name dbChannel Operation Functions */
/** \brief dbGet() through a dbChannel.
*
* Calls dbGet() for the field that \p chan refers to.
* Only call this routine if the record is already locked.
* \param[in] chan Pointer to the dbChannel object.
* \param[in] type Request type from dbFldTypes.h.
* \param[out] pbuffer Pointer to data buffer.
* \param[in,out] options Request options from dbAccessDefs.h.
* \param[in,out] nRequest Pointer to the element count.
* \param[in] pfl Pointer to a db_field_log or NULL.
* \returns 0, or an error status value.
*/
DBCORE_API long dbChannelGet(dbChannel *chan, short type,
void *pbuffer, long *options, long *nRequest, void *pfl);
/** \brief dbGetField() through a dbChannel.
*
* Get values from a PV through a channel.
* This routine locks the record, calls
* dbChannelGet(), then unlocks the record again.
* \param[in] chan Pointer to the dbChannel object.
* \param[in] type Request type from dbFldTypes.h.
* \param[out] pbuffer Pointer to data buffer.
* \param[in,out] options Request options from dbAccessDefs.h.
* \param[in,out] nRequest Pointer to the element count.
* \param[in] pfl Pointer to a db_field_log or NULL.
* \returns 0, or an error status value.
*
* \since UNRELEASED If pfl is NULL and chan has filters, db_create_read_log() will be called
* internally to create a temporary db_field_log which is passed to dbChannelGet()
* then deallocated.
*/
DBCORE_API long dbChannelGetField(dbChannel *chan, short type,
void *pbuffer, long *options, long *nRequest, void *pfl);
/** \brief dbPut() through a dbChannel.
*
* Put values to a PV through a channel. Only call this routine if the
* record is already locked.
* Calls dbPut() for the field that \p chan refers to.
* \param chan[in] Pointer to the dbChannel object.
* \param type[in] Request type from dbFldTypes.h.
* \param pbuffer[in] Pointer to data buffer.
* \param nRequest[in] Number of elements in pbuffer.
* \returns 0, or an error status value.
*/
DBCORE_API long dbChannelPut(dbChannel *chan, short type,
const void *pbuffer, long nRequest);
/** \brief dbPutField() through a dbChannel.
*
* Put values to a PV through a channel.
* This routine calls dbPutField() for the field that \p chan refers to.
* \param chan[in] Pointer to the dbChannel object.
* \param type[in] Request type from dbFldTypes.h.
* \param pbuffer[in] Pointer to data buffer.
* \param nRequest[in] Number of elements in pbuffer.
* \returns 0, or an error status value.
*/
DBCORE_API long dbChannelPutField(dbChannel *chan, short type,
const void *pbuffer, long nRequest);
/** \brief Print report on a channel.
*
* Print information about the channel to stdout.
* \param chan Pointer to the dbChannel object.
* \param level Higher levels may provide more detail.
* \param indent Indent all lines by this many spaces.
*/
DBCORE_API void dbChannelShow(dbChannel *chan, int level,
const unsigned short indent);
/** \brief Print report on a channel's filters.
*
* Print information about the channel's filters to stdout.
* \param chan Pointer to the dbChannel object.
* \param level Higher levels may provide more detail.
* \param indent Indent all lines by this many spaces.
*/
DBCORE_API void dbChannelFilterShow(dbChannel *chan, int level,
const unsigned short indent);
/** \brief Delete a channel.
*
* Releases resources owned by this channel and its filters.
* \param chan Pointer to the dbChannel object.
*/
DBCORE_API void dbChannelDelete(dbChannel *chan);
/** \name Other routines */
DBCORE_API void dbRegisterFilter(const char *key,
const chFilterIf *fif, void *puser);
DBCORE_API db_field_log* dbChannelRunPreChain(dbChannel *chan,
db_field_log *pLogIn);
DBCORE_API db_field_log* dbChannelRunPostChain(dbChannel *chan,
db_field_log *pLogIn);
DBCORE_API const chFilterPlugin * dbFindFilter(const char *key, size_t len);
DBCORE_API void dbChannelGetArrayInfo(dbChannel *chan,
void **pfield, long *no_elements, long *offset);
#ifdef __cplusplus
}
#endif
#endif /* INC_dbChannel_H */