1095 lines
32 KiB
C
1095 lines
32 KiB
C
/* $Id$
|
||
* Author: Roger A. Cole
|
||
* Date: 12-04-90
|
||
*
|
||
* Experimental Physics and Industrial Control System (EPICS)
|
||
*
|
||
* Copyright 1991-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:
|
||
* -----------------
|
||
* .00 12-04-90 rac initial version
|
||
* .01 06-18-91 rac installed in SCCS
|
||
* .02 06-19-91 rac replace <fields.h> with <alarm.h>
|
||
* .03 08-14-91 rac
|
||
* .04 02-27-92 rac do ts rounding here instead of sydSubr.c
|
||
* .05 08-18-92 rac add SYD_FC_STOP function code
|
||
* .06 09-09-92 rac allow reading text values such as `no data'; look at
|
||
* the _end_ of the file to do name searches; handle
|
||
* channels which are missing and/or disconnected;
|
||
* discontinue use of special malloc routines
|
||
*
|
||
* make options
|
||
* -DvxWorks makes a version for VxWorks
|
||
* -DNDEBUG don't compile assert() checking
|
||
* -DDEBUG compile various debug code
|
||
*/
|
||
/*+/mod***********************************************************************
|
||
* TITLE sydSubrSSF.c - acquire synchronous samples from AR `sample set' files
|
||
*
|
||
* DESCRIPTION
|
||
*
|
||
*-***************************************************************************/
|
||
#include <genDefs.h>
|
||
#define SYD_PRIVATE
|
||
#include <sydDefs.h>
|
||
#include <alarm.h>
|
||
#ifndef INC_tsDefs_h
|
||
# include <tsDefs.h>
|
||
#endif
|
||
#ifndef INCLcadefh
|
||
# include <cadef.h>
|
||
#endif
|
||
|
||
#ifdef vxWorks
|
||
# include <vxWorks.h>
|
||
# include <ioLib.h> /* for O_RDWR and O_RDONLY definitions */
|
||
# include <stdioLib.h>
|
||
# include <strLib.h>
|
||
#else
|
||
# include <stdio.h>
|
||
# include <sys/file.h> /* for O_RDWR and O_RDONLY definitions */
|
||
# include <string.h>
|
||
# include <strings.h>
|
||
#endif
|
||
|
||
long sydSSFFunc();
|
||
static long sydSSFFuncGetGR();
|
||
static long sydSSFFuncReadData();
|
||
static long sydSSFFuncSeekSample();
|
||
static long sydSSFFuncSeekStamp();
|
||
|
||
/*+/macro*********************************************************************
|
||
* NAME posXxx - text file search and positioning routines
|
||
*
|
||
* DESCRIPTION
|
||
* These routines allow setting the position for text files. The
|
||
* file may be positioned at its beginning or its end. It is
|
||
* supported to search, either forward or backward, for a string,
|
||
* leaving the file positioned at the beginning of the string.
|
||
*
|
||
* long pos;
|
||
* FILE *pStream;
|
||
*
|
||
* pos = posBOF(pStream);
|
||
* pos = posEOF(pStream);
|
||
* pos = posKeyFwd("key", pStream);
|
||
* pos = posKeyRev("key", pStream, pos);
|
||
* pos = posPos(pStream, pos);
|
||
*
|
||
* RETURNS
|
||
* position, or
|
||
* <0 if an error occurs
|
||
*
|
||
* BUGS
|
||
* o the use of fseek and ftell doesn't match the Standard C specification
|
||
* and may be non-portable. They have been verified to function
|
||
* properly under SunOS 4.1 and VxWorks 4.0.2.
|
||
*
|
||
*-*/
|
||
static long posBOF();
|
||
static long posEOF();
|
||
static long posKeyFwd();
|
||
static long posKeyRev();
|
||
static long posPos();
|
||
|
||
/*+/internal******************************************************************
|
||
* NAME posBOF - position file at the beginning
|
||
*
|
||
*-*/
|
||
static long
|
||
posBOF(pStream)
|
||
FILE *pStream;
|
||
{
|
||
if (fseek(pStream, 0L, 0) < 0) {
|
||
perror("posBOF: fseek error");
|
||
return -1;
|
||
}
|
||
return ftell(pStream);
|
||
}
|
||
/*+/internal******************************************************************
|
||
* NAME posEOF - position file at the end
|
||
*
|
||
*-*/
|
||
static long
|
||
posEOF(pStream)
|
||
FILE *pStream;
|
||
{
|
||
if (fseek(pStream, 0L, 2) < 0) {
|
||
perror("posEOF: fseek error");
|
||
return -1;
|
||
}
|
||
return ftell(pStream);
|
||
}
|
||
/*+/internal******************************************************************
|
||
* NAME posKeyFwd - search forward for a string
|
||
*
|
||
* RETURNS
|
||
* position of key (its first character), or
|
||
* -1 if search fails
|
||
*
|
||
*-*/
|
||
static long
|
||
posKeyFwd(key, pStream)
|
||
char *key;
|
||
FILE *pStream;
|
||
{
|
||
int i, lastChar;
|
||
int c;
|
||
long pos;
|
||
|
||
lastChar = strlen(key) - 1;
|
||
i = 0;
|
||
|
||
if ((pos = ftell(pStream)) < 0) {
|
||
if (!feof(pStream))
|
||
perror("posKeyFwd: ftell error");
|
||
return -1;
|
||
}
|
||
|
||
while (1) {
|
||
if ((c = fgetc(pStream)) == EOF)
|
||
return -1;
|
||
if ((char)c == key[i]) {
|
||
if (i == lastChar)
|
||
break;
|
||
i++;
|
||
}
|
||
else {
|
||
if (i == 0) {
|
||
if ((pos = ftell(pStream)) < 0) {
|
||
if (!feof(pStream))
|
||
perror("posKeyFwd: ftell error");
|
||
return -1;
|
||
}
|
||
}
|
||
else if (i == 1) {
|
||
pos++;
|
||
if ((char)c != key[0])
|
||
i = 0;
|
||
}
|
||
else {
|
||
pos++;
|
||
if (fseek(pStream, pos, 0) != 0) {
|
||
if (!feof(pStream))
|
||
perror("posKeyFwd: fseek error");
|
||
return -1;
|
||
}
|
||
i = 0;
|
||
}
|
||
}
|
||
}
|
||
if (fseek(pStream, pos, 0) != 0) {
|
||
if (!feof(pStream))
|
||
perror("posKeyFwd: fseek error");
|
||
return -1;
|
||
}
|
||
return pos;
|
||
}
|
||
/*+/internal******************************************************************
|
||
* NAME posKeyRev - search backward for a string
|
||
*
|
||
* RETURNS
|
||
* position of key (its first character), or
|
||
* -1 if search fails
|
||
*
|
||
*-*/
|
||
static long
|
||
posKeyRev(key, pStream, lastPos)
|
||
char *key;
|
||
FILE *pStream;
|
||
long lastPos; /* I position to start search; -1 for EOF */
|
||
{
|
||
int i, lastChar;
|
||
int c;
|
||
long pos;
|
||
|
||
lastChar = i = strlen(key) - 1;
|
||
|
||
if (lastPos >= 0) {
|
||
if (fseek(pStream, lastPos, 0) != 0) {
|
||
if (!feof(pStream))
|
||
perror("posKeyRev: fseek error");
|
||
return -1;
|
||
}
|
||
}
|
||
else {
|
||
if (fseek(pStream, 0L, 2) != 0) {
|
||
if (!feof(pStream))
|
||
perror("posKeyRev: fseek error");
|
||
return -1;
|
||
}
|
||
}
|
||
while (1) {
|
||
if (ftell(pStream) <= 0)
|
||
return -1;
|
||
if (fseek(pStream, -1L, 1) != 0) {
|
||
if (!feof(pStream))
|
||
perror("posKeyRev: fseek error");
|
||
return -1;
|
||
}
|
||
if ((c = fgetc(pStream)) == EOF)
|
||
return -1;
|
||
if (fseek(pStream, -1L, 1) != 0) {
|
||
if (!feof(pStream))
|
||
perror("posKeyRev: fseek error");
|
||
return -1;
|
||
}
|
||
if ((char)c == key[i]) {
|
||
if (i == 0)
|
||
break;
|
||
i--;
|
||
}
|
||
else
|
||
i = lastChar;
|
||
}
|
||
if ((pos = ftell(pStream)) < 0)
|
||
perror("posKeyRev: ftell error");
|
||
return pos;
|
||
}
|
||
/*+/internal******************************************************************
|
||
* NAME posPos - set file to desired position
|
||
*
|
||
*-*/
|
||
static long
|
||
posPos(pStream, pos)
|
||
FILE *pStream;
|
||
long pos;
|
||
{
|
||
if (fseek(pStream, pos, 0) < 0) {
|
||
perror("posPos: fseek error");
|
||
return -1;
|
||
}
|
||
return ftell(pStream);
|
||
}
|
||
|
||
/*+/subr**********************************************************************
|
||
* NAME sydOpenSSF
|
||
*
|
||
*-*/
|
||
long
|
||
sydOpenSSF(ppSspec, filePath)
|
||
SYD_SPEC **ppSspec; /* O pointer to synchronous set spec pointer */
|
||
char *filePath; /* I path name for `sample set' archive file */
|
||
{
|
||
long stat;
|
||
|
||
assert(ppSspec != NULL);
|
||
|
||
if ((*ppSspec = (SYD_SPEC *)malloc(sizeof(SYD_SPEC))) == NULL)
|
||
return S_syd_noMem;
|
||
(*ppSspec)->pFunc = sydSSFFunc;
|
||
(*ppSspec)->type = SYD_TY_SSF;
|
||
if ((stat = sydSSFFunc(*ppSspec, NULL,SYD_FC_INIT,filePath)) != S_syd_OK){
|
||
free((char *)*ppSspec);
|
||
*ppSspec = NULL;
|
||
return stat;
|
||
}
|
||
(*ppSspec)->nInBufs = 2;
|
||
return sydOpen(ppSspec);
|
||
}
|
||
/*+/subr**********************************************************************
|
||
* NAME sydSSFFunc - handle "sample set" data file interactions
|
||
*
|
||
* DESCRIPTION
|
||
*
|
||
* sydSSFFunc(pSspec, NULL, SYD_FC_INIT, filePath) open "sample set" file
|
||
* sydSSFFunc(pSspec, pSChan, SYD_FC_OPEN, NULL) chanName already in pSChan
|
||
* sydSSFFunc(pSspec, pSChan, SYD_FC_READ, NULL)
|
||
* sydSSFFunc(pSspec, pSChan, SYD_FC_POSITION, &stamp)
|
||
* sydSSFFunc(pSspec, pSChan, SYD_FC_CLOSE, NULL)
|
||
* sydSSFFunc(pSspec, NULL, SYD_FC_FILEINFO, outStream)
|
||
* sydSSFFunc(pSspec, NULL, SYD_FC_WRAPUP, NULL) close "sample set" file
|
||
*
|
||
* RETURNS
|
||
* S_syd_OK, or
|
||
* S_syd_EOF, or
|
||
* other code indicating error
|
||
*
|
||
* BUGS
|
||
* o doesn't detect or report EOF
|
||
* o doesn't do anything (or even detect) overwriting un-sampled
|
||
* buffers (for SYD_FC_READ)
|
||
* o needs a "get graphics information" function and function code
|
||
* o if a channel wasn't connected when the last snapshot in the file
|
||
* was written, its elCount is assumed to be 1
|
||
*
|
||
* SEE ALSO
|
||
*
|
||
* EXAMPLE
|
||
*
|
||
*-*/
|
||
long
|
||
sydSSFFunc(pSspec, pStruct, funcCode, pArg)
|
||
SYD_SPEC *pSspec; /* IO pointer to synchronous set spec */
|
||
void *pStruct; /* IO pointer to data struct used by funcCode */
|
||
enum sydFuncCode funcCode;/* I function code */
|
||
void *pArg; /* I pointer to arg, as required by funcCode */
|
||
{
|
||
SYD_CHAN *pSChan; /* pointer to syncSet channel descriptor */
|
||
FILE *ssFile; /* file pointer for sample set data file */
|
||
long retStat=S_syd_OK;
|
||
long stat;
|
||
int i;
|
||
long pos;
|
||
char record[120];
|
||
char recDelim;
|
||
char *pRecord;
|
||
char *pField;
|
||
char delim;
|
||
char chanName[db_name_dim+2];
|
||
chtype type;
|
||
int elCount;
|
||
|
||
pSChan = (SYD_CHAN *)pStruct;
|
||
if (pSspec != NULL)
|
||
ssFile = (FILE *)pSspec->pHandle;
|
||
|
||
if (funcCode == SYD_FC_INIT) {
|
||
if ((ssFile = fopen((char *)pArg, "r")) == NULL) {
|
||
(void)printf("couldn't open %s\n", (char *)pArg);
|
||
perror(NULL);
|
||
retStat = S_syd_ERROR;
|
||
}
|
||
pSspec->pHandle = (void *)ssFile;
|
||
}
|
||
else if (funcCode == SYD_FC_OPEN) {
|
||
if ((pos = posKeyRev("%endHeader%", ssFile, -1L)) < 0) {
|
||
(void)printf("couldn't find %%endHeader%%\n");
|
||
(void)posBOF(ssFile);
|
||
return S_syd_chanNotFound;
|
||
}
|
||
strcpy(chanName, "\n");
|
||
strcat(chanName, pSChan->name);
|
||
strcat(chanName, " ");
|
||
if ((pos = posKeyFwd(chanName, ssFile)) < 0)
|
||
return S_syd_chanNotFound;
|
||
assert(sizeof(record) == 120);
|
||
fgetc(ssFile); /* skip over the \n */
|
||
if (fgets(record, 120, ssFile) == NULL)
|
||
assertAlways(0);
|
||
pRecord = record;
|
||
recDelim = record[strlen(record)-1];
|
||
if (nextChanNameField(&pRecord, &pField, &delim) <= 1)
|
||
assertAlways(0);
|
||
if (strcmp(pField, pSChan->name) != 0)
|
||
assertAlways(0);
|
||
pSChan->pHandle = NULL;
|
||
if (nextAlphField(&pRecord, &pField, &delim) <= 1)
|
||
assertAlways(0);
|
||
dbf_text_to_type(pField, type);
|
||
if (type >= 0) {
|
||
pSChan->dbfType = type;
|
||
pSChan->dbrType = dbf_type_to_DBR_TIME(type);
|
||
pSChan->dbrGrType = dbf_type_to_DBR_GR(type);
|
||
stat = sscanf(pRecord, "%*d %*d %d", &elCount);
|
||
assert(stat == 1);
|
||
pSChan->elCount = elCount;
|
||
}
|
||
else {
|
||
pSChan->dbfType = TYPENOTCONN;
|
||
pSChan->dbrType = TYPENOTCONN;
|
||
pSChan->dbrGrType = TYPENOTCONN;
|
||
pSChan->elCount = 1;
|
||
}
|
||
sydSSFFuncGetGR(pSspec, pSChan);
|
||
pSChan->sync = SYD_SY_NONF; /* force channel to be sync */
|
||
(void)posBOF(ssFile);
|
||
}
|
||
else if (funcCode == SYD_FC_READ) {
|
||
TS_STAMP timeStamp;
|
||
enum sydBStatus bufStat, newStat;
|
||
int bufNum, oldBufNum, first=1;
|
||
char typeText[80];
|
||
|
||
/*-----------------------------------------------------------------------------
|
||
* find the next line with SAMPLE: and get the time stamp. When done:
|
||
* o bufStat will be one of:
|
||
* SYD_B_RESTART this sample starts a new "run" of data
|
||
* SYD_B_FULL this is an ordinary sample
|
||
* SYD_B_EOF end of file
|
||
* SYD_B_MISSING no value for this channel i nthis sample
|
||
* o the file will be positioned following the %endHeader% line
|
||
* o if time stamp rounding has been requested, it will be done
|
||
*----------------------------------------------------------------------------*/
|
||
while (1) {
|
||
newStat = SYD_B_EMPTY;
|
||
bufStat = sydSSFFuncSeekSample(ssFile, &timeStamp);
|
||
if (pSspec->roundNsec > 0)
|
||
sydTsRound(&timeStamp, pSspec->roundNsec);
|
||
|
||
/*-----------------------------------------------------------------------------
|
||
* now, read the actual data. This is done by processing all the data
|
||
* lines in the file, storing those which match a channel in the synchronous
|
||
* set. (This is somewhat contrary to the "advertising" for this function
|
||
* code, which implies that only a single channel is read.) When done:
|
||
* o all channels in the set will have an appropriate buffer status
|
||
* o all channels will have the same time stamp
|
||
* o all channels with data will have the data in the buffer
|
||
* o all channels will have .lastInBuf updated
|
||
*----------------------------------------------------------------------------*/
|
||
pSChan = pSspec->pChanHead;
|
||
if (first) {
|
||
oldBufNum = pSChan->lastInBuf;
|
||
bufNum = NEXT_INBUF(pSChan, oldBufNum);
|
||
first = 0;
|
||
}
|
||
while (pSChan != NULL) {
|
||
if (bufStat != SYD_B_EOF)
|
||
pSChan->inStatus[bufNum] = SYD_B_MISSING;
|
||
else
|
||
pSChan->inStatus[bufNum] = bufStat;
|
||
pSChan->pInBuf[bufNum]->tfltval.stamp = timeStamp;
|
||
pSChan->lastInBuf = bufNum;
|
||
if (pSChan->firstInBuf < 0)
|
||
pSChan->firstInBuf = bufNum;
|
||
pSChan = pSChan->pNext;
|
||
}
|
||
if (bufStat == SYD_B_EOF)
|
||
return S_syd_EOF;
|
||
while (1) {
|
||
stat = fscanf(ssFile, "%s", chanName);
|
||
assert(stat == 1);
|
||
if ((pSChan = sydChanFind(pSspec, chanName)) != NULL) {
|
||
(void)fscanf(ssFile, "%s", typeText);
|
||
dbf_text_to_type(typeText, type);
|
||
if (pSChan->dbfType == TYPENOTCONN && type >= 0) {
|
||
pSChan->dbfType = type;
|
||
pSChan->dbrType = dbf_type_to_DBR_TIME(type);
|
||
pSChan->dbrGrType = dbf_type_to_DBR_GR(type);
|
||
sydSSFFuncGetGR(pSspec, pSChan);
|
||
}
|
||
stat = sydSSFFuncReadData(pSChan, ssFile,
|
||
bufStat, bufNum, oldBufNum, type);
|
||
if (pSChan->inStatus[bufNum] != SYD_B_MISSING &&
|
||
newStat == SYD_B_EMPTY) {
|
||
newStat = pSChan->inStatus[bufNum];
|
||
}
|
||
}
|
||
else if (strcmp(chanName, "%endData%") == 0)
|
||
break;
|
||
else {
|
||
while ((delim = fgetc(ssFile)) != EOF && delim != '\n')
|
||
; /* keep skipping until end of line */
|
||
}
|
||
}
|
||
if (newStat != SYD_B_EMPTY)
|
||
break;
|
||
}
|
||
}
|
||
else if (funcCode == SYD_FC_STOP) {
|
||
}
|
||
else if (funcCode == SYD_FC_POSITION) {
|
||
TS_STAMP *pStamp;
|
||
TS_STAMP stamp, earlyStamp;
|
||
long earlyPos;
|
||
|
||
pStamp = (TS_STAMP *)pArg;
|
||
if (pStamp == NULL) {
|
||
(void)posBOF(ssFile);
|
||
return retStat;
|
||
}
|
||
/*-----------------------------------------------------------------------------
|
||
* find 2 adjacent samples to make it easy to determine which way to
|
||
* search. There are several cases:
|
||
* 1. the file is at least 2 away from EOF and both stamps can be found
|
||
* forward
|
||
* 2. the file is 1 away from EOF--1 stamp backward and 1 forward
|
||
* 3. the file contains no stamps
|
||
* 4. the file is at EOF and both stamps must be found backward
|
||
* 5. the file only contains one stamp
|
||
*
|
||
* Cases 2 and 4 are treated by rewinding the file to avoid any backward
|
||
* searching. This transforms those cases into one of the other cases.
|
||
*
|
||
* When this section is done, `earlyStamp' and `stamp' will contain two
|
||
* adjacent stamps. If they are equal, then case 4 exists. The file
|
||
* will be positioned following `stamp'.
|
||
*----------------------------------------------------------------------------*/
|
||
earlyPos = sydSSFFuncSeekStamp(ssFile, &earlyStamp, 0, 0L);
|
||
pos = sydSSFFuncSeekStamp(ssFile, &stamp, 0, 0L);
|
||
if (earlyPos < 0 || pos < 0) {
|
||
(void)posBOF(ssFile);
|
||
earlyPos = sydSSFFuncSeekStamp(ssFile, &earlyStamp, 0, 0L);
|
||
if ((pos = sydSSFFuncSeekStamp(ssFile, &stamp, 0, 0L)) < 0) {
|
||
stamp = earlyStamp;
|
||
pos = earlyPos;
|
||
}
|
||
}
|
||
if (earlyPos < 0)
|
||
return S_syd_ERROR;
|
||
if (TsCmpStampsLT(pStamp, &earlyStamp)) {
|
||
(void)posBOF(ssFile);
|
||
earlyPos = sydSSFFuncSeekStamp(ssFile, &earlyStamp, 0, 0L);
|
||
if ((pos = sydSSFFuncSeekStamp(ssFile, &stamp, 0, 0L)) < 0) {
|
||
stamp = earlyStamp;
|
||
pos = earlyPos;
|
||
}
|
||
}
|
||
if (earlyPos < 0)
|
||
return S_syd_ERROR;
|
||
/*-----------------------------------------------------------------------------
|
||
* now, find the record with the proper time stamp, going forward
|
||
*----------------------------------------------------------------------------*/
|
||
while (1) {
|
||
if (TsCmpStampsLE(pStamp, &earlyStamp))
|
||
break;
|
||
else if (TsCmpStampsLE(pStamp, &stamp) &&
|
||
TsCmpStampsGE(pStamp, &earlyStamp)) {
|
||
if (TsCmpStampsEQ(pStamp, &earlyStamp)) {
|
||
stamp = earlyStamp;
|
||
pos = earlyPos;
|
||
}
|
||
break;
|
||
}
|
||
else if (TsCmpStampsEQ(&earlyStamp, &stamp))
|
||
break;
|
||
else if (TsCmpStampsGT(pStamp, &stamp)) {
|
||
earlyStamp = stamp;
|
||
earlyPos = pos;
|
||
pos = sydSSFFuncSeekStamp(ssFile, &stamp, 0, earlyPos);
|
||
if (pos < 0) {
|
||
stamp = earlyStamp;
|
||
pos = earlyPos;
|
||
}
|
||
}
|
||
}
|
||
/*-----------------------------------------------------------------------------
|
||
* finally, position the file at the proper record
|
||
*----------------------------------------------------------------------------*/
|
||
if (TsCmpStampsGT(pStamp, &stamp))
|
||
return S_syd_EOF;
|
||
if (TsCmpStampsLE(pStamp, &earlyStamp))
|
||
(void)posPos(ssFile, earlyPos);
|
||
else
|
||
(void)posPos(ssFile, pos);
|
||
}
|
||
else if (funcCode == SYD_FC_CLOSE) {
|
||
; /* no action to close a channel in sample set files */
|
||
}
|
||
else if (funcCode == SYD_FC_FILEINFO) {
|
||
TS_STAMP oldestStamp;
|
||
char oldestStampText[28];
|
||
TS_STAMP newestStamp;
|
||
char newestStampText[28];
|
||
int j;
|
||
|
||
if (posBOF(ssFile) < 0) {
|
||
(void)fprintf((FILE *)pArg, "error positioning file\n");
|
||
return S_syd_ERROR;
|
||
}
|
||
if (sydSSFFuncSeekStamp(ssFile, &oldestStamp, 0, 0L) < 0) {
|
||
(void)fprintf((FILE *)pArg, "error finding oldest time stamp\n");
|
||
return S_syd_ERROR;
|
||
}
|
||
if (sydSSFFuncSeekStamp(ssFile, &newestStamp, 1, -1L) < 0) {
|
||
(void)fprintf((FILE *)pArg, "error finding newest time stamp\n");
|
||
return S_syd_ERROR;
|
||
}
|
||
(void)fprintf((FILE *)pArg, "data from %s through %s\n",
|
||
tsStampToText(&oldestStamp, TS_TEXT_MMDDYY, oldestStampText),
|
||
tsStampToText(&newestStamp, TS_TEXT_MMDDYY, newestStampText) );
|
||
if (posKeyRev("%endHeader%", ssFile, -1L) < 0) {
|
||
(void)fprintf((FILE *)pArg,
|
||
"error locating %%endHeader%% in file\n");
|
||
return S_syd_ERROR;
|
||
}
|
||
(void)fgets(record, 120, ssFile); /* skip over endHeader */
|
||
while (1) {
|
||
if (fgets(record, 120, ssFile) == NULL) {
|
||
(void)fprintf((FILE *)pArg, "error reading channel name\n");
|
||
return S_syd_ERROR;
|
||
}
|
||
pRecord = record;
|
||
recDelim = record[strlen(record)-1];
|
||
if (nextChanNameField(&pRecord, &pField, &delim) <= 1) {
|
||
(void)fprintf((FILE *)pArg, "error reading channel type\n");
|
||
return S_syd_ERROR;
|
||
}
|
||
if (strcmp(pField, "%endData%") == 0)
|
||
break;
|
||
(void)strcpy(chanName, pField);
|
||
if (nextAlphField(&pRecord, &pField, &delim) <= 1) {
|
||
(void)fprintf((FILE *)pArg, "error reading channel type\n");
|
||
return S_syd_ERROR;
|
||
}
|
||
if (sscanf(pRecord, "%*d %*d %d", &elCount) != 1) {
|
||
(void)fprintf((FILE *)pArg, "error reading element count\n");
|
||
return S_syd_ERROR;
|
||
}
|
||
j = strlen(chanName);
|
||
if (j > 4) {
|
||
if (strncmp(&chanName[j-4], ".VAL", 4) == 0)
|
||
chanName[j-4] = '\0';
|
||
}
|
||
|
||
(void)fprintf((FILE *)pArg, "%s %d %s\n",
|
||
chanName, elCount, pField);
|
||
if (recDelim != '\n') {
|
||
while ((delim = fgetc(ssFile)) != EOF && delim != '\n')
|
||
; /* keep skipping until end of line */
|
||
}
|
||
}
|
||
if (posBOF(ssFile) < 0) {
|
||
(void)fprintf((FILE *)pArg, "error positioning file\n");
|
||
return S_syd_ERROR;
|
||
}
|
||
}
|
||
else if (funcCode == SYD_FC_WRAPUP) {
|
||
if (ssFile != NULL)
|
||
(void)close(ssFile);
|
||
pSspec->pHandle = NULL;
|
||
}
|
||
|
||
return retStat;
|
||
}
|
||
|
||
/*+/internal******************************************************************
|
||
* NAME sydSSFFuncReadData
|
||
*
|
||
* DESCRIPTION
|
||
* Read data from the file into the sync set. An entire snapshot is
|
||
* read. For each channel in the snapshot, the data is stored in the
|
||
* sync set only if the channel is in the set.
|
||
*
|
||
* BUGS
|
||
* o if a channel has a smaller elCount in the file than in the sync set,
|
||
* then the `extra' values in the sync set will be garbage
|
||
*
|
||
*-*/
|
||
static long
|
||
sydSSFFuncReadData(pSChan, ssFile, bufStat, bufNum, oldBufNum, type)
|
||
SYD_CHAN *pSChan; /* pointer to syncSet channel descriptor */
|
||
FILE *ssFile;
|
||
int bufStat, bufNum, oldBufNum;
|
||
chtype type;
|
||
{
|
||
long stat;
|
||
int alStat, alSev, elCount;
|
||
int i;
|
||
|
||
/*-----------------------------------------------------------------------------
|
||
* first, get and store the alarm status and severity .
|
||
* (This is done using the "float" form of the buffer, which is
|
||
* possible because for these items all types of buffers look the same.)
|
||
*----------------------------------------------------------------------------*/
|
||
stat = fscanf(ssFile, "%d%d%d", &alSev, &alStat, &elCount);
|
||
assert(stat == 3);
|
||
pSChan->pInBuf[bufNum]->tfltval.status = alStat;
|
||
pSChan->pInBuf[bufNum]->tfltval.severity = alSev;
|
||
/*-----------------------------------------------------------------------------
|
||
* now get the actual data
|
||
*----------------------------------------------------------------------------*/
|
||
if (pSChan->dbrType == TYPENOTCONN || type == TYPENOTCONN) {
|
||
stat = fscanf(ssFile, "%*s");
|
||
bufStat = SYD_B_MISSING;
|
||
}
|
||
else if (pSChan->dbrType == DBR_TIME_FLOAT) {
|
||
float *pFl, val;
|
||
pFl = &pSChan->pInBuf[bufNum]->tfltval.value;
|
||
for (i=0; i<elCount; i++) {
|
||
stat = fscanf(ssFile, "%f", &val);
|
||
if (stat != 1) {
|
||
bufStat = SYD_B_MISSING;
|
||
break;
|
||
}
|
||
else if (i < (int) pSChan->elCount)
|
||
*pFl++ = val;
|
||
}
|
||
}
|
||
else if (pSChan->dbrType == DBR_TIME_SHORT) {
|
||
short *pSh, val;
|
||
pSh = &pSChan->pInBuf[bufNum]->tshrtval.value;
|
||
for (i=0; i<elCount; i++) {
|
||
stat = fscanf(ssFile, "%hd", &val);
|
||
if (stat != 1) {
|
||
bufStat = SYD_B_MISSING;
|
||
break;
|
||
}
|
||
else if (i < (int) pSChan->elCount)
|
||
*pSh++ = val;
|
||
}
|
||
}
|
||
else if (pSChan->dbrType == DBR_TIME_DOUBLE) {
|
||
double *pDbl, val;
|
||
pDbl = &pSChan->pInBuf[bufNum]->tdblval.value;
|
||
for (i=0; i<elCount; i++) {
|
||
stat = fscanf(ssFile, "%lf", &val);
|
||
if (stat != 1) {
|
||
bufStat = SYD_B_MISSING;
|
||
break;
|
||
}
|
||
else if (i < (int) pSChan->elCount)
|
||
*pDbl++ = val;
|
||
}
|
||
}
|
||
else if (pSChan->dbrType == DBR_TIME_LONG) {
|
||
long *pL, val;
|
||
pL = &pSChan->pInBuf[bufNum]->tlngval.value;
|
||
for (i=0; i<elCount; i++) {
|
||
stat = fscanf(ssFile, "%ld", &val);
|
||
if (stat != 1) {
|
||
bufStat = SYD_B_MISSING;
|
||
break;
|
||
}
|
||
else if (i < (int) pSChan->elCount)
|
||
*pL++ = val;
|
||
}
|
||
}
|
||
else if (pSChan->dbrType == DBR_TIME_STRING) {
|
||
char *pC, val[db_strval_dim];
|
||
int nChar;
|
||
pC = pSChan->pInBuf[bufNum]->tstrval.value;
|
||
for (i=0; i<elCount; i++) {
|
||
stat = fscanf(ssFile, "%d", &nChar);
|
||
if (stat != 1) {
|
||
bufStat = SYD_B_MISSING;
|
||
break;
|
||
}
|
||
assert(nChar > 0 && nChar < db_strval_dim);
|
||
if (fgets(val, nChar, ssFile) == NULL)
|
||
assertAlways(0);
|
||
if (i < (int) pSChan->elCount) {
|
||
strcpy(pC, val);
|
||
pC += db_strval_dim;
|
||
}
|
||
}
|
||
}
|
||
else if (pSChan->dbrType == DBR_TIME_CHAR) {
|
||
unsigned char *pC, val;
|
||
pC = &pSChan->pInBuf[bufNum]->tchrval.value;
|
||
for (i=0; i<elCount; i++) {
|
||
stat = fscanf(ssFile, "%c", &val);
|
||
if (stat != 1) {
|
||
bufStat = SYD_B_MISSING;
|
||
break;
|
||
}
|
||
else if (i < (int) pSChan->elCount)
|
||
*pC++ = val;
|
||
}
|
||
}
|
||
else if (pSChan->dbrType == DBR_TIME_ENUM) {
|
||
unsigned short *pSh;
|
||
char state[80];
|
||
int iState;
|
||
pSh = &pSChan->pInBuf[bufNum]->tenmval.value;
|
||
for (i=0; i<elCount; i++) {
|
||
stat = fscanf(ssFile, "%s", state);
|
||
if (stat != 1) {
|
||
bufStat = SYD_B_MISSING;
|
||
break;
|
||
}
|
||
assert(strlen(state) < db_state_text_dim);
|
||
iState = 0;
|
||
while (1) {
|
||
if (iState >= pSChan->grBuf.genmval.no_str) {
|
||
pSChan->grBuf.genmval.no_str++;
|
||
(void)strcpy(pSChan->grBuf.genmval.strs[iState], state);
|
||
break;
|
||
}
|
||
else if (strcmp(pSChan->grBuf.genmval.strs[iState],state) == 0)
|
||
break;
|
||
iState++;
|
||
assert(iState < db_state_dim);
|
||
}
|
||
if (i < (int) pSChan->elCount)
|
||
*pSh++ = iState;
|
||
}
|
||
}
|
||
/*-----------------------------------------------------------------------------
|
||
* set the buffer status. If the previous record had a status of MISSING,
|
||
* then the buffer status will be SYD_B_RESTART; otherwise, the caller's
|
||
* status will be used. (Unless, of course, this routine discovered that
|
||
* the status needs to be something else.)
|
||
*----------------------------------------------------------------------------*/
|
||
if (bufStat == SYD_B_MISSING)
|
||
pSChan->inStatus[bufNum] = bufStat;
|
||
else if (oldBufNum >= 0 && pSChan->inStatus[oldBufNum] == SYD_B_MISSING)
|
||
pSChan->inStatus[bufNum] = SYD_B_RESTART;
|
||
else
|
||
pSChan->inStatus[bufNum] = bufStat;
|
||
|
||
return S_syd_OK;
|
||
}
|
||
|
||
/*+/internal******************************************************************
|
||
* NAME sydSSFFuncGetGR - simulate getting graphics information
|
||
*
|
||
*-*/
|
||
static long
|
||
sydSSFFuncGetGR(pSspec, pSChan)
|
||
SYD_SPEC *pSspec; /* pointer to syncSet specification */
|
||
SYD_CHAN *pSChan; /* pointer to syncSet channel descriptor */
|
||
{
|
||
int i;
|
||
|
||
if (pSChan->dbrType == DBR_TIME_FLOAT) {
|
||
#define FLT_DEST pSChan->grBuf.gfltval
|
||
FLT_DEST.status = NO_ALARM;
|
||
FLT_DEST.severity = NO_ALARM;
|
||
#if 1
|
||
FLT_DEST.precision = 3;
|
||
(void)strcpy(FLT_DEST.units, " ");
|
||
FLT_DEST.upper_disp_limit = 0.;
|
||
FLT_DEST.lower_disp_limit = 0.;
|
||
FLT_DEST.upper_alarm_limit = 0.;
|
||
FLT_DEST.lower_alarm_limit = 0.;
|
||
FLT_DEST.upper_warning_limit = 0.;
|
||
FLT_DEST.lower_warning_limit = 0.;
|
||
#else
|
||
#define FLT_SRC ArCDChanHdr(pChanDesc).graphics.floatGr
|
||
(void)strcpy(FLT_DEST.units, FLT_SRC.units);
|
||
FLT_DEST.upper_disp_limit = FLT_SRC.upDispLim;
|
||
FLT_DEST.lower_disp_limit = FLT_SRC.lowDispLim;
|
||
FLT_DEST.upper_alarm_limit = FLT_SRC.upAlmLim;
|
||
FLT_DEST.lower_alarm_limit = FLT_SRC.lowAlmLim;
|
||
FLT_DEST.upper_warning_limit = FLT_SRC.upWarnLim;
|
||
FLT_DEST.lower_warning_limit = FLT_SRC.lowWarnLim;
|
||
#endif
|
||
}
|
||
else if (pSChan->dbrType == DBR_TIME_SHORT) {
|
||
#define SHRT_DEST pSChan->grBuf.gshrtval
|
||
SHRT_DEST.status = NO_ALARM;
|
||
SHRT_DEST.severity = NO_ALARM;
|
||
#if 1
|
||
(void)strcpy(SHRT_DEST.units, " ");
|
||
SHRT_DEST.upper_disp_limit = 0;
|
||
SHRT_DEST.lower_disp_limit = 0;
|
||
SHRT_DEST.upper_alarm_limit = 0;
|
||
SHRT_DEST.lower_alarm_limit = 0;
|
||
SHRT_DEST.upper_warning_limit = 0;
|
||
SHRT_DEST.lower_warning_limit = 0;
|
||
#else
|
||
#define SHRT_SRC ArCDChanHdr(pChanDesc).graphics.shortGr
|
||
(void)strcpy(SHRT_DEST.units, SHRT_SRC.units);
|
||
SHRT_DEST.upper_disp_limit = SHRT_SRC.upDispLim;
|
||
SHRT_DEST.lower_disp_limit = SHRT_SRC.lowDispLim;
|
||
SHRT_DEST.upper_alarm_limit = SHRT_SRC.upAlmLim;
|
||
SHRT_DEST.lower_alarm_limit = SHRT_SRC.lowAlmLim;
|
||
SHRT_DEST.upper_warning_limit = SHRT_SRC.upWarnLim;
|
||
SHRT_DEST.lower_warning_limit = SHRT_SRC.lowWarnLim;
|
||
#endif
|
||
}
|
||
else if (pSChan->dbrType == DBR_TIME_DOUBLE) {
|
||
#define DBL_DEST pSChan->grBuf.gdblval
|
||
DBL_DEST.status = NO_ALARM;
|
||
DBL_DEST.severity = NO_ALARM;
|
||
#if 1
|
||
DBL_DEST.precision = 3;
|
||
(void)strcpy(DBL_DEST.units, " ");
|
||
DBL_DEST.upper_disp_limit = 0.;
|
||
DBL_DEST.lower_disp_limit = 0.;
|
||
DBL_DEST.upper_alarm_limit = 0.;
|
||
DBL_DEST.lower_alarm_limit = 0.;
|
||
DBL_DEST.upper_warning_limit = 0.;
|
||
DBL_DEST.lower_warning_limit = 0.;
|
||
#else
|
||
#define DBL_SRC ArCDChanHdr(pChanDesc).graphics.doubleGr
|
||
(void)strcpy(DBL_DEST.units, DBL_SRC.units);
|
||
DBL_DEST.upper_disp_limit = DBL_SRC.upDispLim;
|
||
DBL_DEST.lower_disp_limit = DBL_SRC.lowDispLim;
|
||
DBL_DEST.upper_alarm_limit = DBL_SRC.upAlmLim;
|
||
DBL_DEST.lower_alarm_limit = DBL_SRC.lowAlmLim;
|
||
DBL_DEST.upper_warning_limit = DBL_SRC.upWarnLim;
|
||
DBL_DEST.lower_warning_limit = DBL_SRC.lowWarnLim;
|
||
#endif
|
||
}
|
||
else if (pSChan->dbrType == DBR_TIME_LONG) {
|
||
#define LNG_DEST pSChan->grBuf.glngval
|
||
LNG_DEST.status = NO_ALARM;
|
||
LNG_DEST.severity = NO_ALARM;
|
||
#if 1
|
||
(void)strcpy(LNG_DEST.units, " ");
|
||
LNG_DEST.upper_disp_limit = 0;
|
||
LNG_DEST.lower_disp_limit = 0;
|
||
LNG_DEST.upper_alarm_limit = 0;
|
||
LNG_DEST.lower_alarm_limit = 0;
|
||
LNG_DEST.upper_warning_limit = 0;
|
||
LNG_DEST.lower_warning_limit = 0;
|
||
#else
|
||
#define LNG_SRC ArCDChanHdr(pChanDesc).graphics.longGr
|
||
(void)strcpy(LNG_DEST.units, LNG_SRC.units);
|
||
LNG_DEST.upper_disp_limit = LNG_SRC.upDispLim;
|
||
LNG_DEST.lower_disp_limit = LNG_SRC.lowDispLim;
|
||
LNG_DEST.upper_alarm_limit = LNG_SRC.upAlmLim;
|
||
LNG_DEST.lower_alarm_limit = LNG_SRC.lowAlmLim;
|
||
LNG_DEST.upper_warning_limit = LNG_SRC.upWarnLim;
|
||
LNG_DEST.lower_warning_limit = LNG_SRC.lowWarnLim;
|
||
#endif
|
||
}
|
||
else if (pSChan->dbrType == DBR_TIME_STRING) {
|
||
#define STR_DEST pSChan->grBuf.gstrval
|
||
STR_DEST.status = NO_ALARM;
|
||
STR_DEST.severity = NO_ALARM;
|
||
}
|
||
else if (pSChan->dbrType == DBR_TIME_ENUM) {
|
||
#define ENM_DEST pSChan->grBuf.genmval
|
||
ENM_DEST.status = NO_ALARM;
|
||
ENM_DEST.severity = NO_ALARM;
|
||
#if 1
|
||
ENM_DEST.no_str = 0;
|
||
#else
|
||
#define ENM_SRC ArCDChanHdr(pChanDesc).graphics.enumGr
|
||
ENM_DEST.no_str = ArCFChanNStates(pChanDesc);
|
||
for (i=0; i<ENM_DEST.no_str; i++)
|
||
(void)strcpy(ENM_DEST.strs[i], ArCFChanStates(pChanDesc)[i]);
|
||
#endif
|
||
}
|
||
else if (pSChan->dbrType == DBR_TIME_CHAR) {
|
||
#define CHR_DEST pSChan->grBuf.gchrval
|
||
CHR_DEST.status = NO_ALARM;
|
||
CHR_DEST.severity = NO_ALARM;
|
||
(void)strcpy(CHR_DEST.units, " ");
|
||
#if 1
|
||
CHR_DEST.upper_disp_limit = 0;
|
||
CHR_DEST.lower_disp_limit = 0;
|
||
CHR_DEST.upper_alarm_limit = 0;
|
||
CHR_DEST.lower_alarm_limit = 0;
|
||
CHR_DEST.upper_warning_limit = 0;
|
||
CHR_DEST.lower_warning_limit = 0;
|
||
#else
|
||
#define CHR_SRC ArCDChanHdr(pChanDesc).graphics.charGr
|
||
(void)strcpy(CHR_DEST.units, CHR_SRC.units);
|
||
CHR_DEST.upper_disp_limit = CHR_SRC.upDispLim;
|
||
CHR_DEST.lower_disp_limit = CHR_SRC.lowDispLim;
|
||
CHR_DEST.upper_alarm_limit = CHR_SRC.upAlmLim;
|
||
CHR_DEST.lower_alarm_limit = CHR_SRC.lowAlmLim;
|
||
CHR_DEST.upper_warning_limit = CHR_SRC.upWarnLim;
|
||
CHR_DEST.lower_warning_limit = CHR_SRC.lowWarnLim;
|
||
#endif
|
||
}
|
||
return S_syd_OK;
|
||
}
|
||
|
||
/*+/internal******************************************************************
|
||
* NAME sydSSFFuncSeekStamp - find a `SAMPLE: at' line
|
||
*
|
||
* DESCRIPTION
|
||
* Find a `SAMPLE: at' line and get the time stamp from it.
|
||
*
|
||
* If the search is forward, the file is left positioned following
|
||
* the stamp. If backward, the position is at the beginning of the
|
||
* line.
|
||
*
|
||
* RETURNS
|
||
* position of the line
|
||
*
|
||
*-*/
|
||
static long
|
||
sydSSFFuncSeekStamp(ssFile, pStamp, revFlag, pos)
|
||
FILE *ssFile;
|
||
TS_STAMP *pStamp;
|
||
int revFlag; /* I 1 says seek in reverse direction */
|
||
long pos; /* I position to start search if reverse; -1
|
||
says to start at EOF */
|
||
{
|
||
long bufStat;
|
||
char record[120], recDelim;
|
||
char *pRecord, *pField, delim;
|
||
long stat;
|
||
|
||
if (revFlag)
|
||
pos = posKeyRev("SAMPLE: at", ssFile, pos);
|
||
else
|
||
pos = posKeyFwd("SAMPLE: at", ssFile);
|
||
if (pos < 0)
|
||
return pos;
|
||
assert(sizeof(record) == 120);
|
||
if (fgets(record, 120, ssFile) == NULL)
|
||
return -1;
|
||
|
||
pRecord = record;
|
||
recDelim = record[strlen(record)-1];
|
||
if (nextNonSpaceField(&pRecord, &pField, &delim) <= 1)
|
||
assertAlways(0);
|
||
if (strcmp(pField, "SAMPLE:") != 0)
|
||
assertAlways(0);
|
||
if (nextNonSpaceField(&pRecord, &pField, &delim) <= 1)
|
||
assertAlways(0);
|
||
if (strcmp(pField, "at") != 0)
|
||
assertAlways(0);
|
||
stat = tsTextToStamp(pStamp, &pRecord);
|
||
assert(stat == S_ts_OK);
|
||
if (revFlag) {
|
||
if (posPos(ssFile, pos) < 0)
|
||
assertAlways(0);
|
||
}
|
||
return pos;
|
||
}
|
||
|
||
/*+/internal******************************************************************
|
||
* NAME sydSSFFuncSeekSample - find the next sample
|
||
*
|
||
* DESCRIPTION
|
||
* Finds the next `SAMPLE: at' line, gets the time stamp for the
|
||
* sample, and gets the `status' for the snapshot. The file is
|
||
* positioned following the %endHeader%, ready for reading the data.
|
||
*
|
||
* RETURNS
|
||
* SYD_B_RESTART if `initial' was specified on the `SAMPLE: at' line, or
|
||
* SYD_B_FULL
|
||
*
|
||
*-*/
|
||
static long
|
||
sydSSFFuncSeekSample(ssFile, pStamp)
|
||
FILE *ssFile;
|
||
TS_STAMP *pStamp;
|
||
{
|
||
long pos;
|
||
long bufStat;
|
||
char record[120], recDelim;
|
||
char *pRecord, *pField, delim;
|
||
long stat;
|
||
|
||
if ((pos = posKeyFwd("SAMPLE: at", ssFile)) < 0)
|
||
return SYD_B_EOF;
|
||
assert(sizeof(record) == 120);
|
||
if (fgets(record, 120, ssFile) == NULL)
|
||
return SYD_B_EOF;
|
||
|
||
pRecord = record;
|
||
recDelim = record[strlen(record)-1];
|
||
if (nextNonSpaceField(&pRecord, &pField, &delim) <= 1)
|
||
assertAlways(0);
|
||
if (strcmp(pField, "SAMPLE:") != 0)
|
||
assertAlways(0);
|
||
if (nextNonSpaceField(&pRecord, &pField, &delim) <= 1)
|
||
assertAlways(0);
|
||
if (strcmp(pField, "at") != 0)
|
||
assertAlways(0);
|
||
stat = tsTextToStamp(pStamp, &pRecord);
|
||
assert(stat == S_ts_OK);
|
||
while (*pRecord == '-')
|
||
pRecord++;
|
||
if (strncmp(pRecord, "initial", 7) == 0)
|
||
bufStat = SYD_B_RESTART;
|
||
else
|
||
bufStat = SYD_B_FULL;
|
||
if (recDelim != '\n') {
|
||
while ((delim = fgetc(ssFile)) != EOF && delim != '\n')
|
||
; /* keep skipping until end of line */
|
||
}
|
||
if (posKeyFwd("%endHeader%", ssFile) < 0)
|
||
return SYD_B_EOF;
|
||
else if (fgets(record, 120, ssFile) == NULL)
|
||
return SYD_B_EOF;
|
||
return bufStat;
|
||
}
|