Files
epics-base/src/libCom/arAccessLib.c
1994-07-13 02:29:43 +00:00

2916 lines
102 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* $Id$
* Author: Roger A. Cole
* Date: 03-09-90
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1990-92, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 03-09-90 rac initial version
* .02 07-31-91 rac installed in SCCS
* .03 09-18-91 rac change arg for arCFChanRead to int--short
* causes problems on Sun4; other minor fixes
* .04 04-04-92 rac add arCFChanWrite_args; handle flags for begin
* and end of snapshot
* .05 09-14-92 rac remove use of special malloc and free routines
* .06 09-30-92 rac ignore time running backward
*
* make options
* -DNDEBUG don't compile assert() checking
* -DDEBUG compile various debug code
*/
/*+/mod***********************************************************************
* TITLE arAccessLib.c - AR access library for AR data set files
*
* DESCRIPTION
*
* QUICK REFERENCE
* AR_CHAN_DESC *pCFDesc; pointer to 'AR channel file' descriptor
*
* #include <genDefs.h> some general use definitions
* #include <tsDefs.h> definitions for time stamps
* #include <cadef.h> definitions for Channel Access
* #include <db_access.h> definitions for database related items
* #include <arAccessLib.h> structures for AR access library routines
* #include <sys/file.h> obtain mode definitions for SunOS
*
* AR_CHAN_DESC *pChanDesc; pointer to channel descriptor in chan file
* AR_CF_DESC *pCfDesc; pointer to channel file descriptor
* chtype type; one of the DBR_xxx types
* TS_STAMP *pStamp; pointer to a time stamp structure
* int mode; mode to open file or channel--O_RDONLY
* or O_RDWR
*
* long arCFChanClose( >ppChanDesc )
* long arCFChanDel( >ppChanDesc )
* long arCFChanFind( pCfDesc, chanName )
* AR_CHAN_DESC *arCFChanOpen( pCfDesc, chanName, mode )
* long arCFChanPosition( pChanDesc, pStamp )
* long arCFChanPurge( pChanDesc, pStamp )
* long arCFChanRead( pChanDesc, type, >pDbrBuf, count)
* long arCFChanWrite( pChanDesc, pEvHandArg )
* long arCFChanWrite_args( pChanDesc, stamp, dbfType,
* count, pData, alStat, alSevr)
* long arCFChanWriteByte( pArChanDesc, value )
* long arCFChanWriteGR( pChanDesc, pEvHandArg )
* long arCFClose( >ppCfDesc )
* AR_CF_DESC *arCFCreate( name, maxBytes )
* AR_CF_DESC *arCFOpen( name, mode )
* long arCFSyncRead( pCfDesc )
* long arCFSyncWrite( pCfDesc )
*
* int ArCFChanElementCount( pChanDesc )
* chtype ArCFChanFieldType( pChanDesc )
* char *ArCFChanName( pChanDesc )
* int ArCFChanNStates( pChanDesc )
* int ArCFChanPrec( pChanDesc )
* char *ArCFChanStates( pChanDesc )
* char *ArCFChanUnits( pChanDesc )
*
*-***************************************************************************/
#include <arAccessLib.h>
#ifndef INC_genDefs_h
# include <genDefs.h>
#endif
#ifndef INC_tsDefs_h
# include <tsDefs.h>
#endif
#ifndef INC_arCS_h
# include <arCS.h>
#endif
#ifndef INCLcadefh
# include <cadef.h>
#endif
#ifndef INCLdb_accessh
# include <db_access.h>
#endif
#include <stdio.h>
#include <sys/file.h> /* for O_RDWR and O_RDONLY definitions */
#include <strings.h>
/*+/internal------------------------------------------------------------------
* NAME arCF_malloc/arCF_free
*
* malloc and free routines for arCF routines to use for the various kinds
* of data structures. Should the need arise, these routines will
* be enhanced to maintain lists of free data structures of the various
* kinds, to eliminate malloc()/free() overhead and memory fragmentation.
*
* The following routines are used for calling malloc() and free() for
* some common data structures in arAccessLib. They have two versions,
* depending on whether DEBUG is defined. Both versions perform the
* described function. The DEBUG version provides the capability for
* `bounds checking' on allocated buffers, and also for filling buffers
* with a known pattern for use in `un-initialized buffer' bug detection.
*
* AR_CF_DESC *arCF_mallocCF_DESC() void arCF_freeCF_DESC(pCFDesc)
* AR_CHAN_DESC *arCF_mallocCHAN_DESC() void arCF_freeCHAN_DESC(pChanDesc)
*---------------------------------------------------------------------------*/
/* Forward declarations */
long arCFChanReadTs();
long arCFReadMIBuffers();
AR_CHAN_DESC *
arCF_mallocCHAN_DESC()
{
#ifdef DEBUG
return (AR_CHAN_DESC *)arMalloc(sizeof(AR_CHAN_DESC), "AR_CHAN_DESC");
#else
return (AR_CHAN_DESC *)malloc(sizeof(AR_CHAN_DESC));
#endif
}
void
arCF_freeCHAN_DESC(ptr)
AR_CHAN_DESC *ptr;
{
#ifdef DEBUG
arFree(ptr, sizeof(AR_CHAN_DESC), "AR_CHAN_DESC");
#else
free(ptr);
#endif
}
AR_CF_DESC *
arCF_mallocCF_DESC()
{
#ifdef DEBUG
return (AR_CF_DESC *)arMalloc(sizeof(AR_CF_DESC), "AR_CF_DESC");
#else
return (AR_CF_DESC *)malloc(sizeof(AR_CF_DESC));
#endif
}
void
arCF_freeCF_DESC(ptr)
AR_CF_DESC *ptr;
{
#ifdef DEBUG
arFree(ptr, sizeof(AR_CF_DESC), "AR_CF_DESC");
#else
free(ptr);
#endif
}
/*+/macro*********************************************************************
* NAME ArCFChanXxx - convenience macros for channel files
*
* DESCRIPTION
* These macros provide easy access to information about a channel in
* a channel file. The macros are defined in arAccessLib.h
*
* int ArCFChanElementCount( pArChanDesc )
* chtype ArCFChanFieldType( pArChanDesc )
* char *ArCFChanName( pArChanDesc )
*
* The following marcos for obtaining graphics information bypasses
* the need to do a read with DBR_GR_xxx, and thus simplifies
* programming. However, programs which must run with both
* Channel Access and the AR access library should probably use a
* read to obtain the information.
*
* int ArCFChanNStates( pArChanDesc )
* int ArCFChanPrec( pArChanDesc )
* char *ArCFChanStates( pArChanDesc )
* char *ArCFChanUnits( pArChanDesc )
*
* RETURNS
* as indicated
*
* NOTES
* 1. These macros assume, but do not check, that the channel descriptor
* is open.
* 2. The macros for obtaining graphics information assume, but do not
* check, that the macro being used is appropriate for the native
* type of the channel.
*
*-*/
/*+/subr**********************************************************************
* NAME arCFChanClose - close a channel of an AR channel data file
*
* DESCRIPTION
* Close a channel descriptor. If any I/O is outstanding for the
* descriptor, that I/O is forced to completion; this may include
* writing data and index blocks to disk and updating oldestStamp and
* newestStamp in the chanHdr. Memory buffers for data and index blocks
* are free()'d.
*
* The count of channel descriptors open to the channel is decremented.
*
* The channel descriptor is removed from the channel descriptor list
* in the channel file descriptor. The channel descriptor is free()'d
* and NULL is stored in caller's pointer to prevent further use
*
* RETURNS
* OK
*
* BUGS
* o doesn't flush the MI buffer
* o primitive error handling
*
* SEE ALSO
* arCFChanOpen(), arCFChanDel()
*
*-*/
long
arCFChanClose(ppArChanDesc)
AR_CHAN_DESC **ppArChanDesc; /* IO ptr to ptr to channel descriptor;
will be NULL at completion */
{
int stat; /* status from calls */
int retStat=OK; /* return status to caller */
AR_CHAN_DESC *pArChanDesc; /* pointer to channel descriptor */
AR_CHAN_HDR *pChanHdr; /* pointer to channel header */
assert(*ppArChanDesc != NULL);
pArChanDesc = *ppArChanDesc;
assert(pArChanDesc != NULL);
assert(pArChanDesc->pArCfDesc != NULL);
assert(pArChanDesc->pMIBuf != NULL);
pChanHdr = &ArCDChanHdr(pArChanDesc);
assert(pChanHdr->count > 0);
/*----------------------------------------------------------------------------
* flush data and index buffers and free() them.
* if the MI for this channel has been altered, write it
*---------------------------------------------------------------------------*/
if (pArChanDesc->pDataBuf != NULL) {
stat = arCF_DFlushAndFree(pArChanDesc);
assert(stat == OK);
}
if (pArChanDesc->pIndexBuf != NULL) {
stat = arCF_IFlushAndFree(pArChanDesc);
assert(stat == OK);
}
if (pArChanDesc->pMIBuf != NULL) {
stat = arCF_MIFlush(pArChanDesc->pArCfDesc, pArChanDesc->pMIBuf);
assert(stat == OK);
}
/*----------------------------------------------------------------------------
* remove this channel descriptor from the list of channel descriptors.
*---------------------------------------------------------------------------*/
if (pArChanDesc->pNextDesc != NULL)
pArChanDesc->pNextDesc->pPrevDesc = pArChanDesc->pPrevDesc;
else
pArChanDesc->pArCfDesc->pChanDescTail = pArChanDesc->pPrevDesc;
if (pArChanDesc->pPrevDesc != NULL)
pArChanDesc->pPrevDesc->pNextDesc = pArChanDesc->pNextDesc;
else
pArChanDesc->pArCfDesc->pChanDescHead = pArChanDesc->pNextDesc;
/*----------------------------------------------------------------------------
* decrement count of descriptors open to this chanHdr
* if this descriptor had the chanHdr open in O_RDWR mode, reset the
* flag in the chanHdr
*---------------------------------------------------------------------------*/
pChanHdr->count -= 1;
if ((pArChanDesc->flags & AR_CDESC_WRITE) != 0) {
assert((pChanHdr->flags & AR_CHDR_WRITE) != 0);
pChanHdr->flags ^= AR_CHDR_WRITE;
}
/*----------------------------------------------------------------------------
* free the descriptor and put NULL in the caller's pointer, to prevent
* further use of the descriptor
*---------------------------------------------------------------------------*/
arCF_freeCHAN_DESC(pArChanDesc);
*ppArChanDesc = NULL;
return retStat;
}
/*+/subr**********************************************************************
* NAME arCFChanDel - delete a channel of an AR channel data file
*
* DESCRIPTION
* Deletes all data and index buffers for the channel, deletes the
* chanHdr for the channel, and closes the channel descriptor. If the
* delete is successful, then the channel descriptor has been changed
* to NULL, preventing further use; memory associated with data buffers,
* index buffers, and the channel descriptor has been free()'d. Disk
* blocks which are deleted from the channel are added to the channel
* file's free block list; the chanHdr is added to the free chanHdr list.
*
* The channel descriptor must be open O_RDWR, and there must be no
* other channel descriptors open to the channel.
*
* RETURNS
* OK, or
* ERROR
*
* BUGS
* o text
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
arCFChanDel(ppArChanDesc)
AR_CHAN_DESC **ppArChanDesc; /* IO ptr to ptr to channel descriptor */
{
int stat; /* status from calls */
int retStat=OK; /* return status to caller */
AR_CHAN_DESC *pArChanDesc; /* pointer to channel descriptor */
AR_CHAN_HDR *pChanHdr; /* pointer to channel header */
assert(*ppArChanDesc != NULL);
pArChanDesc = *ppArChanDesc;
assert(pArChanDesc != NULL);
assert(pArChanDesc->pArCfDesc != NULL);
assert(pArChanDesc->pMIBuf != NULL);
pChanHdr = &ArCDChanHdr(pArChanDesc);
assert(pChanHdr->count > 0);
if ((pArChanDesc->flags & AR_CDESC_WRITE) == 0) {
(void)fprintf(stderr, "arCFChanDel: %s not open O_RDWR\n",
ArCDChanHdr(pArChanDesc).name);
retStat = ERROR;
}
if (pChanHdr->count > 1) {
(void)fprintf(stderr, "arCFChanDel: others have %s open\n",
ArCDChanHdr(pArChanDesc).name);
retStat = ERROR;
}
/*----------------------------------------------------------------------------
* free all the data and index buffers for this channel
*---------------------------------------------------------------------------*/
if (retStat == OK) {
if ((stat = arCFChanPurge(pArChanDesc, (TS_STAMP *)NULL)) != OK) {
(void)fprintf(stderr,
"arCFChanDel: error deleting data and/or index buffers for %s\n",
ArCDChanHdr(pArChanDesc).name);
retStat = ERROR;
}
}
/*----------------------------------------------------------------------------
* remove this channel descriptor from the list of channel descriptors.
*---------------------------------------------------------------------------*/
if (retStat == OK) {
if (pArChanDesc->pNextDesc != NULL)
pArChanDesc->pNextDesc->pPrevDesc = pArChanDesc->pPrevDesc;
else
pArChanDesc->pArCfDesc->pChanDescTail = pArChanDesc->pPrevDesc;
if (pArChanDesc->pPrevDesc != NULL)
pArChanDesc->pPrevDesc->pNextDesc = pArChanDesc->pNextDesc;
else
pArChanDesc->pArCfDesc->pChanDescHead = pArChanDesc->pNextDesc;
/*----------------------------------------------------------------------------
* move the chanHdr to the free chanHdr list and mark the MI block
* as MODIFIED
*---------------------------------------------------------------------------*/
stat = arCF_MIDelChan(pArChanDesc->pArCfDesc,
pArChanDesc->pMIBuf, pArChanDesc->hdrNum);
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pMIBuf);
/*----------------------------------------------------------------------------
* free the descriptor and put NULL in the caller's pointer, to prevent
* further use of the descriptor
*---------------------------------------------------------------------------*/
arCF_freeCHAN_DESC(pArChanDesc);
*ppArChanDesc = NULL;
}
return retStat;
}
/*+/subr**********************************************************************
* NAME arCFChanFind - find a channel in an AR channel data file
*
* DESCRIPTION
* Find a channel in a channel data file. This routine is intended
* to be used in cases where it is necessary to know prior to doing
* an arCFChanOpen() whether the file contains a particular channel.
*
* RETURNS
* OK if channel is found, or
* ERROR
*
* BUGS
* o text
*
* EXAMPLE
*
*-*/
long
arCFChanFind(pArCfDesc, chanName)
AR_CF_DESC *pArCfDesc; /* I pointer to channel file descriptor */
char *chanName; /* I channel name */
{
AR_MI_BUF *pMIBuf; /* ptr to MI block containing chanHdr */
int indx; /* index in MI block of chanHdr */
int l; /* length of chanName */
char name[AR_NAME_DIM]; /* buffer for appending .VAL */
assert(pArCfDesc != NULL);
l = strlen(chanName);
if (strchr(chanName, '.') == 0) {
if (l <= 0 || l >= AR_NAME_DIM-5) {
(void)fprintf(stderr, "arCFChanFind: chanName illegal length\n");
return ERROR;
}
(void)strcpy(name, chanName);
(void)strcat(name, ".VAL");
chanName = name;
}
else {
if (l <= 0 || l >= AR_NAME_DIM) {
(void)fprintf(stderr, "arCFChanFind: chanName illegal length\n");
return ERROR;
}
}
return arCF_MIFindChan(pArCfDesc, chanName, &pMIBuf, &indx);
}
/*+/subr**********************************************************************
* NAME arCFChanOpen - open a channel to an AR channel data file
*
* DESCRIPTION
* Open a channel in a channel data file. If the open is successful,
* a channel descriptor is returned. If the channel doesn't exist,
* one is created if mode is O_RDWR; in this case, the .flags item in
* the channel descriptor will have AR_CDESC_NEW set.
*
* When the open is complete, the relevent index and data blocks will
* be in memory. If the mode is O_RDWR, the position will be at the
* end of the channel's data, ready for writing; for O_RDONLY, at the
* start of the channel's data, ready for reading.
*
* If the channel descriptor isn't closed before the channel data file
* is closed, it will be closed automatically, with updated blocks
* written to the channel data file.
*
* If O_RDWR is specified, the channel data file must have been opened
* with O_RDWR and there must be no other channel descriptor open in
* O_RDWR mode to the desired channel.
*
* The count of channel descriptors open to the desired channel is
* incremented.
*
* RETURNS
* pointer to channel descriptor, or
* NULL
*
* BUGS
* o no exit handler (this will probably be covered by the exit handler
* for the file, which isn't presently done)
* o for reading, the possibility of opening to a position (date/time)
* would potentially avoid wasting a disk read
*
* SEE ALSO
* arCFChanClose(), arCFOpen(), arCFClose()
*
* EXAMPLE
*
*-*/
AR_CHAN_DESC *
arCFChanOpen(pArCfDesc, chanName, mode)
AR_CF_DESC *pArCfDesc; /* IO pointer to channel file descriptor */
char *chanName; /* I channel name */
int mode; /* I mode for open--O_RDONLY or O_RDWR; if mode is
O_RDWR and the channel doesn't exist, it
is created */
{
int stat; /* status return from calls */
int retStat=OK; /* cumulative status in this routine */
AR_MI_BUF *pMIBuf; /* ptr to MI block containing chanHdr */
int indx; /* index in MI block of chanHdr */
AR_CHAN_HDR *pChanHdr; /* pointer to chanHdr for this channel */
AR_CHAN_DESC *pChanDesc=NULL;/* pointer to channel descriptor */
int l; /* length of chanName */
char name[AR_NAME_DIM]; /* buffer for appending .VAL */
assert(pArCfDesc != NULL);
assert(mode == O_RDONLY || mode == O_RDWR);
/*----------------------------------------------------------------------------
* check chanName to see if it specifies a .field ; if it doesn't, copy
* chanName to 'name' and append .VAL , then change chanName to point to
* 'name'
*---------------------------------------------------------------------------*/
l = strlen(chanName);
if (strchr(chanName, '.') == 0) {
if (l <= 0 || l >= AR_NAME_DIM-5) {
(void)fprintf(stderr, "arCFChanOpen: chanName illegal length\n");
retStat = ERROR;
}
else {
(void)strcpy(name, chanName);
(void)strcat(name, ".VAL");
chanName = name;
}
}
else {
if (l <= 0 || l >= AR_NAME_DIM) {
(void)fprintf(stderr, "arCFChanOpen: chanName illegal length\n");
retStat = ERROR;
}
}
/*----------------------------------------------------------------------------
* make sure file is O_RDWR if ChanOpen request is O_RDWR
*---------------------------------------------------------------------------*/
if ( (mode == O_RDWR) &&
((pArCfDesc->pBfDesc->flags & BF_DESC_WRITE) == 0) ) {
(void)fprintf(stderr,
"arCFChanOpen: can't open %s O_RDWR--file not O_RDWR\n",
chanName);
retStat = ERROR;
}
/*----------------------------------------------------------------------------
* allocate a channel descriptor and clear the 'flags' item. If the open
* is with O_RDWR, set the AR_CDESC_WRITE bit in the 'flags' item.
*---------------------------------------------------------------------------*/
if (retStat == OK) {
if ((pChanDesc = arCF_mallocCHAN_DESC()) == NULL) {
(void)fprintf(stderr,
"arCFChanOpen: can't malloc for chan desc for %s\n",
chanName);
retStat = ERROR;
}
else {
pChanDesc->flags = 0;
if (mode == O_RDWR)
pChanDesc->flags |= AR_CDESC_WRITE;
}
}
/*----------------------------------------------------------------------------
* try to locate the chanHdr for the channel. If no chanHdr is found,
* create one, providing that mode is O_RDWR; if mode is O_RDONLY, then
* not finding a chanHdr is an error. If a new chanHdr is created, then
* set AR_CDESC_NEW in the channel descriptor.
*---------------------------------------------------------------------------*/
if (retStat == OK) {
stat = arCF_MIFindChan(pArCfDesc, chanName, &pMIBuf, &indx);
if (stat != OK) {
if ((pChanDesc->flags & AR_CDESC_WRITE) != 0) {
stat = arCF_MIAddChan(pArCfDesc, chanName, &pMIBuf, &indx);
if (stat == OK)
pChanDesc->flags |= AR_CDESC_NEW;
else {
(void)fprintf(stderr,
"arCFChanOpen: error adding chanHdr for %s\n",
chanName);
retStat = ERROR;
}
}
else {
(void)fprintf(stderr, "arCFChanOpen: %s not found\n", chanName);
retStat = ERROR;
}
}
}
if (retStat == OK) {
/*----------------------------------------------------------------------------
* compute the actual address of the chanHdr, for easier manipulation. If
* the chanHdr is already open in O_RDWR mode, and if this open is for
* O_RDWR, an error exists. If this is the only request for O_RDWR, flag
* AR_CHDR_WRITE in the chanHdr to disallow other writers. If all is well,
* increment in the chanHdr the count of descriptors open to it.
*---------------------------------------------------------------------------*/
assert(pMIBuf != NULL);
pChanHdr = &pMIBuf->chanHdr[indx];
if ((pChanDesc->flags & AR_CDESC_WRITE) != 0) {
if ((pChanHdr->flags & AR_CHDR_WRITE) != 0) {
(void)fprintf(stderr,
"arCFChanOpen: %s already open for writing\n",
chanName);
retStat = ERROR;
}
else {
pChanHdr->flags |= AR_CHDR_WRITE;
pChanHdr->count += 1;
}
}
else
pChanHdr->count += 1;
}
if (retStat == OK) {
/*----------------------------------------------------------------------------
* the channel file descriptor has a list of open channel descriptors;
* link this channel descriptor onto the end of that list (which is
* a doubly linked list).
*---------------------------------------------------------------------------*/
pChanDesc->pNextDesc = NULL;
pChanDesc->pPrevDesc = pArCfDesc->pChanDescTail;
if (pArCfDesc->pChanDescHead != NULL) {
assert(pArCfDesc->pChanDescTail != NULL);
pArCfDesc->pChanDescTail->pNextDesc = pChanDesc;
}
else {
assert(pArCfDesc->pChanDescTail == NULL);
pArCfDesc->pChanDescHead = pChanDesc;
}
pArCfDesc->pChanDescTail = pChanDesc;
/*----------------------------------------------------------------------------
* set up the channel descriptor. Part of this setup includes getting
* an index buffer and a data buffer. If the open is O_RDONLY, then
* the position will be at the first index block and first data block;
* for O_RDWR, the last index block and last data block will be used.
*---------------------------------------------------------------------------*/
pChanDesc->pArCfDesc = pArCfDesc;
pChanDesc->pMIBuf = pMIBuf;
pChanDesc->hdrNum = indx;
pChanDesc->pIndexBuf = NULL;
pChanDesc->pDataBuf = NULL;
pChanDesc->pData = NULL;
pChanDesc->remainCount = -10;
pChanDesc->datInfNum = -10;
pChanDesc->timeStamp.secPastEpoch = 0;
pChanDesc->timeStamp.nsec = 0;
stat = arCF_DNextBlock(pChanDesc);
if (stat == ERROR && (pChanDesc->flags & AR_CDESC_EOF) == 0)
assertAlways(0);
}
else {
if (pChanDesc != NULL) {
arCF_freeCHAN_DESC(pChanDesc);
pChanDesc = NULL;
}
}
return pChanDesc;
}
/*+/subr**********************************************************************
* NAME arCFChanPosition - position channel decscriptor based on time stamp
*
* DESCRIPTION
* Position a channel descriptor so it is ready to read the data with
* the specified time stamp. If no data with the specified time stamp
* is available, the positioning is for the first data following.
*
* End of file results in an error return, with the AR_CDESC_EOF flag
* set in chanDesc.flags .
*
* RETURNS
* OK, or
* ERROR
*
* SEE ALSO
* arCFChanRead()
*
* EXAMPLE
*
*-*/
long
arCFChanPosition(pArChanDesc, pPosStamp)
AR_CHAN_DESC *pArChanDesc;/* IO pointer to channel descriptor */
TS_STAMP *pPosStamp; /* I stamp for desired data; NULL for `rewind' */
{
int stat; /* status from calls */
int retStat=OK; /* return status to caller */
AR_CHAN_HDR *pChanHdr; /* pointer to chanHdr */
AR_INDEX_BUF *pIndexBuf; /* pointer to buffer for index buffer */
AR_DATA_BUF *pDataBuf; /* pointer to buffer for data buffer */
BF_BLKNUM indexBlock; /* block # for index block */
BF_BLKNUM nextIndexBlock; /* block # for next index block */
BF_BLKNUM dataBlock; /* block # for data block */
int firstDatInf; /* first datInfo in index block */
int curDatInf; /* current datInfo in index block */
int lastDatInf; /* last datInfo in index block */
int i; /* temp for loops */
chtype type;
assert(pArChanDesc != NULL);
assert(pArChanDesc->pArCfDesc != NULL);
pChanHdr = &ArCDChanHdr(pArChanDesc);
assert(pChanHdr != NULL);
if (pChanHdr->newestSecPastEpoch == 0) {
pArChanDesc->flags |= AR_CDESC_EOF;
return ERROR;
}
else if (pPosStamp == NULL ||
pPosStamp->secPastEpoch < pChanHdr->oldestSecPastEpoch) {
/*----------------------------------------------------------------------------
* rewind
* either a rewind was requested, or else the requested stamp is prior
* to the oldest data for the file
*----------------------------------------------------------------------------*/
pArChanDesc->datInfNum = -10;
pArChanDesc->remainCount = -10;
stat = arCF_DNextBlock(pArChanDesc);
if (stat == ERROR && (pArChanDesc->flags & AR_CDESC_EOF) == 0)
assertAlways(0);
return stat;
}
/*----------------------------------------------------------------------------
* search is required
* It isn't known yet whether the search will be forward or reverse.
* If the desired stamp is less than that of the first datInf,
* then a backward search is required. If the desired stamp
* is greater than that of the first datInf, then a forward
* search (possibly confined to this index block) is required.
*
* A special case occurs when the present index block contains no
* datInf's with time stamps. In this case, a backward search is
* done. (This case arises because data blocks which contain only
* data items don't have a time stamp in their datInf entry.)
*----------------------------------------------------------------------------*/
pIndexBuf = pArChanDesc->pIndexBuf;
lastDatInf = (pIndexBuf->bfInfo.lastByte - BF_BLOCK_DATA) /
sizeof(AR_DAT_INFO);
while (lastDatInf > 0 &&
pIndexBuf->datInfo[lastDatInf].stamp.secPastEpoch == 0) {
lastDatInf--;
}
firstDatInf = (pIndexBuf->bfInfo.firstByte - BF_BLOCK_DATA) /
sizeof(AR_DAT_INFO);
while (firstDatInf < lastDatInf &&
pIndexBuf->datInfo[firstDatInf].stamp.secPastEpoch == 0) {
firstDatInf++;
}
if (firstDatInf > lastDatInf ||
TsCmpStampsLT(pPosStamp, &pIndexBuf->datInfo[firstDatInf].stamp)) {
/*----------------------------------------------------------------------------
* reverse search
* Since the file contains extremely little information to allow
* `going backward', a reverse search is accomplished with a rewind
* followed by a forward search.
*----------------------------------------------------------------------------*/
pArChanDesc->datInfNum = -10;
pArChanDesc->remainCount = -10;
stat = arCF_INextDatInfo(pArChanDesc);
if (stat == ERROR && (pArChanDesc->flags & AR_CDESC_EOF) == 0)
assertAlways(0);
if (stat == ERROR)
return stat;
}
pIndexBuf = pArChanDesc->pIndexBuf;
lastDatInf = (pIndexBuf->bfInfo.lastByte - BF_BLOCK_DATA) /
sizeof(AR_DAT_INFO);
while (lastDatInf > 0 &&
pIndexBuf->datInfo[lastDatInf].stamp.secPastEpoch == 0) {
lastDatInf--;
}
/*----------------------------------------------------------------------------
* forward search
*----------------------------------------------------------------------------*/
while (lastDatInf >= 0 &&
TsCmpStampsGT(pPosStamp, &pIndexBuf->datInfo[lastDatInf].stamp)) {
/*----------------------------------------------------------------------------
* desired position may be in this index block, or it may be in a
* following one. In order to find out, we'll have to read the next
* one and do an explicit check.
*----------------------------------------------------------------------------*/
indexBlock = pIndexBuf->blkNum;
if ((nextIndexBlock = pIndexBuf->bfInfo.flink) != 0) {
stat = arCF_IRead(pArChanDesc, pIndexBuf, nextIndexBlock);
if (stat == OK) {
lastDatInf = (pIndexBuf->bfInfo.lastByte - BF_BLOCK_DATA) /
sizeof(AR_DAT_INFO);
while (lastDatInf > 0 &&
pIndexBuf->datInfo[lastDatInf].stamp.secPastEpoch == 0) {
lastDatInf--;
}
firstDatInf = (pIndexBuf->bfInfo.firstByte - BF_BLOCK_DATA) /
sizeof(AR_DAT_INFO);
while (firstDatInf < lastDatInf &&
pIndexBuf->datInfo[firstDatInf].stamp.secPastEpoch == 0) {
firstDatInf++;
}
if (firstDatInf > lastDatInf ||
TsCmpStampsLE(pPosStamp,
&pIndexBuf->datInfo[firstDatInf].stamp)) {
/* woops!!! time to back up! */
stat = arCF_IRead(pArChanDesc, pIndexBuf, indexBlock);
if (stat != OK)
assertAlways(0);
else
break;
}
}
else {
/* error reading next block; back up and let normal code
handle the error */
stat = arCF_IRead(pArChanDesc, pIndexBuf, indexBlock);
if (stat != OK)
assertAlways(0);
else
break;
}
}
else
break;
}
/*----------------------------------------------------------------------------
* desired position may be somewhere in this index block; this is the
* index block where we'll start narrowing down the search.
*
* First, the datInfo whose stamp is greater than or equal to the
* desired stamp will be found. Then reading will start with the
* preceding datInfo until a stamp equal to or greater than the
* desired stamp is found.
*----------------------------------------------------------------------------*/
indexBlock = pIndexBuf->blkNum;
lastDatInf = (pIndexBuf->bfInfo.lastByte - BF_BLOCK_DATA) /
sizeof(AR_DAT_INFO);
while (lastDatInf > 0 &&
pIndexBuf->datInfo[lastDatInf].stamp.secPastEpoch == 0) {
lastDatInf--;
}
assert(lastDatInf >= 0);
firstDatInf = (pIndexBuf->bfInfo.firstByte - BF_BLOCK_DATA) /
sizeof(AR_DAT_INFO);
while (firstDatInf < lastDatInf &&
pIndexBuf->datInfo[firstDatInf].stamp.secPastEpoch == 0) {
firstDatInf++;
}
assert(firstDatInf >= 0);
i = curDatInf = firstDatInf;
while (i < lastDatInf) {
if (pIndexBuf->datInfo[i+1].stamp.secPastEpoch == 0)
i++;
else if (TsCmpStampsGE(&pIndexBuf->datInfo[i+1].stamp, pPosStamp))
break;
else {
i++;
curDatInf = i;
}
}
pArChanDesc->datInfNum = curDatInf;
pArChanDesc->timeStamp= pIndexBuf->datInfo[curDatInf].stamp;
pArChanDesc->remainCount = -10;
stat = arCF_DRead(pArChanDesc, pArChanDesc->pDataBuf,
pIndexBuf->datInfo[curDatInf].dataBlock);
if (stat != OK)
assertAlways(0);
while (1) {
stat = arCFChanReadTs(pArChanDesc);
if (stat != OK) {
if ((pArChanDesc->flags &
(AR_CDESC_BEGIN|AR_CDESC_INACT|
AR_CDESC_SNAP_BEGIN|AR_CDESC_SNAP_END)) == 0) {
retStat = ERROR;
break;
}
}
else if (TsCmpStampsGE(&pArChanDesc->timeStamp, pPosStamp))
break;
else {
type = dbf_type_to_DBR(ArCFChanFieldType(pArChanDesc));
stat = arCFChanRead(pArChanDesc, type, (void *)NULL, 0);
if (stat != OK) {
retStat = ERROR;
break;
}
}
}
return retStat;
}
/*+/subr**********************************************************************
* NAME arCFChanPurge - purge data for a channel of an AR channel data file
*
* DESCRIPTION
*
* RETURNS
*
* BUGS
* o doesn't update oldestSecPastEpoch and newestSecPastEpoch
* o doesn't initialize the read/write items in the chanDesc
* o always does TOTAL purge, ignoring requested time stamp
* o there are some critical sections unprotected
* - between malloc() and storing pointer in descriptor
* - between releasing blocks and updating pointers and links
* o error handling is shakey, at best
*
* SEE ALSO
* arCFChanDel()
*
* EXAMPLE
*
*-*/
long
arCFChanPurge(pArChanDesc, pKeepStamp)
AR_CHAN_DESC *pArChanDesc;/* IO pointer to channel descriptor */
TS_STAMP *pKeepStamp; /* I stamp for oldest data to keep; NULL says
delete all data */
{
int stat; /* status from calls */
int retStat=OK; /* return status to caller */
AR_CHAN_HDR *pChanHdr; /* pointer to chanHdr */
BF_DESC *pBfDesc; /* pointer to block file descriptor */
AR_INDEX_BUF *pIndexBuf; /* pointer to buffer for index buffer */
AR_DATA_BUF *pDataBuf; /* pointer to buffer for data buffer */
assert(pArChanDesc != NULL);
assert(pArChanDesc->pArCfDesc != NULL);
pBfDesc = pArChanDesc->pArCfDesc->pBfDesc;
assert(pBfDesc != NULL);
assert(pArChanDesc->pMIBuf != NULL);
pChanHdr = &ArCDChanHdr(pArChanDesc);
assert(pChanHdr->count > 0);
if ((pArChanDesc->flags & AR_CDESC_WRITE) == 0) {
(void)fprintf(stderr, "arCFChanPurge: %s not open O_RDWR\n",
ArCDChanHdr(pArChanDesc).name);
retStat = ERROR;
}
if (pChanHdr->count > 1) {
(void)fprintf(stderr, "arCFChanPurge: others have %s open\n",
ArCDChanHdr(pArChanDesc).name);
retStat = ERROR;
}
/*----------------------------------------------------------------------------
* o if a read block is in memory, free it
* o if a write block is in memory, ????
* o get buffers for index and data blocks, and put their pointers in
* the channel descriptor
* o traverse the index buffer chain, and all the data buffers, freeing
* the appropriate buffers
*---------------------------------------------------------------------------*/
if (retStat == OK) {
if ((pIndexBuf = arCF_mallocINDEX_BUF()) == NULL) {
(void)fprintf(stderr, "arCFChanPurge: can't malloc to purge %s\n",
ArCDChanHdr(pArChanDesc).name);
retStat = ERROR;
}
else
pArChanDesc->pIndexBuf = pIndexBuf;
if (retStat == OK) {
if ((pDataBuf = arCF_mallocDATA_BUF()) == NULL) {
(void)fprintf(stderr,
"arCFChanPurge: can't malloc to purge %s\n",
ArCDChanHdr(pArChanDesc).name);
retStat = ERROR;
}
else
pArChanDesc->pDataBuf = pDataBuf;
}
}
if (retStat == OK) {
BF_BLKNUM indexBlock; /* block # for index block */
BF_BLKNUM nextIndexBlock; /* block # for next index block */
BF_BLKNUM dataBlock; /* block # for data block */
int curDatInf; /* current datInfo in index block */
int lastDatInf; /* last datInfo in index block */
indexBlock = pChanHdr->indexHead;
while (retStat == OK && indexBlock != 0) {
stat = bfRead(pBfDesc, &pIndexBuf->bfInfo, indexBlock);
if (stat == OK) {
nextIndexBlock = pIndexBuf->bfInfo.flink;
pIndexBuf->blkNum = indexBlock;
ArCFModifyInit(pIndexBuf);
pIndexBuf->pBlock = &pIndexBuf->bfInfo;
}
else {
(void)fprintf(stderr,
"arCFChanPurge: bfRead error, indxBlk for %s\n",
ArCDChanHdr(pArChanDesc).name);
retStat = ERROR;
}
if (stat == OK && pIndexBuf->bfInfo.lastByte > BF_BLOCK_DATA) {
/*----------------------------------------------------------------------------
* march through all the datInfo entries in this index block. Use
* firstByte and lastByte to figure out how many datInfo's there are.
*
* for each datInfo entry,
* o read the block
* o release the block to the free block list
* o adjust firstByte in the index block to be past the datInfo just
* processed (this keeps firstByte valid)
*
* BUGS
* o this section of code should actually compare time stamps in datInfo
* with the caller supplied time stamp. If newestSecPastEpoch is newer
* than the caller's time stamp, delete the whole data block; otherwise,
* either the purge is complete, or only part of the data is to be
* purged (which means keeping the datInfo entry, adjusting its
* time stamp value, and adjusting firstByte in the data block). In
* addition, it must be taken into account that the stamps in some
* datInfo are zero--for the data blocks which don't have a time item.
*---------------------------------------------------------------------------*/
curDatInf = (pIndexBuf->bfInfo.firstByte - BF_BLOCK_DATA) /
sizeof(AR_DAT_INFO);
lastDatInf = (pIndexBuf->bfInfo.lastByte - BF_BLOCK_DATA) /
sizeof(AR_DAT_INFO);
while (curDatInf < lastDatInf && retStat == OK) {
dataBlock = pIndexBuf->datInfo[curDatInf].dataBlock;
stat = bfRead(pBfDesc, &pDataBuf->bfInfo, dataBlock);
if (stat == OK) {
pDataBuf->blkNum = dataBlock;
ArCFModifyInit(pDataBuf);
pDataBuf->pBlock = &pDataBuf->bfInfo;
}
else {
(void)fprintf(stderr,
"arCFChanPurge: bfRead error, dataBlk for %s\n",
ArCDChanHdr(pArChanDesc).name);
retStat = ERROR;
}
if (retStat == OK)
stat = bfRelease(pBfDesc, &pDataBuf->bfInfo, dataBlock);
if (stat == OK) {
/* adjust firstByte in index block to indicate that
* this blockInfo entry is no longer valid, just in
* case */
pIndexBuf->bfInfo.firstByte += sizeof(AR_DAT_INFO);
}
else {
(void)fprintf(stderr,
"arCFChanPurge: bfRelease error, dataBlk for %s\n",
ArCDChanHdr(pArChanDesc).name);
retStat = ERROR;
}
curDatInf++;
}
}
if (retStat == OK) {
/*----------------------------------------------------------------------------
* now release the index block back to the free block list.
*
* set to process the next index block, using the forward link which
* was saved earlier. Also, set the pointer the the index list in the
* chanHdr to point to the next index block.
*
* BUGS
* o if purging by time stamp is being done, then not all datInfo
* structures may have been deleted from the index block. In that case,
* it would be necessary to write the updated index block back to the
* file and leave it as the head of the index block list.
*---------------------------------------------------------------------------*/
stat = bfRelease(pBfDesc, &pIndexBuf->bfInfo, indexBlock);
if (stat != OK) {
(void)fprintf(stderr,
"arCFChanPurge: bfRelease error, indexBlk for %s\n",
ArCDChanHdr(pArChanDesc).name);
retStat = ERROR;
}
indexBlock = nextIndexBlock;
pChanHdr->indexHead = indexBlock;
}
}
}
/*----------------------------------------------------------------------------
* final cleanup
*
* BUGS
* o cleanup assumes total purge
*----------------------------------------------------------------------------*/
arCF_freeDATA_BUF(pDataBuf);
pArChanDesc->pDataBuf = NULL;
arCF_freeINDEX_BUF(pIndexBuf);
pArChanDesc->pIndexBuf = NULL;
pArChanDesc->pData = NULL;
pArChanDesc->remainCount = -10;
pArChanDesc->datInfNum = -10;
if (pChanHdr->indexHead == 0)
pChanHdr->indexTail = 0;
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pMIBuf);
return retStat;
}
/*----------------------------------------------------------------------------
* macros for use by arCFChanRead() and friends
*
* int ArCRFetch fetch as an int the next character from the current
* read buffer; if there isn't a next character
* (or if there isn't a current read buffer),
* get the next read buffer and fetch the
* first available character from it. On error
* or at end of file, returns ERROR.
*---------------------------------------------------------------------------*/
#define ArCRFetch \
( (--pArChanDesc->remainCount >= 0) \
? ((int)*(++pArChanDesc->pData))&0xff \
: arCF_DFillRbufAndGetc(pArChanDesc) )
/*+/subr**********************************************************************
* NAME arCFChanRead - read next value from a channel
*
* DESCRIPTION
* This routine reads the next value for a channel and fills in the
* caller's db_access_val union with the value, time stamp, etc.
* as appropriate.
*
* An ERROR return indicates that a valid value wasn't put into the
* caller's buffer. The .flags item in the channel descriptor must be
* examined to determine the reason:
*
* AR_CDESC_EOF indicates that the stream for the channel is at
* end of file
* AR_CDESC_BEGIN indicates that a new run of data is beginning
* for the channel
* AR_CDESC_SNAP_BEGIN indicates that a new run of data is beginning
* for the channel, where the run is a synchronous snapshot
* AR_CDESC_SNAP_END indicates that the following value is the
* last in a snapshot (may occur with AR_CDESC_SNAP_BEGIN)
* AR_CDESC_INACT indicates that the run of data is done, because
* the archive request was inactivated (or disabled)
*
* RETURNS
* OK, or
* ERROR
*
* BUGS
* o assumes that representation of numbers is the same on `retrieval
* host' as on original `store host'
* o the type of the read request must be the same `flavor' as that of
* the channel--no conversions are provided from short to string, etc.
* o need a way to return to caller number of elements actually read
*
* NOTES
* 1. `count' need not match native count. If the count is less than
* the native count, excess values will be discarded. If the requested
* type is not a `plain' type (e.g., DBR_FLOAT), then information
* will be stored in the caller's buffer even if count is 0 (assuming
* the caller's buffer pointer isn't NULL).
*
*-*/
long
arCFChanRead(pArChanDesc, type, pAccessBuf, count)
register
AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */
chtype type; /* I buffer type; DBR_xxx */
register
union db_access_val *pAccessBuf;/* IO pointer to caller's buffer, or NULL */
int count; /* I array element count for caller's buffer */
{
int retStat=OK; /* return status to caller */
int nBytes; /* number of value bytes in file item value */
int countBytes; /* number of bytes allowed by caller's buf */
int i; /* temp for loops */
int byte0, byteN; /* bytes from the entry */
char *pBuf; /* temporary pointer for moving data */
chtype recType; /* type of buffer */
assert(pArChanDesc != NULL);
assert(pArChanDesc->pMIBuf != NULL);
assert(dbr_type_is_valid(type) || count == 0);
assert(pAccessBuf != NULL || count == 0);
assert(count >= 0);
/*----------------------------------------------------------------------------
* handle time stamp
*
* (arCFChanReadTs() resets the various flags)
*
* put the proper time stamp information into the access buffer if
* the type is DBR_TIME_xxx.
*----------------------------------------------------------------------------*/
if ((retStat = arCFChanReadTs(pArChanDesc)) == OK) {
if (dbr_type_is_TIME(type))
pAccessBuf->tstrval.stamp = pArChanDesc->timeStamp;
}
/*----------------------------------------------------------------------------
* handle alarm status/severity if this item has it. If the item does
* have it, process it and then exit this section with a make believe
* item code of value without status.
*
* This section assumes that all DBR_ types which have status have
* the status and severity structure items in the same place as
* struct dbr_sts_string .
*---------------------------------------------------------------------------*/
if (retStat == OK) {
byte0 = ArCRFetch;
if (byte0 == AR_DAT_IC_VAL_STAT) {
byte0 = ArCRFetch;
byteN = ArCRFetch;
if (!dbr_type_is_plain(type)) {
pAccessBuf->sstrval.status = byte0;
pAccessBuf->sstrval.severity = byteN;
}
if (byteN == ERROR)
retStat = ERROR;
else
byte0 = AR_DAT_IC_VAL;
}
}
/*----------------------------------------------------------------------------
* if this is DBR_GR_xxx, get graphics info from chanHdr
*---------------------------------------------------------------------------*/
if (retStat == OK && dbr_type_is_GR(type)) {
switch (type) {
case DBR_GR_STRING:
break; /* no graphics for string */
case DBR_GR_SHORT:
#define ArCRShrtGr ArCDChanHdr(pArChanDesc).graphics.shortGr
(void)strcpy(pAccessBuf->gshrtval.units, ArCRShrtGr.units);
pAccessBuf->gshrtval.upper_disp_limit = ArCRShrtGr.upDispLim;
pAccessBuf->gshrtval.lower_disp_limit = ArCRShrtGr.lowDispLim;
pAccessBuf->gshrtval.upper_alarm_limit = ArCRShrtGr.upAlmLim;
pAccessBuf->gshrtval.upper_warning_limit = ArCRShrtGr.upWarnLim;
pAccessBuf->gshrtval.lower_warning_limit =
ArCRShrtGr.lowWarnLim;
pAccessBuf->gshrtval.lower_alarm_limit = ArCRShrtGr.lowAlmLim;
break;
case DBR_GR_LONG:
#define ArCRLngGr ArCDChanHdr(pArChanDesc).graphics.longGr
(void)strcpy(pAccessBuf->glngval.units, ArCRLngGr.units);
pAccessBuf->glngval.upper_disp_limit = ArCRLngGr.upDispLim;
pAccessBuf->glngval.lower_disp_limit = ArCRLngGr.lowDispLim;
pAccessBuf->glngval.upper_alarm_limit = ArCRLngGr.upAlmLim;
pAccessBuf->glngval.upper_warning_limit = ArCRLngGr.upWarnLim;
pAccessBuf->glngval.lower_warning_limit =
ArCRLngGr.lowWarnLim;
pAccessBuf->glngval.lower_alarm_limit = ArCRLngGr.lowAlmLim;
break;
case DBR_GR_CHAR:
#define ArCRChrGr ArCDChanHdr(pArChanDesc).graphics.shortGr
(void)strcpy(pAccessBuf->gchrval.units, ArCRChrGr.units);
pAccessBuf->gchrval.upper_disp_limit = ArCRChrGr.upDispLim;
pAccessBuf->gchrval.lower_disp_limit = ArCRChrGr.lowDispLim;
pAccessBuf->gchrval.upper_alarm_limit = ArCRChrGr.upAlmLim;
pAccessBuf->gchrval.upper_warning_limit = ArCRChrGr.upWarnLim;
pAccessBuf->gchrval.lower_warning_limit = ArCRChrGr.lowWarnLim;
pAccessBuf->gchrval.lower_alarm_limit = ArCRChrGr.lowAlmLim;
break;
case DBR_GR_FLOAT:
#define ArCRFltGr ArCDChanHdr(pArChanDesc).graphics.floatGr
pAccessBuf->gfltval.precision = ArCRFltGr.precision;
(void)strcpy(pAccessBuf->gfltval.units, ArCRFltGr.units);
pAccessBuf->gfltval.upper_disp_limit = ArCRFltGr.upDispLim;
pAccessBuf->gfltval.lower_disp_limit = ArCRFltGr.lowDispLim;
pAccessBuf->gfltval.upper_alarm_limit = ArCRFltGr.upAlmLim;
pAccessBuf->gfltval.upper_warning_limit = ArCRFltGr.upWarnLim;
pAccessBuf->gfltval.lower_warning_limit = ArCRFltGr.lowWarnLim;
pAccessBuf->gfltval.lower_alarm_limit = ArCRFltGr.lowAlmLim;
break;
case DBR_GR_DOUBLE:
#define ArCRDblGr ArCDChanHdr(pArChanDesc).graphics.doubleGr
pAccessBuf->gdblval.precision = ArCRDblGr.precision;
(void)strcpy(pAccessBuf->gdblval.units, ArCRDblGr.units);
pAccessBuf->gdblval.upper_disp_limit = ArCRDblGr.upDispLim;
pAccessBuf->gdblval.lower_disp_limit = ArCRDblGr.lowDispLim;
pAccessBuf->gdblval.upper_alarm_limit = ArCRDblGr.upAlmLim;
pAccessBuf->gdblval.upper_warning_limit = ArCRDblGr.upWarnLim;
pAccessBuf->gdblval.lower_warning_limit = ArCRDblGr.lowWarnLim;
pAccessBuf->gdblval.lower_alarm_limit = ArCRDblGr.lowAlmLim;
break;
case DBR_GR_ENUM:
#define ArCREnmGr ArCDChanHdr(pArChanDesc).graphics.enumGr.pGRBuf
pAccessBuf->genmval.no_str = ArCREnmGr->numStrings;
for (i=0; i<ArCREnmGr->numStrings; i++) {
(void)strcpy(pAccessBuf->genmval.strs[i],
ArCREnmGr->states[i]);
}
break;
default:
(void)fprintf(stderr, "arCFChanRead: unimplemented GR type\n");
retStat = ERROR;
break;
}
}
/*----------------------------------------------------------------------------
* now handle the value item. Only the actual values are handled here, since
* status, time stamp, etc. have already been handled.
*---------------------------------------------------------------------------*/
if (retStat != OK)
; /* no action */
else if (byte0 != AR_DAT_IC_VAL) {
(void)fprintf(stderr, "arCFChanRead: unexpected item code %d\n", byte0);
retStat = ERROR;
}
else {
assert(dbr_type_is_valid(type));
recType = ArCDChanHdr(pArChanDesc).chanType;
if (count <= 0) pBuf = NULL;
else pBuf = ((char *)dbr_value_ptr(pAccessBuf, type)) - 1;
/* set byte count one byte shy to allow checking status on the
last one. This is done as a time saving measure */
nBytes = ArCDChanHdr(pArChanDesc).elCount*dbr_value_size[recType] - 1;
countBytes = count * dbr_value_size[recType];
for (i=0; i<nBytes; i++) {
if (i<countBytes) *(++pBuf) = ArCRFetch;
else ArCRFetch;
}
byteN = ArCRFetch; /* last, for status */
if (countBytes > nBytes) *(++pBuf) = byteN;
if (byteN == ERROR) retStat = ERROR;
}
return retStat;
}
/*+/internal******************************************************************
* NAME arCFChanReadTs - read and process time stamp items
*
* DESCRIPTION
* This routine reads successive items until a non-time stamp item
* is found. The chanDesc time stamp is updated appropriately.
*
* End of file results in an error return, with the AR_CDESC_EOF flag
* set in chanDesc.flags .
*
* Encountering one of the AR_DAT_IC_MON_xxx flags results in an error
* return, with an appropriate AR_CDESC_xxx flag set in chanDesc.flags .
*
* RETURNS
* OK, or
* ERROR
*
*-*/
long
arCFChanReadTs(pArChanDesc)
register
AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */
{
int retStat=OK; /* return status to caller */
TS_STAMP timeDel; /* delta time */
int byte0, byteN; /* bytes from the entry */
assert(pArChanDesc != NULL);
assert(pArChanDesc->pMIBuf != NULL);
/* turn off various flags */
pArChanDesc->flags &= ~(AR_CDESC_EOF | AR_CDESC_BEGIN | AR_CDESC_INACT |
AR_CDESC_SNAP_BEGIN | AR_CDESC_SNAP_END |
AR_CDESC_DISCON | AR_CDESC_SUP);
/*----------------------------------------------------------------------------
* handle time stamp
*
* if the first byte indicates a time stamp, process it,
* updating the time information in the channel descriptor.
*
* this section of code loops until a non-time item is found, since
* several time related items can follow each other. The chanDesc
* is left so that the next read will start with the non-time item.
*
* this section assumes that all DBR_ types which have time have
* the the time structure items in the same place as
* struct dbr_time_string .
*---------------------------------------------------------------------------*/
while (retStat == OK) {
timeDel.secPastEpoch = 0;
timeDel.nsec = 0;
if ( (byte0 = ArCRFetch) != ERROR) {
if ((byte0 & 0x80) == 0) { /* delta time, milli-sec */
if ( (byteN = ArCRFetch) != ERROR) {
if ((timeDel.nsec = ((byte0<<8) + byteN)) < 1000)
timeDel.nsec *= 1000000;
else {
timeDel.secPastEpoch = timeDel.nsec / 1000;
timeDel.nsec = 1000000 * (timeDel.nsec % 1000);
}
TsAddStamps(&pArChanDesc->timeStamp,
&pArChanDesc->timeStamp, &timeDel);
}
else
retStat = ERROR;
}
else if (byte0 == AR_DAT_IC_MON_BEGIN) { /* begin monitor */
pArChanDesc->flags |= AR_CDESC_BEGIN;
retStat = ERROR;
}
else if (byte0 == AR_DAT_IC_SNAP_BEGIN) { /* begin snapshot */
pArChanDesc->flags |= AR_CDESC_SNAP_BEGIN;
retStat = ERROR;
}
else if (byte0 == AR_DAT_IC_SNAP_SINGLE) { /* 1 sample snapshot */
pArChanDesc->flags |= AR_CDESC_SNAP_BEGIN | AR_CDESC_SNAP_END;
retStat = ERROR;
}
else if (byte0 == AR_DAT_IC_SNAP_END) { /* end snapshot */
pArChanDesc->flags |= AR_CDESC_SNAP_END;
retStat = ERROR;
}
else if (byte0 == AR_DAT_IC_MON_INACT) { /* req inactivated */
pArChanDesc->flags |= AR_CDESC_INACT;
retStat = ERROR;
}
else if (byte0 == AR_DAT_IC_MON_DISCON) { /* chan disconnected */
pArChanDesc->flags |= AR_CDESC_DISCON;
retStat = ERROR;
}
else if ((byte0 & 0xf0) != AR_DAT_IC_TIME_XXX) {
pArChanDesc->remainCount++; /* `un-read' the byte */
pArChanDesc->pData--;
goto timeDone; /* not a time code; bail out */
}
else if (byte0 == AR_DAT_IC_TIME_STAMP) {
timeDel.secPastEpoch = ArCRFetch <<8;
timeDel.secPastEpoch = (timeDel.secPastEpoch + ArCRFetch)<<8;
timeDel.secPastEpoch = (timeDel.secPastEpoch + ArCRFetch)<<8;
timeDel.secPastEpoch += (byteN = ArCRFetch);
if (byteN != ERROR)
pArChanDesc->timeStamp = timeDel;
else
retStat = ERROR;
}
else if (byte0 == AR_DAT_IC_TIME_USEC) { /* delta time, micro-sec */
timeDel.nsec = ArCRFetch <<8;
timeDel.nsec = (timeDel.nsec + ArCRFetch)<<8;
if ((timeDel.nsec += (byteN = ArCRFetch)) < 1000000)
timeDel.nsec *= 1000;
else {
timeDel.secPastEpoch = timeDel.nsec / 1000000;
timeDel.nsec = 1000 * (timeDel.nsec % 1000000);
}
if (byteN != ERROR) {
TsAddStamps(&pArChanDesc->timeStamp,
&pArChanDesc->timeStamp, &timeDel);
}
else
retStat = ERROR;
}
else if (byte0 == AR_DAT_IC_TIME_NSEC) { /* delta time, nano-sec */
timeDel.nsec = ArCRFetch <<8;
timeDel.nsec = (timeDel.nsec + ArCRFetch)<<8;
timeDel.nsec = (timeDel.nsec + ArCRFetch)<<8;
timeDel.nsec += (byteN = ArCRFetch);
if (byteN != ERROR) {
TsAddStamps(&pArChanDesc->timeStamp,
&pArChanDesc->timeStamp, &timeDel);
}
else
retStat = ERROR;
}
else
assertAlways(0); /* not one of the expected codes */
}
if (byte0 == ERROR)
retStat = ERROR;
}
timeDone: /* must FOLLOW the while loop */
return retStat;
}
/*----------------------------------------------------------------------------
* macros for use by arCFChanWrite() and friends
*
* int ArCWStore(c) insert character into current write buffer; if
* there isn't room (or if there isn't a current
* buffer) flush current buffer and insert
* character into new buffer
*
* int ArCWFlushAndStore(c) flush current write buffer (if any) and
* insert character into new buffer
*---------------------------------------------------------------------------*/
#define ArCWFlushAndStore(c) ArCFFlushAndStore((c), pArChanDesc)
#define ArCWStore(c) ArCFStore((c), pArChanDesc)
/*+/subr**********************************************************************
* NAME arCFChanWrite - write a channel access buffer to a channel
*
* DESCRIPTION
* This routine accepts an event_handler_args structure for a channel and
* writes time stamp, alarm status/severity, and value(s) to the
* channel data file for the channel. The buffer must be one of the
* DBR_TIME_xxx types.
*
* The header information for the channel must exist prior to calling
* this routine. In addition, the buffer type must agree with the
* native type stored in the header (e.g., if buffer type is
* DBR_TIME_SHORT, then the header must indicate DBF_SHORT). If
* any of these requirements are violated, then no write is done and
* ERROR status is returned.
*
* RETURNS
* OK, or
* ERROR
*
* BUGS
* o error handling needs to be validated
* o no attention is paid to issues related to calling this routine from
* Channel Access event handler. In particular, there should be
* queues and tasks for: receiving full buffers from this routine;
* supplying empty buffers to this routine; and supplying free blocks
* to this routine. THIS ROUTINE SHOULD NEVER BLOCK!!!
*
* NOTES
* 1. This routine uses the smallest delta time item consistent with the
* significance present in the .nsec member of the time stamp. E.g.,
* if .nsec is a multiple of 1000000, then a milli-sec delta time will
* be used.
*
*-*/
long
arCFChanWrite(pArChanDesc, pArg)
register
AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */
struct event_handler_args *pArg;/* I pointer to monitor structure */
{
#define CW_NSEC ((struct dbr_time_string *)pCaBuf)->stamp.nsec
#define CW_SPE ((struct dbr_time_string *)pCaBuf)->stamp.secPastEpoch
register
void *pCaBuf; /* pointer to buffer */
int stat; /* status from calls */
int deltaCode; /* AR_DAT_IC_TIME_xxx code for delta time */
int deltaCount; /* count of bytes to represent delta time */
TS_STAMP diff; /* difference between data stamp and chanDesc */
ULONG fraction; /* fractional part of second, units vary */
int fracCode; /* AR_DAT_IC_TIME_xxx code for delta time */
int fracCount; /* count of bytes to represent delta time */
register
char *ptr; /* temp for pointer in loops */
register
char *ptrEnd; /* temp for pointer bound in loops */
/*----------------------------------------------------------------------------
* get some stuff out of the structure supplied as input pArg
*---------------------------------------------------------------------------*/
pCaBuf = pArg->dbr;
assert(pArChanDesc != NULL);
assert(pArChanDesc->pMIBuf != NULL);
assert(pCaBuf != NULL);
if (!dbr_type_is_TIME(pArg->type))
goto error;
if (pArg->count != ArCDChanHdr(pArChanDesc).elCount)
goto error; /* ignore changed count */
pArChanDesc->flags &= ~AR_CDESC_SUP; /* turn off suppress */
/*----------------------------------------------------------------------------
* time stamp handling
*
* (This is done 'in-line' as an optimization strategy. When done as a
* separate routine, this used as much CPU time as all the rest of
* arCFChanWrite() used for status and data for DBR_TIME_FLOAT.)
*
* NOTES
* 1. A lot of the logic in this routine is in support of the rule that
* when a "time item" is used, the item can't cross a block
* boundary.
*---------------------------------------------------------------------------*/
if (TsCmpStampsLE(&((struct dbr_time_string *)pCaBuf)->stamp,
&pArChanDesc->timeStamp)) {
goto ignore; /* ignore time running backward */
}
if (ArCDDatInfo(pArChanDesc).stamp.secPastEpoch == 0) {
/*----------------------------------------------------------------------------
* case 1
* this is the first item which has its beginning in this block. A
* time stamp in the datInfo is needed; there must be enough room
* in the block to avoid starting a new block--i.e., to hold the item
* code for the data entry.
*
* Since this is the first item which has its beginning in this block,
* datInfo.stamp must be set. The .firstByte item may also need to be set.
*
* This may also be the first item for the channel. If this is the case,
* then .oldestSecPastEpoch in chanHdr needs to be set.
*---------------------------------------------------------------------------*/
if (pArChanDesc->remainCount < 1) {
if (ArCWFlushAndStore(AR_DAT_IC_VAL_STAT ) != OK)
goto error;
}
else {
if (ArCWStore( AR_DAT_IC_VAL_STAT ) != OK)
goto error;
}
if (pArChanDesc->pDataBuf->bfInfo.firstByte == 0) {
pArChanDesc->pDataBuf->bfInfo.firstByte = pArChanDesc->pData -
(char *)(&pArChanDesc->pDataBuf->bfInfo);
}
ArCDDatInfo(pArChanDesc).stamp =
((struct dbr_time_string *)pCaBuf)->stamp;
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pIndexBuf);
if (ArCDChanHdr(pArChanDesc).oldestSecPastEpoch == 0) {
ArCDChanHdr(pArChanDesc).oldestSecPastEpoch = CW_SPE;
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pMIBuf);
}
}
else {
/*----------------------------------------------------------------------------
* not first item beginning in this block. Figure out first what's
* going to be needed to handle the time stamp. The questions are:
* o without loss of significance, what type of delta time item is
* required? For that type of delta time item, is the delta time
* less than the maximum? If so, is there enough space left in the
* block to hold the delta time item and the first byte of a data item?
* o if a delta time item can't be used or if there isn't enough room
* in the block, then a full time stamp is needed.
*---------------------------------------------------------------------------*/
TsDiffAsStamp(&diff, &((struct dbr_time_string *)pCaBuf)->stamp,
&pArChanDesc->timeStamp);
if (diff.nsec % 1000000 == 0 && diff.secPastEpoch < 30 &&
pArChanDesc->remainCount >= 2+1) {
/*----------------------------------------------------------------------------
* case 2
* the change can be represented as a delta milli-second (and, there is
* room enough to store the item and the first byte of value)
*----------------------------------------------------------------------------*/
fraction = diff.secPastEpoch*1000 + diff.nsec/1000000;
(void)ArCWStore( fraction>> 8 );
(void)ArCWStore( fraction );
if (ArCWStore( AR_DAT_IC_VAL_STAT ) != OK)
goto error;
}
else if (diff.nsec % 1000 == 0 && diff.secPastEpoch < 15 &&
pArChanDesc->remainCount >= 4+1) {
/*----------------------------------------------------------------------------
* case 3
* the change can be represented as a delta micro-second (and, there is
* room enough to store the item and the first byte of value)
*----------------------------------------------------------------------------*/
fraction = diff.secPastEpoch*1000000 + diff.nsec/1000;
(void)ArCWStore( AR_DAT_IC_TIME_USEC );
(void)ArCWStore( fraction>>16 );
(void)ArCWStore( fraction>> 8 );
(void)ArCWStore( fraction );
if (ArCWStore( AR_DAT_IC_VAL_STAT ) != OK)
goto error;
}
else if (diff.secPastEpoch < 4 && pArChanDesc->remainCount >= 5+1) {
/*----------------------------------------------------------------------------
* case 4
* the change can be represented as a delta nano-second (and, there is
* room enough to store the item and the first byte of value)
*----------------------------------------------------------------------------*/
fraction = diff.secPastEpoch*1000000000 + diff.nsec;
(void)ArCWStore( AR_DAT_IC_TIME_NSEC );
(void)ArCWStore( fraction>>24 );
(void)ArCWStore( fraction>>16 );
(void)ArCWStore( fraction>> 8 );
(void)ArCWStore( fraction );
if (ArCWStore( AR_DAT_IC_VAL_STAT ) != OK)
goto error;
}
else {
/*----------------------------------------------------------------------------
* case 5
* the change is either too large to be accomodated by a delta time
* item or else there isn't enough room left in the block for a delta
* time item followed by the first byte of a data item. If there isn't
* enough space left in the block for the required items, a new block
* will be started.
*---------------------------------------------------------------------------*/
deltaCode = AR_DAT_IC_TIME_STAMP; /* full time stamp needed */
deltaCount = 5+1; /* 5 for stamp, 1 for value code */
if (CW_NSEC == 0) {
fracCount = 2;
fracCode = AR_DAT_IC_TIME_MSEC; /* delta msec */
/* don't add to deltaCount, since this item is optional */
fraction = 0;
}
else if (CW_NSEC % 1000000 == 0) {
fracCount = 2;
fracCode = AR_DAT_IC_TIME_MSEC; /* delta msec */
deltaCount += fracCount;
fraction = CW_NSEC / 1000000;
}
else if (CW_NSEC % 1000 == 0) {
fracCount = 4;
fracCode = AR_DAT_IC_TIME_USEC; /* delta usec */
deltaCount += fracCount;
fraction = CW_NSEC / 1000;
}
else {
fracCount = 5;
fracCode = AR_DAT_IC_TIME_NSEC; /* delta nsec */
deltaCount += fracCount;
fraction = CW_NSEC;
}
if (pArChanDesc->remainCount < deltaCount) {
if (ArCWFlushAndStore(AR_DAT_IC_VAL_STAT ) != OK)
goto error;
pArChanDesc->pDataBuf->bfInfo.firstByte = pArChanDesc->pData -
(char *)(&pArChanDesc->pDataBuf->bfInfo);
ArCDDatInfo(pArChanDesc).stamp =
((struct dbr_time_string *)pCaBuf)->stamp;
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pIndexBuf);
}
else {
(void)ArCWStore(deltaCode );
(void)ArCWStore(CW_SPE >>24 );
(void)ArCWStore(CW_SPE >>16 );
(void)ArCWStore(CW_SPE >> 8 );
(void)ArCWStore(CW_SPE );
if (fraction != 0) {
if (fracCode != AR_DAT_IC_TIME_MSEC) {
(void)ArCWStore(fracCode );
if (fracCode == AR_DAT_IC_TIME_NSEC)
(void)ArCWStore(fraction>>24 );
(void)ArCWStore(fraction>>16 );
}
(void)ArCWStore( fraction>> 8 );
(void)ArCWStore( fraction );
}
if (ArCWStore( AR_DAT_IC_VAL_STAT ) != OK)
goto error;
}
}
}
pArChanDesc->timeStamp = ((struct dbr_time_string *)pCaBuf)->stamp;
/*----------------------------------------------------------------------------
* now store the status and data. The code above has already stored
* an AR_DAT_IC_VAL_STAT item code in the file.
*----------------------------------------------------------------------------*/
(void)ArCWStore(((struct dbr_time_string *)pCaBuf)->status);
(void)ArCWStore(((struct dbr_time_string *)pCaBuf)->severity);
if (pArg->type == DBR_TIME_STRING) {
#define ArString ((struct dbr_time_string *)pCaBuf)
assert(ArCDChanHdr(pArChanDesc).chanType == DBF_STRING);
ptr = (char *)(ArString->value);
ptrEnd = ptr + pArg->count*db_strval_dim - 1;
while (ptr < ptrEnd)
(void)ArCWStore(*ptr++);
if (ArCWStore(*ptr++) != OK) goto error;
#undef ArString
}
else if (pArg->type == DBR_TIME_SHORT) {
#define ArShort ((struct dbr_time_short *)pCaBuf)
assert(ArCDChanHdr(pArChanDesc).chanType == DBF_SHORT);
ptr = (char *)(&ArShort->value);
ptrEnd = ptr + pArg->count * sizeof(short) - 1;
while (ptr < ptrEnd)
(void)ArCWStore(*ptr++);
if (ArCWStore(*ptr++) != OK) goto error;
#undef ArShort
}
else if (pArg->type == DBR_TIME_FLOAT) {
#define ArFloat ((struct dbr_time_float *)pCaBuf)
assert(ArCDChanHdr(pArChanDesc).chanType == DBF_FLOAT);
ptr = (char *)(&ArFloat->value);
ptrEnd = ptr + pArg->count * sizeof(float) - 1;
while (ptr < ptrEnd)
(void)ArCWStore(*ptr++);
if (ArCWStore(*ptr++) != OK) goto error;
#undef ArFloat
}
else if (pArg->type == DBR_TIME_LONG) {
#define ArLong ((struct dbr_time_long *)pCaBuf)
assert(ArCDChanHdr(pArChanDesc).chanType == DBF_LONG);
ptr = (char *)(&ArLong->value);
ptrEnd = ptr + pArg->count * sizeof(long) - 1;
while (ptr < ptrEnd)
(void)ArCWStore(*ptr++);
if (ArCWStore(*ptr++) != OK) goto error;
#undef ArLong
}
else if (pArg->type == DBR_TIME_DOUBLE) {
#define ArDouble ((struct dbr_time_double *)pCaBuf)
assert(ArCDChanHdr(pArChanDesc).chanType == DBF_DOUBLE);
ptr = (char *)(&ArDouble->value);
ptrEnd = ptr + pArg->count * sizeof(double) - 1;
while (ptr < ptrEnd)
(void)ArCWStore(*ptr++);
if (ArCWStore(*ptr++) != OK) goto error;
#undef ArDouble
}
else if (pArg->type == DBR_TIME_ENUM) {
#define ArEnum ((struct dbr_time_enum *)pCaBuf)
assert(ArCDChanHdr(pArChanDesc).chanType == DBF_ENUM);
ptr = (char *)(&ArEnum->value);
ptrEnd = ptr + pArg->count * sizeof(short) - 1;
while (ptr < ptrEnd)
(void)ArCWStore(*ptr++);
if (ArCWStore(*ptr++) != OK) goto error;
#undef ArEnum
}
else if (pArg->type == DBR_TIME_CHAR) {
#define ArChar ((struct dbr_time_char *)pCaBuf)
assert(ArCDChanHdr(pArChanDesc).chanType == DBF_CHAR);
ptr = (char *)(&ArChar->value);
ptrEnd = ptr + pArg->count * sizeof(char) - 1;
while (ptr < ptrEnd)
(void)ArCWStore(*ptr++);
if (ArCWStore(*ptr++) != OK) goto error;
#undef ArChar
}
else
assertAlways(0);
/*-----------------------------------------------------------------------------
* update bookkeeping information
*
* .lastByte
* .modified in data buffer
* .newestStamp in MI buffer
* .modified in MI buffer
*----------------------------------------------------------------------------*/
if (pArChanDesc->pData != NULL) {
pArChanDesc->pDataBuf->bfInfo.lastByte = pArChanDesc->pData -
(char *)&pArChanDesc->pDataBuf->bfInfo;
assert(pArChanDesc->pDataBuf->bfInfo.lastByte > 0);
assert(pArChanDesc->pDataBuf->bfInfo.lastByte < 2000);
}
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pDataBuf);
ArCDChanHdr(pArChanDesc).newestSecPastEpoch =
pArChanDesc->timeStamp.secPastEpoch;
ArCDChanHdr(pArChanDesc).newestNsec = pArChanDesc->timeStamp.nsec;
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pMIBuf);
return OK;
error:
return ERROR;
ignore:
return OK;
#undef CW_NSEC
#undef CW_SPE
}
/*+/subr**********************************************************************
* NAME arCFChanWrite_args
*
*-*/
long
arCFChanWrite_args(pArChanDesc, stamp, dbfType, count, pData, alStat, alSevr)
register
AR_CHAN_DESC *pArChanDesc;/* IO pointer to channel descriptor */
TS_STAMP stamp; /* I time stamp for data */
chtype dbfType; /* I type of data, one of the DBF_... */
int count; /* I element count for data */
void *pData; /* I pointer to actual data */
int alStat; /* I alarm status for data */
int alSevr; /* I alarm severity for data */
{
#define CW_NSEC stamp.nsec
#define CW_SPE stamp.secPastEpoch
int stat; /* status from calls */
int deltaCode; /* AR_DAT_IC_TIME_xxx code for delta time */
int deltaCount; /* count of bytes to represent delta time */
TS_STAMP diff; /* difference between data stamp and chanDesc */
ULONG fraction; /* fractional part of second, units vary */
int fracCode; /* AR_DAT_IC_TIME_xxx code for delta time */
int fracCount; /* count of bytes to represent delta time */
register
char *ptr; /* temp for pointer in loops */
register
char *ptrEnd; /* temp for pointer bound in loops */
assert(pArChanDesc != NULL);
assert(pArChanDesc->pMIBuf != NULL);
if (count != ArCDChanHdr(pArChanDesc).elCount)
goto error; /* ignore changed count */
pArChanDesc->flags &= ~AR_CDESC_SUP; /* turn off suppress */
/*----------------------------------------------------------------------------
* time stamp handling
*
* (This is done 'in-line' as an optimization strategy. When done as a
* separate routine, this used as much CPU time as all the rest of
* arCFChanWrite() used for status and data for DBR_FLOAT.)
*
* NOTES
* 1. A lot of the logic in this routine is in support of the rule that
* when a "time item" is used, the item can't cross a block
* boundary.
*---------------------------------------------------------------------------*/
if (TsCmpStampsLE(&stamp, &pArChanDesc->timeStamp))
goto ignore; /* ignore time running backward */
if (ArCDDatInfo(pArChanDesc).stamp.secPastEpoch == 0) {
/*----------------------------------------------------------------------------
* case 1
* this is the first item which has its beginning in this block. A
* time stamp in the datInfo is needed; there must be enough room
* in the block to avoid starting a new block--i.e., to hold the item
* code for the data entry.
*
* Since this is the first item which has its beginning in this block,
* datInfo.stamp must be set. The .firstByte item may also need to be set.
*
* This may also be the first item for the channel. If this is the case,
* then .oldestSecPastEpoch in chanHdr needs to be set.
*---------------------------------------------------------------------------*/
if (pArChanDesc->remainCount < 1) {
if (ArCWFlushAndStore(AR_DAT_IC_VAL_STAT ) != OK)
goto error;
}
else {
if (ArCWStore( AR_DAT_IC_VAL_STAT ) != OK)
goto error;
}
if (pArChanDesc->pDataBuf->bfInfo.firstByte == 0) {
pArChanDesc->pDataBuf->bfInfo.firstByte = pArChanDesc->pData -
(char *)(&pArChanDesc->pDataBuf->bfInfo);
}
ArCDDatInfo(pArChanDesc).stamp = stamp;
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pIndexBuf);
if (ArCDChanHdr(pArChanDesc).oldestSecPastEpoch == 0) {
ArCDChanHdr(pArChanDesc).oldestSecPastEpoch = CW_SPE;
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pMIBuf);
}
}
else {
/*----------------------------------------------------------------------------
* not first item beginning in this block. Figure out first what's
* going to be needed to handle the time stamp. The questions are:
* o without loss of significance, what type of delta time item is
* required? For that type of delta time item, is the delta time
* less than the maximum? If so, is there enough space left in the
* block to hold the delta time item and the first byte of a data item?
* o if a delta time item can't be used or if there isn't enough room
* in the block, then a full time stamp is needed.
*---------------------------------------------------------------------------*/
TsDiffAsStamp(&diff, &stamp, &pArChanDesc->timeStamp);
if (diff.nsec % 1000000 == 0 && diff.secPastEpoch < 30 &&
pArChanDesc->remainCount >= 2+1) {
/*----------------------------------------------------------------------------
* case 2
* the change can be represented as a delta milli-second (and, there is
* room enough to store the item and the first byte of value)
*----------------------------------------------------------------------------*/
fraction = diff.secPastEpoch*1000 + diff.nsec/1000000;
(void)ArCWStore( fraction>> 8 );
(void)ArCWStore( fraction );
if (ArCWStore( AR_DAT_IC_VAL_STAT ) != OK)
goto error;
}
else if (diff.nsec % 1000 == 0 && diff.secPastEpoch < 15 &&
pArChanDesc->remainCount >= 4+1) {
/*----------------------------------------------------------------------------
* case 3
* the change can be represented as a delta micro-second (and, there is
* room enough to store the item and the first byte of value)
*----------------------------------------------------------------------------*/
fraction = diff.secPastEpoch*1000000 + diff.nsec/1000;
(void)ArCWStore( AR_DAT_IC_TIME_USEC );
(void)ArCWStore( fraction>>16 );
(void)ArCWStore( fraction>> 8 );
(void)ArCWStore( fraction );
if (ArCWStore( AR_DAT_IC_VAL_STAT ) != OK)
goto error;
}
else if (diff.secPastEpoch < 4 && pArChanDesc->remainCount >= 5+1) {
/*----------------------------------------------------------------------------
* case 4
* the change can be represented as a delta nano-second (and, there is
* room enough to store the item and the first byte of value)
*----------------------------------------------------------------------------*/
fraction = diff.secPastEpoch*1000000000 + diff.nsec;
(void)ArCWStore( AR_DAT_IC_TIME_NSEC );
(void)ArCWStore( fraction>>24 );
(void)ArCWStore( fraction>>16 );
(void)ArCWStore( fraction>> 8 );
(void)ArCWStore( fraction );
if (ArCWStore( AR_DAT_IC_VAL_STAT ) != OK)
goto error;
}
else {
/*----------------------------------------------------------------------------
* case 5
* the change is either too large to be accomodated by a delta time
* item or else there isn't enough room left in the block for a delta
* time item followed by the first byte of a data item. If there isn't
* enough space left in the block for the required items, a new block
* will be started.
*---------------------------------------------------------------------------*/
deltaCode = AR_DAT_IC_TIME_STAMP; /* full time stamp needed */
deltaCount = 5+1; /* 5 for stamp, 1 for value code */
if (CW_NSEC == 0) {
fracCount = 2;
fracCode = AR_DAT_IC_TIME_MSEC; /* delta msec */
/* don't add to deltaCount, since this item is optional */
fraction = 0;
}
else if (CW_NSEC % 1000000 == 0) {
fracCount = 2;
fracCode = AR_DAT_IC_TIME_MSEC; /* delta msec */
deltaCount += fracCount;
fraction = CW_NSEC / 1000000;
}
else if (CW_NSEC % 1000 == 0) {
fracCount = 4;
fracCode = AR_DAT_IC_TIME_USEC; /* delta usec */
deltaCount += fracCount;
fraction = CW_NSEC / 1000;
}
else {
fracCount = 5;
fracCode = AR_DAT_IC_TIME_NSEC; /* delta nsec */
deltaCount += fracCount;
fraction = CW_NSEC;
}
if (pArChanDesc->remainCount < deltaCount) {
if (ArCWFlushAndStore(AR_DAT_IC_VAL_STAT ) != OK)
goto error;
pArChanDesc->pDataBuf->bfInfo.firstByte = pArChanDesc->pData -
(char *)(&pArChanDesc->pDataBuf->bfInfo);
ArCDDatInfo(pArChanDesc).stamp = stamp;
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pIndexBuf);
}
else {
(void)ArCWStore(deltaCode );
(void)ArCWStore(CW_SPE >>24 );
(void)ArCWStore(CW_SPE >>16 );
(void)ArCWStore(CW_SPE >> 8 );
(void)ArCWStore(CW_SPE );
if (fraction != 0) {
if (fracCode != AR_DAT_IC_TIME_MSEC) {
(void)ArCWStore(fracCode );
if (fracCode == AR_DAT_IC_TIME_NSEC)
(void)ArCWStore(fraction>>24 );
(void)ArCWStore(fraction>>16 );
}
(void)ArCWStore( fraction>> 8 );
(void)ArCWStore( fraction );
}
if (ArCWStore( AR_DAT_IC_VAL_STAT ) != OK)
goto error;
}
}
}
pArChanDesc->timeStamp = stamp;
/*----------------------------------------------------------------------------
* now store the status and data. The code above has already stored
* an AR_DAT_IC_VAL_STAT item code in the file.
*----------------------------------------------------------------------------*/
(void)ArCWStore(alStat);
(void)ArCWStore(alSevr);
if (dbfType == DBF_STRING) {
assert(ArCDChanHdr(pArChanDesc).chanType == DBF_STRING);
ptr = (char *)(struct dbf_string *)pData;
ptrEnd = ptr + count*db_strval_dim - 1;
while (ptr < ptrEnd)
(void)ArCWStore(*ptr++);
if (ArCWStore(*ptr++) != OK) goto error;
}
else if (dbfType == DBF_SHORT) {
assert(ArCDChanHdr(pArChanDesc).chanType == DBF_SHORT);
ptr = (char *)(struct dbf_short *)pData;
ptrEnd = ptr + count * sizeof(short) - 1;
while (ptr < ptrEnd)
(void)ArCWStore(*ptr++);
if (ArCWStore(*ptr++) != OK) goto error;
}
else if (dbfType == DBF_FLOAT) {
assert(ArCDChanHdr(pArChanDesc).chanType == DBF_FLOAT);
ptr = (char *)(struct dbf_float *)pData;
ptrEnd = ptr + count * sizeof(float) - 1;
while (ptr < ptrEnd)
(void)ArCWStore(*ptr++);
if (ArCWStore(*ptr++) != OK) goto error;
}
else if (dbfType == DBF_LONG) {
assert(ArCDChanHdr(pArChanDesc).chanType == DBF_LONG);
ptr = (char *)(struct dbf_long *)pData;
ptrEnd = ptr + count * sizeof(long) - 1;
while (ptr < ptrEnd)
(void)ArCWStore(*ptr++);
if (ArCWStore(*ptr++) != OK) goto error;
}
else if (dbfType == DBF_DOUBLE) {
assert(ArCDChanHdr(pArChanDesc).chanType == DBF_DOUBLE);
ptr = (char *)(struct dbf_double *)pData;
ptrEnd = ptr + count * sizeof(double) - 1;
while (ptr < ptrEnd)
(void)ArCWStore(*ptr++);
if (ArCWStore(*ptr++) != OK) goto error;
}
else if (dbfType == DBF_ENUM) {
assert(ArCDChanHdr(pArChanDesc).chanType == DBF_ENUM);
ptr = (char *)(struct dbf_enum *)pData;
ptrEnd = ptr + count * sizeof(short) - 1;
while (ptr < ptrEnd)
(void)ArCWStore(*ptr++);
if (ArCWStore(*ptr++) != OK) goto error;
}
else if (dbfType == DBF_CHAR) {
assert(ArCDChanHdr(pArChanDesc).chanType == DBF_CHAR);
ptr = (char *)(struct dbf_char *)pData;
ptrEnd = ptr + count * sizeof(char) - 1;
while (ptr < ptrEnd)
(void)ArCWStore(*ptr++);
if (ArCWStore(*ptr++) != OK) goto error;
}
else
assertAlways(0);
/*-----------------------------------------------------------------------------
* update bookkeeping information
*
* .lastByte
* .modified in data buffer
* .newestStamp in MI buffer
* .modified in MI buffer
*----------------------------------------------------------------------------*/
if (pArChanDesc->pData != NULL) {
pArChanDesc->pDataBuf->bfInfo.lastByte = pArChanDesc->pData -
(char *)&pArChanDesc->pDataBuf->bfInfo;
assert(pArChanDesc->pDataBuf->bfInfo.lastByte > 0);
assert(pArChanDesc->pDataBuf->bfInfo.lastByte < 2000);
}
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pDataBuf);
ArCDChanHdr(pArChanDesc).newestSecPastEpoch =
pArChanDesc->timeStamp.secPastEpoch;
ArCDChanHdr(pArChanDesc).newestNsec = pArChanDesc->timeStamp.nsec;
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pMIBuf);
return OK;
error:
return ERROR;
ignore:
return OK;
#undef CW_NSEC
#undef CW_SPE
}
/*+/subr**********************************************************************
* NAME arCFChanWriteByte - write byte into a channel's stream
*
* DESCRIPTION
*
* RETURNS
* OK, or
* ERROR
*
* BUGS
* o text
*
*-*/
long
arCFChanWriteByte(pArChanDesc, value)
AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */
int value; /* I value to write to stream */
{
int stat; /* status from calls */
assert(pArChanDesc != NULL);
assert(pArChanDesc->pMIBuf != NULL);
pArChanDesc->flags &= ~AR_CDESC_SUP; /* turn off suppress */
if (pArChanDesc->remainCount < 1) {
if (ArCWFlushAndStore(value ) != OK)
goto error;
}
else {
if (ArCWStore( value ) != OK)
goto error;
}
if (pArChanDesc->pDataBuf->bfInfo.firstByte == 0) {
pArChanDesc->pDataBuf->bfInfo.firstByte = pArChanDesc->pData -
(char *)(&pArChanDesc->pDataBuf->bfInfo);
}
pArChanDesc->pDataBuf->bfInfo.lastByte = pArChanDesc->pData -
(char *)(&pArChanDesc->pDataBuf->bfInfo);
assert(pArChanDesc->pDataBuf->bfInfo.lastByte > 0);
assert(pArChanDesc->pDataBuf->bfInfo.lastByte < 2000);
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pDataBuf);
return OK;
error:
return ERROR;
}
/*+/subr**********************************************************************
* NAME arCFChanWriteGR - write a channel access DBR_GR buffer to a channel
*
* DESCRIPTION
* This routine writes DBR_GR_xxx information to the chanHdr for a
* channel.
*
* RETURNS
* OK, or
* ERROR
*
* BUGS
* o text
*
* NOTES
* 1. This routine does NOT write a value to the file.
*
*-*/
long
arCFChanWriteGR(pArChanDesc, pArg)
AR_CHAN_DESC *pArChanDesc; /* IO pointer to channel descriptor */
struct event_handler_args *pArg;/* I pointer to monitor structure */
{
void *pCaBuf; /* pointer to buffer */
int i;
long stat;
/*----------------------------------------------------------------------------
* get some stuff out of the structure supplied as input pArg
*---------------------------------------------------------------------------*/
pCaBuf = pArg->dbr;
assert(pArChanDesc != NULL);
assert(pArChanDesc->pMIBuf != NULL);
assert(pCaBuf != NULL);
assert(dbr_type_is_GR(pArg->type));
if (pArg->type == DBR_GR_STRING) {
ArCDChanHdr(pArChanDesc).chanType = DBF_STRING;
ArCDChanHdr(pArChanDesc).elCount = pArg->count;
}
else if (pArg->type == DBR_GR_SHORT) {
#define ArCWShrtGr ArCDChanHdr(pArChanDesc).graphics.shortGr
#define ArCWgr_short ((struct dbr_gr_short *)pCaBuf)
ArCDChanHdr(pArChanDesc).chanType = DBF_SHORT;
ArCDChanHdr(pArChanDesc).elCount = pArg->count;
(void)strcpy(ArCWShrtGr.units, ArCWgr_short->units);
ArCWShrtGr.upDispLim = ArCWgr_short->upper_disp_limit;
ArCWShrtGr.lowDispLim = ArCWgr_short->lower_disp_limit;
ArCWShrtGr.upAlmLim = ArCWgr_short->upper_alarm_limit;
ArCWShrtGr.upWarnLim = ArCWgr_short->upper_warning_limit;
ArCWShrtGr.lowWarnLim = ArCWgr_short->lower_warning_limit;
ArCWShrtGr.lowAlmLim = ArCWgr_short->lower_alarm_limit;
}
else if (pArg->type == DBR_GR_FLOAT) {
#define ArCWFltGr ArCDChanHdr(pArChanDesc).graphics.floatGr
#define ArCWgr_float ((struct dbr_gr_float *)pCaBuf)
ArCDChanHdr(pArChanDesc).chanType = DBF_FLOAT;
ArCDChanHdr(pArChanDesc).elCount = pArg->count;
ArCWFltGr.precision = ArCWgr_float->precision;
(void)strcpy(ArCWFltGr.units, ArCWgr_float->units);
ArCWFltGr.upDispLim = ArCWgr_float->upper_disp_limit;
ArCWFltGr.lowDispLim = ArCWgr_float->lower_disp_limit;
ArCWFltGr.upAlmLim = ArCWgr_float->upper_alarm_limit;
ArCWFltGr.upWarnLim = ArCWgr_float->upper_warning_limit;
ArCWFltGr.lowWarnLim = ArCWgr_float->lower_warning_limit;
ArCWFltGr.lowAlmLim = ArCWgr_float->lower_alarm_limit;
}
else if (pArg->type == DBR_GR_LONG) {
#define ArCWLngGr ArCDChanHdr(pArChanDesc).graphics.longGr
#define ArCWgr_long ((struct dbr_gr_long *)pCaBuf)
ArCDChanHdr(pArChanDesc).chanType = DBF_LONG;
ArCDChanHdr(pArChanDesc).elCount = pArg->count;
(void)strcpy(ArCWLngGr.units, ArCWgr_long->units);
ArCWLngGr.upDispLim = ArCWgr_long->upper_disp_limit;
ArCWLngGr.lowDispLim = ArCWgr_long->lower_disp_limit;
ArCWLngGr.upAlmLim = ArCWgr_long->upper_alarm_limit;
ArCWLngGr.upWarnLim = ArCWgr_long->upper_warning_limit;
ArCWLngGr.lowWarnLim = ArCWgr_long->lower_warning_limit;
ArCWLngGr.lowAlmLim = ArCWgr_long->lower_alarm_limit;
}
else if (pArg->type == DBR_GR_DOUBLE) {
#define ArCWDblGr ArCDChanHdr(pArChanDesc).graphics.doubleGr
#define ArCWgr_double ((struct dbr_gr_double *)pCaBuf)
ArCDChanHdr(pArChanDesc).chanType = DBF_DOUBLE;
ArCDChanHdr(pArChanDesc).elCount = pArg->count;
ArCWDblGr.precision = ArCWgr_double->precision;
(void)strcpy(ArCWDblGr.units, ArCWgr_double->units);
ArCWDblGr.upDispLim = ArCWgr_double->upper_disp_limit;
ArCWDblGr.lowDispLim = ArCWgr_double->lower_disp_limit;
ArCWDblGr.upAlmLim = ArCWgr_double->upper_alarm_limit;
ArCWDblGr.upWarnLim = ArCWgr_double->upper_warning_limit;
ArCWDblGr.lowWarnLim = ArCWgr_double->lower_warning_limit;
ArCWDblGr.lowAlmLim = ArCWgr_double->lower_alarm_limit;
}
else if (pArg->type == DBR_GR_ENUM) {
/*----------------------------------------------------------------------------
* Store the graphics info into the graphics buffer, set the modified
* bit for the buffer, and also set lastByte for the disk block.
*---------------------------------------------------------------------------*/
#define ArCWEnmGr ArCDChanHdr(pArChanDesc).graphics.enumGr.pGRBuf
#define ArCWgr_enum ((struct dbr_gr_enum *)pCaBuf)
if (ArCDChanHdr(pArChanDesc).chanType == DBF_ENUM) {
assert(ArCWEnmGr != NULL); /* better have a buffer */
assert(ArCWEnmGr->blkNum != 0); /* it must contain a block */
}
else {
ArCDChanHdr(pArChanDesc).chanType = DBF_ENUM;
if ((stat = arCF_GRGetBlock(pArChanDesc)) != OK)
assertAlways(0);
}
ArCDChanHdr(pArChanDesc).elCount = pArg->count;
ArCWEnmGr->numStrings = ArCWgr_enum->no_str;
for (i=0; i<ArCWEnmGr->numStrings; i++)
(void)strcpy(ArCWEnmGr->states[i], ArCWgr_enum->strs[i]);
ArCWEnmGr->bfInfo.lastByte = BF_BLOCK_DATA + 2 +
ArCWEnmGr->numStrings * db_state_text_dim - 1;
stat = bfWrite(pArChanDesc->pArCfDesc->pBfDesc,
&ArCWEnmGr->bfInfo, ArCWEnmGr->blkNum);
if (stat != OK) {
(void)fprintf(stderr, "arCFWriteGR: error writing GR block\n");
assertAlways(0);
}
}
else if (pArg->type == DBR_GR_CHAR) {
#define ArCWChrGr ArCDChanHdr(pArChanDesc).graphics.charGr
#define ArCWgr_char ((struct dbr_gr_char *)pCaBuf)
ArCDChanHdr(pArChanDesc).chanType = DBF_CHAR;
ArCDChanHdr(pArChanDesc).elCount = pArg->count;
(void)strcpy(ArCWChrGr.units, ArCWgr_char->units);
ArCWChrGr.upDispLim = ArCWgr_char->upper_disp_limit;
ArCWChrGr.lowDispLim = ArCWgr_char->lower_disp_limit;
ArCWChrGr.upAlmLim = ArCWgr_char->upper_alarm_limit;
ArCWChrGr.upWarnLim = ArCWgr_char->upper_warning_limit;
ArCWChrGr.lowWarnLim = ArCWgr_char->lower_warning_limit;
ArCWChrGr.lowAlmLim = ArCWgr_char->lower_alarm_limit;
}
else {
assertAlways(0);
}
ArCFModifySet(pArChanDesc->pArCfDesc, pArChanDesc->pMIBuf);
return OK;
}
/*+/subr**********************************************************************
* NAME arCFClose - close an AR channel data file
*
* DESCRIPTION
* Closes an AR channel data file. If the file was open in O_RDWR
* mode, then buffers are flushed and the lock file is deleted.
*
* RETURNS
* OK, or
* ERROR
*
* BUGS
* o text
*
* SEE ALSO
* arCFOpen(), arCFCreate()
*
* EXAMPLE
*
*-*/
long
arCFClose(ppArCfDesc)
AR_CF_DESC **ppArCfDesc; /* IO ptr to ptr to channel file descriptor */
{
int stat; /* status from calls */
int retStat=OK; /* return status */
AR_CF_DESC *pCfDesc; /* pointer to channel file descriptor */
AR_CHAN_DESC *pChanDesc; /* pointer to channel descriptor */
assert(ppArCfDesc != NULL);
assert(*ppArCfDesc != NULL);
pCfDesc = *ppArCfDesc;
if (pCfDesc->pChanDescHead != NULL) {
assert(pCfDesc->pBfDesc != NULL);
assert(pCfDesc->pChanDescTail != NULL);
/* close all the channel descriptors */
while ((pChanDesc = pCfDesc->pChanDescHead) != NULL) {
stat = arCFChanClose(&pChanDesc);
}
}
assert(pCfDesc->pChanDescHead == NULL); /* should be closed */
assert(pCfDesc->pChanDescTail == NULL);
if (pCfDesc->pMIHead != NULL) {
assert(pCfDesc->pBfDesc != NULL);
assert(pCfDesc->pMITail != NULL);
/*----------------------------------------------------------------------------
* check all MI blocks for "MODIFIED" bit, and write those with it set;
* ALL MI blocks will be free()'d. When the entire MI block list has
* been traversed, both MIHead and MITail in the channel file descriptor
* will be null.
*
* As part of the traversal, check each chanHdr for DBF_ENUM channels.
* The GR block may need to be written, and the buffer will need to
* be free()'d.
*---------------------------------------------------------------------------*/
while (pCfDesc->pMIHead != NULL) {
AR_MI_BUF *pMIBuf; /* pointer to MI block */
AR_GR_BUF *pGRBuf; /* pointer to GR block */
int i;
pMIBuf = pCfDesc->pMIHead;
for (i=0; i<AR_CF_NHDR; i++) {
if (pMIBuf->chanHdr[i].name[0] == '\0')
; /* no action */
else if (pMIBuf->chanHdr[i].chanType == DBF_ENUM &&
(pGRBuf=pMIBuf->chanHdr[i].graphics.enumGr.pGRBuf) != NULL) {
if (ArCFModifyTestAndReset(pCfDesc, pGRBuf)) {
stat = bfWrite(pCfDesc->pBfDesc,
&pGRBuf->bfInfo, pGRBuf->blkNum);
if (stat != OK) {
(void)fprintf(stderr,
"arCFClose: error writing GR block\n");
retStat = ERROR;
}
}
arCF_freeGR_BUF(pGRBuf);
pMIBuf->chanHdr[i].graphics.enumGr.pGRBuf = NULL;
}
}
pCfDesc->pMIHead = pMIBuf->pNextMI;
if ((stat = arCF_MIFlush(pCfDesc, pMIBuf)) != OK) {
(void)fprintf(stderr, "arCFClose: error writing MI block\n");
retStat = ERROR;
}
arCF_freeMI_BUF(pMIBuf);
}
pCfDesc->pMITail = NULL;
}
assert(pCfDesc->pMIHead == NULL);
assert(pCfDesc->pMITail == NULL);
if (pCfDesc->pBfDesc != NULL) {
stat = bfClose(&(*ppArCfDesc)->pBfDesc);
if (stat != OK) {
(void)fprintf(stderr, "arCFClose: error closing block file\n");
retStat = ERROR;
}
}
arCF_freeCF_DESC(*ppArCfDesc);
*ppArCfDesc = NULL;
return retStat;
}
/*+/subr**********************************************************************
* NAME arCFCreate - create an AR channel data file
*
* DESCRIPTION
* Creates an empty 'block file' of type BF_TYPE_CHAN_DATA. The
* maximum size for the file will be set at the number of blocks
* which will fit with the caller specified number of bytes. The
* initial size for the file is 1 block.
*
* If the create is successful, then the file is open in O_RDWR mode
* when control returns to the caller. The channel file descriptor
* and other structures have been set up as would be done by arCFOpen().
* In addition, a lock file has been created to prevent multiple
* writers.
*
* The block size is forced to be AR_CF_BLKSIZE bytes.
*
* RETURNS
* pointer to channel file descriptor, or
* NULL if the file already exists or if an error occurs on create
*
* BUGS
* o no exit handler is established (see arCFOpen() )
*
* SEE ALSO
* arCFOpen(), arCFClose(), arCFChanOpen()
*
*-*/
AR_CF_DESC *
arCFCreate(name, maxBytes)
char *name; /* I file name */
int maxBytes; /* I maximum number of bytes for file */
{
int stat=OK; /* status from calls */
BF_BLKNUM maxBlocks; /* size limit */
BF_DESC *pDesc; /* pointer to block file descriptor */
AR_CF_DESC *pArCfDesc=NULL;/* pointer to channel file descriptor */
assert(strlen(name) < GEN_FNAME_DIM);
assert(strlen(name) > 0);
/*----------------------------------------------------------------------------
* allocate memory for an AR channel file descriptor and initialize the
* descriptor. Then create the block file and put into the channel file
* descriptor a pointer to the block file descriptor. This hides from
* most users the fact that there are actually two descriptors--they
* just use the channel file descriptor.
*---------------------------------------------------------------------------*/
if ((pArCfDesc = arCF_mallocCF_DESC()) == NULL) {
(void)fprintf(stderr, "arCFCreate: can't malloc for arCfDesc\n");
stat = ERROR;
}
else {
pArCfDesc->pBfDesc = NULL;
pArCfDesc->pMIHead = NULL; /* no MI buffers yet */
pArCfDesc->pMITail = NULL;
pArCfDesc->pMIFree = NULL;
pArCfDesc->MIFree_hdrNum = 0;
pArCfDesc->pChanDescHead = NULL; /* no channel desc exist yet */
pArCfDesc->pChanDescTail = NULL;
pArCfDesc->pWriteHead = NULL;
pArCfDesc->pWriteTail = NULL;
pArCfDesc->b0Modified = 0;
}
if (maxBytes < AR_CF_BLKSIZE) {
(void)printf("nBytes must be at least %d\n", AR_CF_BLKSIZE);
stat = ERROR;
}
if (stat == OK) {
maxBlocks = maxBytes / AR_CF_BLKSIZE;
pDesc = bfCreate(name, BF_TYPE_CHAN_DATA,
1, AR_CF_BLKSIZE, BF_FLAG_GROW, maxBlocks);
if (pDesc == NULL)
stat = ERROR;
else
pArCfDesc->pBfDesc = pDesc;
}
if (stat == OK) {
/*----------------------------------------------------------------------------
* create has succeeded; initialize userInfo in block0
*---------------------------------------------------------------------------*/
ArB0MIHead(pArCfDesc) = 0;
ArB0MITail(pArCfDesc) = 0;
ArB0MINblk(pArCfDesc) = 0;
ArB0MIFree_blkNum(pArCfDesc) = 0;
ArB0MIFree_hdrNum(pArCfDesc) = 0;
ArB0FormatRev(pArCfDesc) = AR_CF_FORMAT_REV;
if ((stat = bfWrite(pDesc, pDesc->pBlock0, 0)) != OK)
(void)printf("arCFCreate: error writing block0\n");
}
if (stat != OK) {
if (pArCfDesc != NULL) {
/*----------------------------------------------------------------------------
* ERROR has occurred; clean up whatever structure has been built and close
* the file.
*---------------------------------------------------------------------------*/
(void)arCFClose(&pArCfDesc);
assert(pArCfDesc == NULL);
}
}
return pArCfDesc;
}
/*+/subr**********************************************************************
* NAME arCFOpen - open an AR channel data file
*
* DESCRIPTION
* Opens an existing AR channel data file (a 'block file' of type
* BF_TYPE_CHAN_DATA) in the specified mode, O_RDONLY or O_RDWR. If
* the mode is O_RDWR, then a lock file is created to prevent multiple
* writers of the file.
*
* Upon a successful open, the master index blocks have been read into
* memory (and the channel file descriptor has the appropriate pointers).
*
* The block size in the file must be AR_CF_BLKSIZE. The file
* `format revision number' must match the `format revision number'
* the code was compiled with.
*
* RETURNS
* pointer to channel file descriptor, or
* NULL
*
* BUGS
* o lock file handling is somewhat `ratty', especially under VxWorks;
* there is a slim chance that multiple writers could occur.
* o no exit handler is established, which means that AR_CFClose()
* isn't automatically called. This means that buffers aren't flushed
* and the lock file isn't deleted if the program aborts.
*
* SEE ALSO
* arCFCreate(), arCFClose(), arCFChanOpen()
*
* EXAMPLE
*
*-*/
AR_CF_DESC *
arCFOpen(name, mode)
char *name; /* I file name */
int mode; /* I mode to use for open--O_RDONLY or O_RDWR */
{
int stat=OK; /* status from calls */
BF_DESC *pDesc=NULL; /* pointer to block file descriptor */
AR_CF_DESC *pArCfDesc=NULL;/* pointer to channel file descriptor */
assert(strlen(name) < GEN_FNAME_DIM);
assert(strlen(name) > 0);
assert(mode == O_RDONLY || mode == O_RDWR);
(void)arCSCheck(); /* check control system 'compatibility' */
(void)arCFAsserts(); /* check control system 'compatibility' */
/*----------------------------------------------------------------------------
* allocate memory for an AR channel file descriptor and initialize the
* descriptor. Then open the block file and put into the channel file
* descriptor a pointer to the block file descriptor. This hides from
* most users the fact that there are actually two descriptors--they
* just use the channel file descriptor.
*---------------------------------------------------------------------------*/
if ((pArCfDesc = arCF_mallocCF_DESC()) == NULL) {
(void)fprintf(stderr, "arCFOpen: can't malloc for arCfDesc\n");
stat = ERROR;
}
else {
pArCfDesc->pBfDesc = NULL;
pArCfDesc->pMIHead = NULL; /* no MI buffers yet */
pArCfDesc->pMITail = NULL;
pArCfDesc->pMIFree = NULL;
pArCfDesc->MIFree_hdrNum = 0;
pArCfDesc->pChanDescHead = NULL; /* no channel desc exist yet */
pArCfDesc->pChanDescTail = NULL;
pArCfDesc->pWriteHead = NULL;
pArCfDesc->pWriteTail = NULL;
pArCfDesc->b0Modified = 0;
}
if (stat == OK) {
if ((pDesc = bfOpen(name, mode, BF_TYPE_CHAN_DATA, 0)) == NULL)
stat = ERROR;
else
pArCfDesc->pBfDesc = pDesc;
}
if (stat == OK) {
if (BfB0BlockSize(pDesc) != AR_CF_BLKSIZE) {
(void)fprintf(stderr, "arCfOpen: block size mismatch\n");
stat = ERROR;
}
}
if (stat == OK) {
if (ArB0FormatRev(pArCfDesc)/100 != AR_CF_FORMAT_REV/100) {
(void)fprintf(stderr,
"arCfOpen: file format mismatch--file %d, code %d\n",
ArB0FormatRev(pArCfDesc), AR_CF_FORMAT_REV);
stat = ERROR;
}
}
if (stat == OK) {
/*----------------------------------------------------------------------------
* so far, the open is going OK. Do any additional processing needed.
*---------------------------------------------------------------------------*/
stat = arCFReadMIBuffers(pArCfDesc);
}
if (stat != OK) {
if (pArCfDesc != NULL) {
/*----------------------------------------------------------------------------
* ERROR has occurred; clean up whatever structure has been built and close
* the file.
*---------------------------------------------------------------------------*/
(void)arCFClose(&pArCfDesc);
assert(pArCfDesc == NULL);
}
}
return pArCfDesc;
}
/*+/subr**********************************************************************
* NAME arCFReadMIBuffers - read (or re-read) the MI blocks
*
* DESCRIPTION
*
* RETURNS
*
* BUGS
* o doesn't have any protection against changes in linked lists while
* this routine is running
* o doesn't handle deleting channels or purging data (i.e., deleting
* .datInfo items or entire index blocks)
* o comments are out of date--they don't describe re-read of MI blocks
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
arCFReadMIBuffers(pArCfDesc)
AR_CF_DESC *pArCfDesc; /* I ptr to channel file descriptor */
{
int retStat=OK; /* return status to caller */
int stat; /* status return from calls */
BF_BLKNUM blockNum; /* block number */
AR_MI_BUF *pMIBuf; /* ptr to buffer for block */
int i;
AR_MI_BUF MIBuf; /* temporary buffer for re-read of MI block */
if (ArB0MIHead(pArCfDesc) == 0)
return OK;
/*----------------------------------------------------------------------------
* the file has some Master Index blocks; read ALL OF THEM into memory.
*
* For each MI block, get a buffer, link the buffer into the MI buffer
* list, put the block number into the buffer, reset the buffer's flags,
* and, finally, read the block into the buffer. Once the block is in
* the buffer, do some initialization for each chanHdr. This
* initialization includes reading the blocks for DBF_ENUM channels.
*
* If block0 points to an MIFree, then check each MI block to see if it
* is the one pointed to; when a match is found, initialize the MIFree
* information in the channel file descriptor.
*---------------------------------------------------------------------------*/
blockNum = ArB0MIHead(pArCfDesc);
while (blockNum > 0) {
pMIBuf = pArCfDesc->pMIHead;
while (pMIBuf != NULL) {
if (pMIBuf->blkNum == blockNum)
break;
pMIBuf = pMIBuf->pNextMI;
}
if (pMIBuf == NULL) {
if ((pMIBuf = arCF_mallocMI_BUF()) == NULL) {
(void)fprintf(stderr, "arCFOpen: can't malloc for MI block\n");
stat = ERROR;
goto readMIError;
}
if (pArCfDesc->pMIHead == NULL) /* first block? */
pArCfDesc->pMIHead = pMIBuf; /* yes; link to head */
else
pArCfDesc->pMITail->pNextMI = pMIBuf; /* no; link from prev */
pArCfDesc->pMITail = pMIBuf; /* this is now tail */
pMIBuf->blkNum = blockNum;
pMIBuf->pNextMI = NULL;
ArCFModifyInit(pMIBuf);
pMIBuf->pBlock = &pMIBuf->bfInfo;
stat = bfRead(pArCfDesc->pBfDesc, &pMIBuf->bfInfo, blockNum);
if (stat != OK) {
(void)fprintf(stderr, "arCFOpen: error reading MI block\n");
stat = ERROR;
goto readMIError;
}
for (i=0; i<AR_CF_NHDR; i++) {
pMIBuf->chanHdr[i].count = 0;
pMIBuf->chanHdr[i].flags = 0;
if (pMIBuf->chanHdr[i].name[0] != '\0' &&
pMIBuf->chanHdr[i].chanType == DBF_ENUM) {
pMIBuf->chanHdr[i].graphics.enumGr.pGRBuf = NULL;
stat = arCF_GRReadBlock(pArCfDesc, &pMIBuf->chanHdr[i]);
if (stat != OK) {
(void)fprintf(stderr,
"arCFOpen: error reading GR block for ENUM\n");
stat = ERROR;
goto readMIError;
}
}
}
}
else {
stat = bfRead(pArCfDesc->pBfDesc, &MIBuf.bfInfo, blockNum);
if (stat != OK) {
(void)fprintf(stderr, "arCFOpen: error reading MI block\n");
stat = ERROR;
goto readMIError;
}
pMIBuf->bfInfo.flink = MIBuf.bfInfo.flink;
for (i=0; i<AR_CF_NHDR; i++) {
if (MIBuf.chanHdr[i].name[0] != '\0') {
assertAlways(strcmp(pMIBuf->chanHdr[i].name,
MIBuf.chanHdr[i].name) == 0);
pMIBuf->chanHdr[i].indexTail = MIBuf.chanHdr[i].indexTail;
pMIBuf->chanHdr[i].newestSecPastEpoch =
MIBuf.chanHdr[i].newestSecPastEpoch;
pMIBuf->chanHdr[i].newestNsec =
MIBuf.chanHdr[i].newestNsec;
}
else {
pMIBuf->chanHdr[i].graphics.nextMIFree =
MIBuf.chanHdr[i].graphics.nextMIFree;
}
}
}
if (ArB0MIFree_blkNum(pArCfDesc) == blockNum) {
pArCfDesc->pMIFree = pMIBuf;
pArCfDesc->MIFree_hdrNum = ArB0MIFree_hdrNum(pArCfDesc);
}
blockNum = pMIBuf->bfInfo.flink; /* next MI block */
}
return OK;
readMIError:
return ERROR;
}
/*+/subr**********************************************************************
* NAME arCFSyncRead - synchronize read buffers in memory with disk
*
* DESCRIPTION
*
* RETURNS
*
* BUGS
* o text
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
arCFSyncRead(pArCfDesc)
AR_CF_DESC *pArCfDesc; /* I ptr to channel file descriptor */
{
int retStat=OK; /* return status to caller */
int stat; /* status return from calls */
int readOnly; /* channel file is opened read-only */
AR_CHAN_DESC *pChanDesc;
BF_DESC *pBfDesc;
assert(pArCfDesc != NULL);
assert(pArCfDesc->pBfDesc != NULL);
pBfDesc = pArCfDesc->pBfDesc;
readOnly = ((pBfDesc->flags & BF_DESC_WRITE) == 0);
if (readOnly) {
stat = bfRead(pBfDesc, pBfDesc->pBlock0, 0);
assertAlways(stat == OK);
stat = arCFReadMIBuffers(pArCfDesc);
assertAlways(stat == OK);
}
pChanDesc = pArCfDesc->pChanDescHead;
while (pChanDesc != NULL) {
if ((pChanDesc->flags & AR_CDESC_WRITE) == 0) {
if (pChanDesc->pIndexBuf != NULL &&
pChanDesc->pIndexBuf->bfInfo.flink == 0) {
stat = bfRead(pBfDesc, &pChanDesc->pIndexBuf->bfInfo,
pChanDesc->pIndexBuf->blkNum);
assertAlways(stat == OK);
}
if (pChanDesc->pDataBuf != NULL &&
pChanDesc->pDataBuf->bfInfo.flink == 0) {
stat = bfRead(pBfDesc, &pChanDesc->pDataBuf->bfInfo,
pChanDesc->pDataBuf->blkNum);
assertAlways(stat == OK);
pChanDesc->remainCount =
pChanDesc->pData-(char *)&pChanDesc->pDataBuf->bfInfo -
pChanDesc->pDataBuf->bfInfo.firstByte;
}
}
pChanDesc = pChanDesc->pNextDesc;
}
return retStat;
}
/*+/subr**********************************************************************
* NAME arCFSyncWrite - synchronize disk with write buffers in memory
*
* DESCRIPTION
*
* RETURNS
* OK
*
* BUGS
* o doesn't lock for list and write operations
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
arCFSyncWrite(pArCfDesc)
AR_CF_DESC *pArCfDesc; /* I ptr to channel file descriptor */
{
int retStat=OK; /* return status to caller */
int stat; /* status return from calls */
AR_MI_BUF *pBuf; /* preamble is same for all types of buffer */
int modified=0;
assert(pArCfDesc != NULL);
while ((pBuf = pArCfDesc->pWriteHead) != NULL) {
modified++;
if (ArCFModifyTestAndReset(pArCfDesc, pBuf)) {
stat = bfWrite(pArCfDesc->pBfDesc, &pBuf->bfInfo, pBuf->blkNum);
assertAlways(stat == OK);
}
}
if (modified) {
pArCfDesc->b0Modified = 0;
stat = bfWrite(pArCfDesc->pBfDesc, pArCfDesc->pBfDesc->pBlock0, 0);
if (stat != OK)
retStat = ERROR;
}
return retStat;
}