/* $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 some general use definitions * #include definitions for time stamps * #include definitions for Channel Access * #include definitions for database related items * #include structures for AR access library routines * #include 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 #ifndef INC_genDefs_h # include #endif #ifndef INC_tsDefs_h # include #endif #ifndef INC_arCS_h # include #endif #ifndef INCLcadefh # include #endif #ifndef INCLdb_accessh # include #endif #include #include /* for O_RDWR and O_RDONLY definitions */ #include /*+/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; inumStrings; 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) *(++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; inumStrings; 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; ichanHdr[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; ichanHdr[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; ichanHdr[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; }