2916 lines
102 KiB
C
2916 lines
102 KiB
C
/* $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;
|
||
}
|