Files
pcas/src/libCom/sydSubr.c

4070 lines
127 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* @(#)sydSubr.c 1.18 2/23/93
* Author: Roger A. Cole
* Date: 12-04-90
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1990-93, 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-15-91 rac update documentation; add args to sydChanOpen;
* enhance for use by ordinary users
* .04 09-11-91 rac for CA, report "noData" if ts==0; add more
* documentation
* .05 09-22-91 rac add sydInputFetch; add trigger routines;
* handle asynchronous ca_search
* .06 10-21-91 rac set the reference time stamp for the first
* sample which is stored; better handle long
* lines in printing; fold channel names when
* printing; put EGU in channel structure;
* put EGU in export file; support time stamp
* rounding; args changed for: sydSampleSetPrint;
* sydSamplePrint; sydInputStoreInSet;
* .07 11-02-91 rac add sydSampleWriteSSF, sydSampleSetWriteSSF
* .08 12-08-91 rac fix alignment for printing of channel names
* .09 01-20-92 rac add a code for VALID_ALARM and handle
* invalid values properly
* .10 02-04-92 rac allow multiple chanOpen for same name
* .11 02-18-92 rac finally handle array channels on print and export;
* add an export column for text time stamp
* .12 02-26-92 rac move timestamp rounding to the individual read
* routines, using a new routine here; add sydSetAttr
* and default deadband
* .13 03-23-92 rac set default monitor to use ADEL deadband
* .14 03-31-92 rac install a kludge to cope with 0 timestamps from EPICS;
* add attributes for calling a user CA monitor routine;
* add a "set-wide" connect flag and a "set-wide" flag
* for "waiting for graphics info"; generalize routines
* for copying buffers; use exponential format for
* exporting and printing tiny numbers; add support
* for mean and standard deviation; add capability to
* restrict range of interest to a subset of the samples;
* put a blank line in export file following snapshot;
* for .RVAL channels, set prec=0, EGU="counts"
* .15 08-25-92 rac fix bugs in exporting for restricted time range;
* fix sydSamplePrint to put a separator in front of
* the no data message; add better handling for SSF
* files with missing data; added a routine to prepare
* a channel for a new retrieval; discontinue use of
* special malloc routines
* .16 09-30-92 rac discard samples with earlier time stamps than the most
* recent sample;
* .17 01-05-93 rac add some documentation regarding the handling of
* samples with SYD_B_MISSING status; fix a bug for
* the handling of SYD_B_MISSING samples; add an
* option for exporting only means; fix a bug in
* sydInputReset where last buffer didn't get discarded;
* .18 03-22-93 changed VALID_ALARM to INVALID_ALARM - thanks
* .19 04-19-94 mrk removed static from sydSamplePrintStats
*
* make options
* -DvxWorks makes a version for VxWorks
* -DNDEBUG don't compile assert checking
* -DDEBUG compile various debug code
*/
#if 0 /* allow comments within the module heading */
/* /*+/mod***********************************************************************
/* * TITLE sydSubr.c - synchronous data routines
/* *
/* * DESCRIPTION
/* * These routines support:
/* * o defining a set of channels for which synchronous samples are
/* * to be acquired
/* * o checking to see if a synchronous sample is available
/* * o acquiring the next synchronous sample or a specified number
/* * of synchronous samples
/* * o storing the most recently acquired synchronous sample at the
/* * end of the set of synchronous samples
/* * o printing a particular synchronous sample or all samples in
/* * a synchronous sample set
/* *
/* * Sample acquisition can be from one of:
/* * o archiver `sample set' file
/* * o archiver `by channel' file
/* * o Channel Access
/* *
/* * sydPlot.c contains routines which support plotting a synchronous
/* * sample set.
/* *
/* * QUICK REFERENCE
/* *
/* * #include <genDefs.h> /* some general use definitions */
/* * #include <db_access.h> /* definitions for database related items */
/* * #include <sydDefs.h> /* structures for synchronous data routines */
/* *
/* * SYD_SPEC *pSspec; /* pointer to synchronous set spec */
/* * SYD_CHAN *pSChan; /* pointer to synchronous channel descriptor */
/* *
/* * long sydChanClose( pSspec, pSChan )
/* * SYD_CHAN *sydChanFind( pSspec, chanName )
/* * long sydChanOpen( pSspec, >ppSChan, chanName, sync, pArg, trig)
/* * sync = SYD_SY_{NONF,FILLED}
/* * long sydChanPrep( pSspec, pSChan )
/* * long sydClose( pSspec )
/* * void sydCopyGr( >pDest, pSrc, srcDbrType )
/* * void sydCopyVal( >pDest, pSrc, count, srcDbrType )
/* * long sydFileInfo( pSspec, out )
/* * long sydInputFetch( pSspec )
/* * long sydInputGet( pSspec, >pMoreFlag )
/* * void sydInputReset( pSspec )
/* * void sydInputResetKeepNewest(pSspec )
/* * void sydInputResetSampled(pSspec )
/* * void sydInputStoreInSet( pSspec, ignorePartial )
/* * long sydInputSync( pSspec )
/* * long sydMonitorStart( pSspec )
/* * long sydMonitorStop( pSspec )
/* * long sydOpenCA( >ppSspec, NULL )
/* * long sydOpenCF( >ppSspec, filePath )
/* * long sydOpenSSF( >ppSspec, filePath )
/* * long sydPosition( pSspec, pStamp )
/* * long sydSampleExport( pSspec, out, fmtFlag, hdrFlag, sampNum)
/* * long sydSampleExportStats(pSspec, out, snapNum )
/* * long sydSamplePrint( pSspec, out, fmtFlag, hdrFlag,
/* * nCol, colWidth, sampNum)
/* * long sydSamplePrintStats( pSspec, out, fmtFlag, hdrFlag,
/* * nCol, colWidth, snapNum)
/* * long sydSampleWriteSSF( pSspec, pFile, progDesc, sampDesc, sampNum)
/* *
/* * long sydSampleSetAlloc( pSspec, reqCount )
/* * long sydSampleSetExport( pSspec, out, fmtFlag )
/* * long sydSampleSetFree( pSspec )
/* * long sydSampleSetGet( pSspec )
/* * long sydSampleSetPrint( pSspec, out, fmtFlag, nCol, colWidth )
/* * long sydSampleSetRestrict(pSspec, pTsBegin, pTsEnd )
/* * long sydSampleSetStats( pSspec )
/* * long sydSampleSetWriteSSF(pSspec, pFile, progDesc, sampDesc )
/* * long sydSetAttr( pSspec, attr, value, pArg )
/* * SYD_ATTR_DEADBAND, 0, {"ADEL" or "MDEL"}
/* * SYD_ATTR_MON_FN, 0, function
/* * SYD_ATTR_USE_STATS, 1, NULL
/* * SYD_ATTR_USE_MEANS, 1, NULL
/* *
/* * long sydTest( pSspec )
/* * long sydTestAddFromText( pSspec, text )
/* * long sydTestClose( pSspec )
/* * int sydValAsDbl( pSChan, sampNum, pDbl )
/* *
/* * BUGS
/* * o error detection and handling isn't "leakproof"
/* * o for retrieving from sample set files, if all the channels in the
/* * set are missing for two snapshots in a row, EOF is reported
/* *
/* * DESCRIPTION, continued
/* *
/* * o special terms
/* *
/* * synchronous sample--is a set of values, one for each channel,
/* * with the same time stamp for each
/* *
/* * synchronous sample set--is a set of synchronous samples, in order
/* * by time stamp. This frequently will be referred to as a
/* * synchronous set.
/* *
/* * synchronous channel descriptor--is the information about one
/* * of the channels for which synchronous data is to be acquired
/* *
/* * synchronous set specification--is the set of information describing
/* * how synchronous samples are to be acquired, and which channels
/* * are to be used
/* *
/* * o synchronous set specification
/* *
/* * A program can have one or more synchronous set specifications at the
/* * same time. Each is created with an "open" and destroyed with a
/* * "close". The "open" returns a pointer which is used in all
/* * subsequent operations with the "sync set spec".
/* *
/* * There are several routines available for opening a sync set spec; the
/* * one which is used determines where samples will be obtained. Most
/* * other sydXxx routines are independent of which source is being used.
/* * See sydOpen for more details.
/* *
/* * o synchronous channel descriptor
/* *
/* * After a sync set spec has been opened, one or more channels will
/* * be added to it with calls to sydChanOpen. When a channel is opened,
/* * it must be specified whether the channel will be treated as "filled"
/* * or as "synchronous". This distinction comes into play while samples
/* * are being acquired (see below).
/* *
/* * The value for a channel might be a scalar, as for a thermocouple,
/* * or an array, as for a digitized waveform. Most of the discussion
/* * which follows makes no distinction between the two kinds of values.
/* *
/* * When a channel is no longer wanted as part of a sync set spec, the
/* * sydChanClose call can be used to remove it. (The sydClose call
/* * automatically closes all channels in a sync set spec.)
/* *
/* * o acquiring synchronous samples
/* *
/* * Each channel is treated as having a stream of time-stamped values.
/* * When sydInputGet is called, the earliest time stamp for all the
/* * channels is found--this is the time stamp for the sample. Each
/* * channel which has a value with that time stamp is placed in the
/* * sample. If no samples have yet been recieved for a channel, then
/* * the channel is flagged as "missing" in the sample. The action taken
/* * when there is no value for a channel depends on how the channel was
/* * opened.
/* *
/* * If the sydChanOpen call specified SYD_SY_FILLED, then when the channel
/* * has no value at the chosen time stamp the most recent prior value is
/* * placed into the sample. This implements the assumption that the
/* * channel's value has remained constant.
/* *
/* * Specifying SYD_SY_NONF in the sydChanOpen call inhibits "filling in"
/* * a value in the sample when a channel has no value at the chosen
/* * time stamp. In this case, the channel will be flagged as "missing"
/* * in the sample.
/* *
/* * If data acquisition is from Channel Access, then some additional
/* * details come into play. The result is to compensate for possible
/* * network delays in transmitting data, which means that the calling
/* * program may occasionally receive a "no data now" status. In that
/* * case, the calling program is expected to try again later to see
/* * if additional data have been received.
/* *
/* * o accessing data for a sample
/* *
/* * o accessing data for a sample set
/* *
/* * EXAMPLE
/* *
/* * #include <genDefs.h>
/* * #include <sydDefs.h>
/* * #include <cadef.h>
/* * #include <db_access.h>
/* *
/* * main()
/* * {
/* * SYD_SPEC *pSspec; /* pointer to sync set spec */
/* * SYD_CHAN *pSchanBase; /* pointer to sync chan desc for POWER:BASE */
/* * SYD_CHAN *pSchanLag; /* pointer to sync chan desc for POWER:LAG30 */
/* * long stat; /* status return */
/* * int i;
/* * int moreFlag; /* 1 if more samples waiting */
/* * float sumBase, sumLag;/* sums for the two channels */
/* * char timeText[28]; /* text for time stamp */
/* * int chanStat; /* input status for channel */
/* *
/* * /*---------------------------------------------------------------------------
/* * * open the synchronous sample set specification and add the channels
/* * * to it
/* * *--------------------------------------------------------------------------*/
/* * stat = sydOpenCA(&pSspec, NULL);
/* * if (stat != S_syd_OK) {
/* * printf("couldn't open sync set spec\n");
/* * exit(1);
/* * }
/* * stat = sydChanOpen(pSspec, &pSchanBase, "rai_2000", SYD_SY_FILLED,NULL,0);
/* * if (stat != S_syd_OK) {
/* * printf("couldn't open POWER:BASE\n");
/* * exit(1);
/* * }
/* * stat = sydChanOpen(pSspec, &pSchanLag, "rao_2000", SYD_SY_FILLED,NULL,0);
/* * if (stat != S_syd_OK) {
/* * printf("couldn't open POWER:LAG30\n");
/* * exit(1);
/* * }
/* * /*---------------------------------------------------------------------------
/* * * now get 100 synchronous samples and accumulate a running sum for
/* * * each channel. Since this example program is using Channel Access,
/* * * it loops on sydInputGet until a status of S_syd_noDataNow is
/* * * received; when retrieving from an archive file, such a loop wouldn't
/* * * be used.
/* * *--------------------------------------------------------------------------*/
/* * sumBase = sumLag = 0.;
/* * i = 0;
/* * while (i < 100) {
/* * ca_pend_event(.1); /* allow Channel Access to get values */
/* * stat = sydInputGet(pSspec, &moreFlag); /* see if any were obtained */
/* * while (stat == S_syd_OK || stat == S_syd_partial) {
/* * i++;
/* * tsStampToText(&SydInputTs(pSspec), TS_TEXT_MMDDYY, timeText);
/* * printf("sample at %s more:%d--", timeText, moreFlag);
/* * chanStat = SydInputStatus(pSchanBase);
/* * if (chanStat != SYD_B_EOF && chanStat != SYD_B_MISSING) {
/* * sumBase += SydInputValAsFloat(pSchanBase);
/* * printf("%s= %f ", SydChanName(pSchanBase),
/* * SydInputValAsFloat(pSchanBase));
/* * SydInputMarkAsSampled(pSchanBase);
/* * }
/* * chanStat = SydInputStatus(pSchanLag);
/* * if (chanStat != SYD_B_EOF && chanStat != SYD_B_MISSING) {
/* * sumLag += SydInputValAsFloat(pSchanLag);
/* * printf("%s= %f ", SydChanName(pSchanLag),
/* * SydInputValAsFloat(pSchanLag));
/* * SydInputMarkAsSampled(pSchanLag);
/* * }
/* * printf("\n");
/* * stat = sydInputGet(pSspec, &moreFlag);
/* * }
/* * }
/* * printf("sumBase= %f sumLag= %f\n", sumBase, sumLag);
/* * }
/* *-***************************************************************************/
/****/
#endif /* allow comments within the module heading */
#include <genDefs.h>
#define SYD_PRIVATE
#define SYD_PRIVATE_DATA
#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>
# include <math.h>
#endif
#define PRE_FL 1
#define POST_FL 2
#define SHOW_AR 4
#define USE_QUO 8
#define ENF_WID 16
static void sydSamplePrint1();
static void sydSamplePrintVal();
static void sydChanFreeArrays();
static void sydInputGetIn();
long sydSampleSetAlloc();
long sydSampleSetFree();
#if SYD_SUBR_TEST
main()
{
SYD_SPEC *pSspec; /* pointer to sync set spec */
SYD_CHAN *pSchanBase; /* pointer to sync chan desc for POWER:BASE */
SYD_CHAN *pSchanLag; /* pointer to sync chan desc for POWER:LAG30 */
long stat; /* status return */
int i;
int moreFlag; /* 1 if more samples waiting */
float sumBase, sumLag;/* sums for the two channels */
char timeText[28]; /* text for time stamp */
int chanStat; /* input status for channel */
int beg, end;
TS_STAMP begTs, endTs;
char begText[28], endText[28];
/*-----------------------------------------------------------------------------
* open the synchronous sample set specification and add the channels
* to it
*----------------------------------------------------------------------------*/
stat = sydOpenCA(&pSspec, NULL);
if (stat != S_syd_OK) {
printf("couldn't open sync set spec\n");
exit(1);
}
stat = sydChanOpen(pSspec, &pSchanBase, "gva_2000", SYD_SY_FILLED,NULL,0);
if (stat != S_syd_OK && stat != S_syd_chanNotConn) {
printf("couldn't open gva_2000\n");
exit(1);
}
stat = sydChanOpen(pSspec, &pSchanLag, "gva_2001", SYD_SY_FILLED,NULL,0);
if (stat != S_syd_OK && stat != S_syd_chanNotConn) {
printf("couldn't open gva_2001\n");
exit(1);
}
#if 0
/*-----------------------------------------------------------------------------
* now get 50 synchronous samples and accumulate a running sum for
* each channel. Since this example program is using Channel Access,
* it loops on sydInputGet until a status of S_syd_noDataNow is
* received; when retrieving from an archive file, such a loop wouldn't
* be used.
*----------------------------------------------------------------------------*/
sumBase = sumLag = 0.;
i = 0;
while (i < 20) {
ca_pend_event(.1); /* allow Channel Access to get values */
stat = sydInputGet(pSspec, &moreFlag); /* see if any were obtained */
while (stat == S_syd_OK || stat == S_syd_partial) {
i++;
tsStampToText(&SydInputTs(pSspec), TS_TEXT_MMDDYY, timeText);
printf("sample at %s more:%d--", timeText, moreFlag);
chanStat = SydInputStatus(pSchanBase);
if (chanStat != SYD_B_EOF && chanStat != SYD_B_MISSING) {
sumBase += SydInputValAsFloat(pSchanBase);
printf("%s= %f ", SydChanName(pSchanBase),
SydInputValAsFloat(pSchanBase));
SydInputMarkAsSampled(pSchanBase);
}
chanStat = SydInputStatus(pSchanLag);
if (chanStat != SYD_B_EOF && chanStat != SYD_B_MISSING) {
sumLag += SydInputValAsFloat(pSchanLag);
printf("%s= %f ", SydChanName(pSchanLag),
SydInputValAsFloat(pSchanLag));
SydInputMarkAsSampled(pSchanLag);
}
printf("\n");
stat = sydInputGet(pSspec, &moreFlag);
}
}
printf("sumBase= %f sumLag= %f\n", sumBase, sumLag);
#endif
printf("checking connections\n");
for (i=0; i<20; i++) {
if (pSchanBase->discon || pSchanLag->discon)
ca_pend_event(.1);
else
break;
}
if (pSchanBase->discon || pSchanLag->discon) {
printf("at least one channel isn't connected\n");
exit(1);
}
printf("getting samples to store--storing all\n");
stat = sydSampleSetAlloc(pSspec, 50);
assert(stat == S_syd_OK);
for (i=0; i<20; ) {
ca_pend_event(.1);
stat = sydInputGet(pSspec, &moreFlag); /* see if any were obtained */
while (stat == S_syd_OK || stat == S_syd_partial) {
sydInputStoreInSet(pSspec, 0);
printf(".");
fflush(stdout);
i++;
stat = sydInputGet(pSspec, &moreFlag);
}
}
printf("\n");
stat = sydSampleSetPrint(pSspec, stdout, 0, 5, 10);
assert(stat == S_syd_OK);
beg = 5;
end = 15;
begTs = pSspec->pTimeStamp[beg];
begTs.nsec = 0;
endTs = pSspec->pTimeStamp[end];
endTs.nsec = 999000000;
tsStampToText(&begTs, TS_TEXT_MMDDYY, begText);
tsStampToText(&endTs, TS_TEXT_MMDDYY, endText);
printf("range: %s to %s\n", begText, endText);
stat = sydSampleSetRestrict(pSspec, &begTs, &endTs);
assert(stat == S_syd_OK);
stat = sydSampleSetPrint(pSspec, stdout, 0, 5, 10);
assert(stat == S_syd_OK);
printf("getting samples to store--storing if gva_200 > 4.5\n");
stat = sydSampleSetAlloc(pSspec, 50);
assert(stat == S_syd_OK);
stat = sydTestAddFromText(pSspec, "if gva_2000 > 4.5");
assert(stat == S_syd_OK);
for (i=0; i<20; ) {
ca_pend_event(.1);
stat = sydInputGet(pSspec, &moreFlag); /* see if any were obtained */
while (stat == S_syd_OK || stat == S_syd_partial) {
sydInputStoreInSet(pSspec, 0);
printf(".");
fflush(stdout);
i++;
stat = sydInputGet(pSspec, &moreFlag);
}
}
printf("\n");
stat = sydSampleSetPrint(pSspec, stdout, 0, 5, 10);
assert(stat == S_syd_OK);
}
#endif
/*+/subr**********************************************************************
* NAME sydChanClose - delete a channel from a synchronous set spec
*
* DESCRIPTION
* This routine deletes a channel from a synchronous set spec. The
* other channels in the set aren't affected, and their data is
* preserved.
*
* RETURNS
* S_syd_OK
*
* SEE ALSO
* sydOpen, sydChanOpen, sydChanFind
* sydInputGet
*
*-*/
long
sydChanClose(pSspec, pSChan)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
SYD_CHAN *pSChan; /* I pointer to synchronous channel descriptor */
{
int i;
assert(pSspec != NULL);
assert(pSChan != NULL);
if (pSspec->pFunc != NULL)
(void)(pSspec->pFunc)(pSspec, pSChan, SYD_FC_CLOSE, NULL);
for (i=0; i<pSChan->nInBufs; i++) {
if (pSChan->pInBuf[i] != NULL)
free((char *)pSChan->pInBuf[i]);
}
sydChanFreeArrays(pSChan);
pSspec->chanCount--;
DoubleListRemove(pSChan, pSspec->pChanHead, pSspec->pChanTail);
if (pSChan->testChan) {
free((char *)pSspec->pAccept);
pSspec->pAccept = NULL;
}
free((char *)pSChan);
return S_syd_OK;
}
/*+/subr**********************************************************************
* NAME sydChanFreeArrays
*
*-*/
static void
sydChanFreeArrays(pSChan)
SYD_CHAN *pSChan;
{
if (pSChan->pData != NULL) {
free((char *)pSChan->pData);
pSChan->pData = NULL;
}
if (pSChan->pDataAlStat != NULL) {
free((char *)pSChan->pDataAlStat);
pSChan->pDataAlStat = NULL;
}
if (pSChan->pDataAlSev != NULL) {
free((char *)pSChan->pDataAlSev);
pSChan->pDataAlSev = NULL;
}
if (pSChan->pDataCodeL != NULL) {
free((char *)pSChan->pDataCodeL);
pSChan->pDataCodeL = NULL;
}
if (pSChan->pDataCodeR != NULL) {
free((char *)pSChan->pDataCodeR);
pSChan->pDataCodeR = NULL;
}
if (pSChan->pFlags != NULL) {
free((char *)pSChan->pFlags);
pSChan->pFlags = NULL;
}
if (pSChan->pStats != NULL) {
free((char *)pSChan->pStats);
pSChan->pStats = NULL;
}
}
/*+/subr**********************************************************************
* NAME sydChanFind - find a channel in a synchronous set spec
*
* DESCRIPTION
* This routine finds a channel in a synchronous set spec. This can
* be useful in detecting duplications in channel name.
*
* RETURNS
* SYD_CHAN * for channel, if found, or
* NULL
*
*-*/
SYD_CHAN *
sydChanFind(pSspec, chanName)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
char *chanName; /* I channel name to find in synchronous set spec */
{
SYD_CHAN *pSChan; /* pointer to channel descriptor */
assert(pSspec != NULL);
assert(chanName != NULL);
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
if (strcmp(pSChan->name, chanName) == 0)
break;
}
return pSChan;
}
/*+/subr**********************************************************************
* NAME sydChanOpen - add a channel to a synchronous set spec
*
* DESCRIPTION
* This routine opens a channel and adds it to the end of a synchronous
* set spec.
*
* Several data channels with the same name can be opened. (If
* retrieving from Channel Access, each will have its own monitor
* to the IOC. If the caller has his own monitor handler, monitoring
* doesn't actually begin until sydMonitorStart is called.)
*
* A channel can also be specified as a `test' channel, to be used
* by sydTest to check a value-related condition. Only one test
* channel can be specified for a sync set spec.
*
* RETURNS
* S_syd_OK, or
* S_syd_noMem no memory for adding channel to set
* S_syd_chanNotConn channel not connected to Channel Access (this
* isn't treated as an error condition--the
* channel is open and added to sync set spec)
* S_syd_chanNotFound channel not found
* S_syd_ERROR test channel specified, but there is already
* a test channel with that name
*
* BUGS
* o always places channel at end of list
* o data are always acquired using the native type of the channel,
* rather than caller-specified type
* o .RVAL channels are always treated as unsigned
*
* SEE ALSO
* sydOpen, sydChanClose, sydSetAttr
* sydInputGet, sydMonitorStart
* sydTest
*
* MACROS
* These macros extract fields from the synchronous channel structure
* created by sydChanOpen. By using these macros, the programmer
* can avoid the need to look inside the sample structures.
*
* Each macro takes as an argument a pointer to a synchronous
* channel structure, as returned by sydChanOpen or sydChanFind.
*
* SydChanArrayCount(pSChan) obtain the array count (1 for scalar)
* SydChanDbfType(pSChan) obtain the channel type. This will
* be one of the DBF_XXX types.
* SydChanDbrType(pSChan) obtain the value type. This will
* be one of the DBR_XXX types.
* SydChanName(pSChan) obtain the channel's name.
* SydChanEGU(pSChan) obtain the channel's engineering
* units text (if channel doesn't have EGU, this will be "none")
*
* Additional macros are described under sydInputGet.
*
* NOTES
* 1. For channels in AR sample set data files, SYD_SY_NONF is forced,
* without comment.
* 2. DBF_ENUM channels are flagged as SYD_ST_STEP; all other channels
* are flagged as SYD_ST_SMOOTH.
* 3. For retrievals from Channel Access, the usual return status will
* be S_syd_chanNotConn. To force the connection attempt to completion,
* the caller can call ca_pend_event following the call to this routine.
* If SydChanDbfType(pSChan) is TYPENOTCONN following the ca_pend_event,
* then the caller can assume the channel isn't presently connectable.
* 4. For retrievals from Channel Access, the deadband which will be
* used is determined by the SYD_ATTR_DEADBAND attribute. By default,
* the ADEL deadband is used.
* 5. For retrievals from Channel Access with a caller-specified monitor
* handler, the caller must call sydMonitorStart once for the sync set
* spec in order to get a monitor started.
* 6. For .RVAL channels, PREC is set to 0 and EGU is set to "counts".
* Special processing is done for printing, plotting, exporting, and
* statistics computing--the value is treated as unsigned.
*
* EXAMPLE
*
*-*/
long
sydChanOpen(pSspec, ppSChan, chanName, sync, pArg, test)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
SYD_CHAN **ppSChan; /* O pointer to sync channel pointer; or NULL */
char *chanName; /* I channel name (.VAL assumed if field omitted) */
enum sydChanSync sync; /* I sync/non-sync; SYD_SY_NONF or SYD_SY_FILLED */
void *pArg; /* I pointer to struct needed to init, or NULL */
int test; /* I 0,1 if this is data,test channel */
{
long retStat=S_syd_OK;/* return status to caller */
long stat; /* status return from calls */
SYD_CHAN *pSChan; /* pointer to syncSet channel descriptor */
enum sydChanStep step; /* step type: SYD_ST_STEP or SYD_ST_CONTIN */
int i;
int new=1;
assert(pSspec != NULL);
assert(strlen(chanName) > 0);
assert(strlen(chanName) < db_name_dim);
if (ppSChan != NULL)
*ppSChan = NULL;
/*-----------------------------------------------------------------------------
* allocate and initialize an empty synchronous channel structure. If this
* channel has already been opened as a data or test channel, then the
* previously used structure is used.
*----------------------------------------------------------------------------*/
if ((pSChan = sydChanFind(pSspec, chanName)) != NULL) {
if (pSChan->testChan) {
if (test)
return S_syd_ERROR;
if (pSChan->dataChan == 0)
new = 0;
}
else
new = 0;
}
if (new) {
pSChan = (SYD_CHAN *)malloc(sizeof(SYD_CHAN));
if (pSChan == NULL)
return S_syd_noMem;
DoubleListAppend(pSChan, pSspec->pChanHead, pSspec->pChanTail);
pSspec->chanCount++;
pSChan->pSspec = pSspec;
pSChan->pHandle = NULL;
pSChan->evid = NULL;
pSChan->nInBufs = pSspec->nInBufs;
for (i=0; i<pSChan->nInBufs; i++) {
pSChan->inStatus[i] = SYD_B_EMPTY;
pSChan->pInBuf[i] = NULL;
}
pSChan->firstInBuf = pSChan->lastInBuf = pSChan->sampInBuf = -1;
pSChan->minDataVal = pSChan->maxDataVal = 0.;
pSChan->minMaxNeedInit = 1;
pSChan->restrictMinDataVal = pSChan->restrictMaxDataVal = 0.;
strcpy(pSChan->name, chanName);
strcpy(pSChan->label, chanName);
strcat(pSChan->label, " not conn");
strcpy(pSChan->EGU, "none");
pSChan->sync = sync;
pSChan->isRVAL = 0;
pSChan->pData = NULL;
pSChan->pDataAlStat = pSChan->pDataAlSev = NULL;
pSChan->pDataCodeL = pSChan->pDataCodeR = NULL;
pSChan->pFlags = NULL;
pSChan->conn = 0;
pSChan->discon = 1;
pSChan->dataChan = test?0:1;
pSChan->pArg = NULL;
pSChan->pStats = NULL;
}
if (pArg != NULL && pSChan->pArg == NULL)
pSChan->pArg = pArg;
pSChan->testChan = test;
/*-----------------------------------------------------------------------------
* "open" the "source" for the data for the channel. This will provide
* information about how large the buffers need to be. If retrieval is
* from Channel Access, the buffer allocation, etc., will be deferred if
* the connection isn't complete.
*----------------------------------------------------------------------------*/
if (new) {
stat = (pSspec->pFunc)(pSspec, pSChan, SYD_FC_OPEN, pArg);
if (stat == S_syd_OK) {
stat = sydChanOpen1(pSspec, pSChan);
if (stat == S_syd_OK)
stat = sydChanOpenGR(pSChan);
}
else if (stat == S_syd_chanNotConn && pSspec->type == SYD_TY_CA) {
if (ppSChan != NULL)
*ppSChan = pSChan;
}
if (stat != S_syd_OK && stat != S_syd_chanNotConn) {
(void)sydChanClose(pSspec, pSChan);
return stat;
}
retStat = stat;
}
if (ppSChan != NULL)
*ppSChan = pSChan;
return retStat;
}
/*+/internal******************************************************************
* NAME sydChanOpen1 - open completion, after connect is complete
*
*-*/
long
sydChanOpen1(pSspec, pSChan)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
SYD_CHAN *pSChan; /* I sync channel pointer */
{
int i;
long retStat=S_syd_OK;
assert(pSspec != NULL);
assert(pSChan != NULL);
pSChan->conn = 1;
pSChan->discon = 0;
/*-----------------------------------------------------------------------------
* allocate the required number of input buffers
* set either SMOOTH or STEP for the channel
*----------------------------------------------------------------------------*/
for (i=0; i<pSChan->nInBufs; i++) {
pSChan->pInBuf[i] = (union db_access_val *)malloc(
dbr_size_n(pSChan->dbrType, pSChan->elCount));
if (pSChan->pInBuf[i] == NULL) {
(void)sydChanClose(pSspec, pSChan);
retStat = S_syd_noMem;
}
}
return retStat;
}
/*+/internal******************************************************************
* NAME sydChanOpenGR - handle graphics info at open time, connect done
*
* DESCRIPTION
* Sets prec, EGU, and label. If the channel is a .RVAL channel,
* prec is set to 0 and EGU is set to `counts'.
*
* `isRVAL' is set for .RVAL channels.
* `step' is set to SYD_ST_SMOOTH, or _STEP for ENUM channels
*
*-*/
long
sydChanOpenGR(pSChan)
SYD_CHAN *pSChan; /* I sync channel pointer */
{
char *pC;
if (pSChan->dbrType != DBR_TIME_ENUM)
pSChan->step = SYD_ST_SMOOTH;
else
pSChan->step = SYD_ST_STEP;
if ((pC = strrchr(pSChan->name, '.')) != NULL &&
strcmp(pC, ".RVAL") == 0) {
pSChan->precision = 0;
strcpy(pSChan->EGU, "counts");
pSChan->isRVAL = 1;
}
else if (pSChan->dbrType == DBR_TIME_FLOAT) {
pSChan->precision = pSChan->grBuf.gfltval.precision;
strcpy(pSChan->EGU, pSChan->grBuf.gfltval.units);
}
else if (pSChan->dbrType == DBR_TIME_SHORT) {
pSChan->precision = 0;
strcpy(pSChan->EGU, pSChan->grBuf.gshrtval.units);
}
else if (pSChan->dbrType == DBR_TIME_DOUBLE) {
pSChan->precision = pSChan->grBuf.gdblval.precision;
strcpy(pSChan->EGU, pSChan->grBuf.gdblval.units);
}
else if (pSChan->dbrType == DBR_TIME_LONG) {
pSChan->precision = 0;
strcpy(pSChan->EGU, pSChan->grBuf.glngval.units);
}
else if (pSChan->dbrType == DBR_TIME_CHAR) {
pSChan->precision = 0;
strcpy(pSChan->EGU, pSChan->grBuf.gchrval.units);
}
else if (pSChan->dbrType == DBR_TIME_ENUM) {
pSChan->precision = -1;
strcpy(pSChan->EGU, " ");
}
else {
pSChan->precision = 0;
strcpy(pSChan->EGU, " ");
}
sprintf(pSChan->label, "%s %s", pSChan->name, pSChan->EGU);
if (pSChan->dbrType != DBR_TIME_ENUM && pSChan->isRVAL == 0) {
if (pSChan->precision > 10 || pSChan->precision <= 0)
pSChan->precision = 3;
}
if (*pSChan->EGU == ' ' || strlen(pSChan->EGU) == 0)
strcpy(pSChan->EGU, "none");
return S_syd_OK;
}
/*+/subr**********************************************************************
* NAME sydChanPrep - prepare a channel for a new retrieval
*
* DESCRIPTION
* This routine initializes a channel structure in preparation for
* a new retrieval.
*
* RETURNS
* S_syd_OK
*
* SEE ALSO
* sydSampleSetGet, sydInputGet, sydInputStoreInSet
*
*-*/
long
sydChanPrep(pSspec, pSChan)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
SYD_CHAN *pSChan; /* I pointer to synchronous channel descriptor */
{
int i;
assert(pSspec != NULL);
assert(pSChan != NULL);
pSChan->minDataVal = pSChan->maxDataVal = 0.;
pSChan->minMaxNeedInit = 1;
pSChan->restrictMinDataVal = pSChan->restrictMaxDataVal = 0.;
}
/*+/subr**********************************************************************
* NAME sydClose - close a synchronous set spec
*
* DESCRIPTION
* This routine closes a synchronous set spec, closing channels, etc.,
* wrapping up the retrieval "source", and then free()'s the data
* structures associated with the set.
*
* RETURNS
* S_syd_OK, or
* other status codes if an error occurs
*
* BUGS
* o the `wrapup' call should do the free, since the free must
* match the malloc--if this routine is compiled with DEBUG and
* the routine which did the malloc (e.g., sydSubrCA) is compiled
* without it, then the free here will complain.
*
* SEE ALSO
* sydOpen
*
* EXAMPLE
*
*-*/
long
sydClose(pSspec)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
{
long retStat=S_syd_OK;/* return status to caller */
long stat;
assert(pSspec != NULL);
while (pSspec->pChanHead != NULL) {
if (sydChanClose(pSspec, pSspec->pChanHead) != S_syd_OK)
(void)printf("sydClose: error closing channel\n");
}
sydSampleSetFree(pSspec);
stat = (pSspec->pFunc)(pSspec, NULL, SYD_FC_WRAPUP, NULL);
if (stat != S_syd_OK)
retStat = stat;
free((char *)pSspec);
return retStat;
}
/*+/subr**********************************************************************
* NAME sydCopyGr
*
* DESCRIPTION
* Copies the graphics information from the Channel Access buffer into
* the caller's buffer. The CA buffer must be a DBR_GR type; the
* caller's buffer is assumed to be the same type.
*
*-*/
void
sydCopyGr(pDest, pSrc, srcDbrType)
void *pDest; /* O pointer to buffer to receive graphics info */
void *pSrc; /* I pointer to buffer holding graphics info */
chtype srcDbrType; /* I DBR_xxx type of the source buffer */
{
#define U_DB union db_access_val *
if (srcDbrType == DBR_GR_FLOAT) {
((U_DB)pDest)->gfltval = ((U_DB)pSrc)->gfltval;
((U_DB)pDest)->gfltval.units[7] = '\0';
}
else if (srcDbrType == DBR_GR_SHORT) {
((U_DB)pDest)->gshrtval = ((U_DB)pSrc)->gshrtval;
((U_DB)pDest)->gshrtval.units[7] = '\0';
}
else if (srcDbrType == DBR_GR_DOUBLE) {
((U_DB)pDest)->gdblval = ((U_DB)pSrc)->gdblval;
((U_DB)pDest)->gdblval.units[7] = '\0';
}
else if (srcDbrType == DBR_GR_LONG) {
((U_DB)pDest)->glngval = ((U_DB)pSrc)->glngval;
((U_DB)pDest)->glngval.units[7] = '\0';
}
else if (srcDbrType == DBR_GR_STRING)
((U_DB)pDest)->gstrval = ((U_DB)pSrc)->gstrval;
else if (srcDbrType == DBR_GR_ENUM)
((U_DB)pDest)->genmval = ((U_DB)pSrc)->genmval;
else if (srcDbrType == DBR_GR_CHAR) {
((U_DB)pDest)->gchrval = ((U_DB)pSrc)->gchrval;
((U_DB)pDest)->gchrval.units[7] = '\0';
}
else
assertAlways(0);
}
/*+/subr**********************************************************************
* NAME sydCopyVal - copy channel value
*
* DESCRIPTION
* Copies the value from the Channel Access buffer into the caller's
* buffer. The CA buffer can be any valid DBR_xxx type; the caller's
* buffer is assumed to be the corresponding DBF_xxx type--i.e., with
* room only for the value, and not the status, time stamp, etc.
*
*-*/
void
sydCopyVal(pDest, pSrc, count, srcDbrType)
void *pDest; /* O pointer to buffer to receive value */
void *pSrc; /* I pointer to buffer holding value */
int count; /* I array element count */
chtype srcDbrType; /* I DBR_xxx type of the source buffer */
{
int i;
if (dbr_type_is_FLOAT(srcDbrType)) {
float *pD=(float *)pDest;
float *pS=(float *)dbr_value_ptr(pSrc, srcDbrType);
for (i=0; i<count; i++)
*pD++ = *pS++;
}
else if (dbr_type_is_SHORT(srcDbrType)) {
short *pD=(short *)pDest;
short *pS=(short *)dbr_value_ptr(pSrc, srcDbrType);
for (i=0; i<count; i++)
*pD++ = *pS++;
}
else if (dbr_type_is_DOUBLE(srcDbrType)) {
double *pD=(double *)pDest;
double *pS=(double *)dbr_value_ptr(pSrc, srcDbrType);
for (i=0; i<count; i++)
*pD++ = *pS++;
}
else if (dbr_type_is_LONG(srcDbrType)) {
long *pD=(long *)pDest;
long *pS=(long *)dbr_value_ptr(pSrc, srcDbrType);
for (i=0; i<count; i++)
*pD++ = *pS++;
}
else if (dbr_type_is_STRING(srcDbrType)) {
char *pD=(char *)pDest;
char *pS=(char *)dbr_value_ptr(pSrc, srcDbrType);
for (i=0; i<count; i++) {
strcpy(pD, pS);
pD += db_strval_dim;
pS += db_strval_dim;
}
}
else if (dbr_type_is_ENUM(srcDbrType)) {
short *pD=(short *)pDest;
short *pS=(short *)dbr_value_ptr(pSrc, srcDbrType);
for (i=0; i<count; i++)
*pD++ = *pS++;
}
else if (dbr_type_is_CHAR(srcDbrType)) {
unsigned char *pD=(unsigned char *)pDest;
unsigned char *pS=(unsigned char *)dbr_value_ptr(pSrc, srcDbrType);
for (i=0; i<count; i++)
*pD++ = *pS++;
}
}
/*+/subr**********************************************************************
* NAME sydFileInfo - print information about the data file
*
* DESCRIPTION
* This routine prints some information about the source of data for
* the synchronous sample set. Typically, this will be information
* about the sample range and channel names in a data file.
*
* RETURNS
* S_syd_OK, or
* S_syd_ERROR
*
* BUGS
* o text
*
*-*/
long
sydFileInfo(pSspec, outStream)
SYD_SPEC *pSspec; /* O pointer to synchronous set spec */
FILE *outStream; /* I file pointer to receive information */
{
long retStat=S_syd_OK;/* return status to caller */
long stat;
assert(pSspec != NULL);
stat = (pSspec->pFunc)(pSspec, NULL, SYD_FC_FILEINFO, outStream);
if (stat != S_syd_OK)
retStat = S_syd_ERROR;
return retStat;
}
/*+/subr**********************************************************************
* NAME sydInputGet - get the next synchronous sample
*
* DESCRIPTION
* This routine acquires the next sample for the channels in the
* synchronous set spec. For retrievals from Channel Access when
* the caller is using his own monitor handler, calling this
* routine does nothing and isn't necessary.
*
* The desired time stamp for the `next sample' is determined by
* searching forward from the time of the previous sample. Among the
* channels in the synchronous set spec, the one which changes soonest
* is the one whose time stamp is used for the next sample. Values are
* handled on either a "non-filled" or a "filled" basis.
*
* For "filled" channels, it is assumed that if a change isn't
* detected in a channel's value, then the present value (i.e., the
* value at the desired time stamp) must be the same as it was when
* it last changed. If there is no data at the desired time stamp for
* one or more of the channels, then the return status will be
* S_syd_partial. There is no data for a "filled" channel
* if the desired time stamp is prior to the first value for the
* channel or if the desired time stamp is after the last value in
* the file for the channel.
*
* For "non-filled" channels, it is assumed that if there is no value
* for the desired time stamp then the channel has missing data at
* that time; in this case the return status will be S_syd_partial.
*
* When retrieving from Channel Access, the situation is slightly more
* complicated. Since there may be an unpredictable delay in receiving
* a monitored value for a channel, values are buffered prior to
* assembling a synchronous sample. If one or more channels don't
* have a value for the time stamp, it may be because appropriate data
* haven't arrived yet. If this is the case, and if no other channels
* are about to overflow their buffers, then a return status of
* S_syd_noDataNow is generated. When S_syd_noDataNow is returned, the
* caller should try again later. Because of the nature of acquiring
* data using Channel Access monitors, it is recommended that this
* routine be called repeatedly until a status of S_syd_noDataNow is
* returned, and then check periodically until more data are available.
* The caller must periodically call ca_pend_event in order to allow
* Channel Access to give data to the sydXxx routines.
*
* There are two main ways of dealing with samples obtained from this
* routine--use the other sydXxx routines to manipulate it or access
* it directly for manipulation by user code. The first case is
* the simplest, and would probably use sydSampleSetGet rather than
* this routine. The second alternative is described in more detail
* below.
*
* The status of the sample for a channel is obtained with the
* SydInputStatus macro; several cases may be encountered:
* o status is SYD_B_FULL, SYD_B_RESTART, or SYD_B_SAMPLED; the
* buffer contains valid sample data. Once the caller has dealt
* with the sample, THE CALLER MUST call SydInputMarkAsSampled!!!
* o status is SYD_B_MISSING or SYD_B_EOF; no data is available
* o statuses of SYD_B_SNAP_BEGIN, SYD_B_SNAP_SINGLE, and
* SYD_B_SNAP_END can also occur; these can be treated as synonyms
* of SYD_B_FULL.
*
* RETURNS
* S_syd_OK all channels are present in sample
* S_syd_partial some channels are missing from sample;
* such channels will have .sampInBuf of -1
* or else buffer status will be SYD_B_EOF
* or SYD_B_MISSING
* S_syd_noDataNow for Channel Access retrievals, a sample
* isn't available now; try later
* S_syd_EOF end of file--no more samples available
*
* BUGS
* o should get and report actual number of bytes from the read
* o for AR sample set files, if all channels are missing for 2
* successive samples, EOF is reported, even though file isn't at end
*
* SEE ALSO
* sydChanOpen, sydSamplePrint, sydInputStoreInSet, sydSampleSetGet
*
* MACROS
* These macros extract fields from input buffers produced by sydInputGet.
* By using these macros, the programmer can avoid the need to look
* inside the sample structures.
*
* Each macro takes as an argument a pointer to a synchronous sample
* channel structure, as returned by sydChanOpen or sydChanFind.
*
* The SydInputValAsXxx macros must not be called if the value
* returned by SydInputStatus is SYD_B_MISSING or SYD_B_EOF.
*
* SydInputStatus(pSChan) obtain the input sample status for a
* channel. This will be one of the SYD_B_XXX values.
* SydInputTs(pSspec) obtain time stamp of the input
* sample. (Note that this uses the sample spec, not the
* channel pointer.)
* SydInputMarkAsSampled(pSChan) set the input sample status to
* SYD_B_SAMPLED
*
* Access the input value for a channel, following sydInputGet (these
* macros don't do any conversion--the one that is used must match
* the type of the input value)
* SydInputValAsFloat(pSChan) returns a float
* SydInputValAsDouble(pSChan) returns a double
* SydInputValAsShort(pSChan) returns a short
* SydInputValAsLong(pSChan) returns a long
* SydInputValAsChar(pSChan) returns a char
* SydInputValAsString(pSChan) returns a string (i.e., char *)
* SydInputValAsVoidPtr(pSChan) returns a void * pointer to
* value (useful for array channels)
*
* Additional macros are described under sydChanOpen.
*
* NOTES
* 1. The `more flag' is for use only with Channel Access. It serves as
* a hint that another call to sydInputGet will be successful. This
* is useful for programs which want to throw away all but the most
* recent sample if they fall behind. The technique with such
* programs is to repeatedly call sydInputGet until the return status
* is S_syd_noDataNow, and process only those cases in which the
* `more flag' is returned as zero. (SydInputMarkAsSampled must
* be called for the skipped samples, as well as for those which
* are processed.) It may be interesting to note that a zero value
* doesn't guarantee that the next call will result in a status of
* S_syd_noDataNow.
* 2. A sample for a channel can have a valid time stamp and still have
* a status of SYD_B_MISSING. The main way this happens is when
* reading a `sample set' file.
*
* EXAMPLE
* Get the next sample, then print the values and statuses for the
* channels. If Channel Access has queued up several values from the
* IOC, then several samples will probably be processed. This example
* ignores the differences between SYD_B_FULL, SYD_B_RESTART, and
* SYD_B_SAMPLED.
*
* SYD_CHAN *pSChan1, *pSChan2, ...; pointers to channel structures
* int status;
* float value;
* char timeText[28];
* int moreFlag;
*
* ca_pend_event(.1);
* stat = sydInputGet(pSspec, &moreFlag);
* while (stat == S_syd_OK || stat == S_syd_partial) {
* tsStampToText(&SydInputTs(pSspec), TS_TEXT_MMDDYY, timeText);
* printf("sample time stamp is %s\n", timeText);
* if (stat == S_syd_partial)
* printf("one or more channels don't have data\n");
* status = SydInputStatus(pSChan1);
* if (status == SYD_B_MISSING || status == SYD_B_EOF)
* printf("%s has missing data\n", pSChan1->name);
* else {
* value = SydInputValAsFloat(pSChan1);
* printf("%s value is: %f\n", value);
* SydInputMarkAsSampled(pSChan1);
* }
*
* stat = sydInputGet(pSspec, &moreFlag); try for another sample
* }
*
*-*/
long
sydInputGet(pSspec, pMoreFlag)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
int *pMoreFlag; /* O pointer to flag or NULL; a return value of 1
indicates more data is available; 0 indicates
uncertainty about whether more is available */
{
long retStat=S_syd_OK;/* return status to caller */
long stat; /* status return from calls */
int readIndx; /* select buffer to use for read */
SYD_CHAN *pSChan; /* pointer to channel in Sspec */
double refDiff; /* difference from reference time */
TS_STAMP nextTs; /* time stamp for next sample */
int i, i1, i2, j, now=0, later=0;
int moreFlag=1; /* assume there's more; reset if not */
enum sydBStatus inStatus_i, inStatus_i1, inStatus_i2;
int full_i, full_i1, full_i2;
assert(pSspec != NULL);
assert(pSspec->pChanHead != NULL);
nextTs.secPastEpoch = 0;
nextTs.nsec = 0;
pSspec->eofCount = 0;
#define BUFiTS pSChan->pInBuf[i]->tstrval.stamp
#define BUFi1TS pSChan->pInBuf[i1]->tstrval.stamp
/*-----------------------------------------------------------------------------
* try to get at least two "available" buffers of input data
*----------------------------------------------------------------------------*/
sydInputGetIn(pSspec);
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
/*-----------------------------------------------------------------------------
* with two buffers of data, determine if this channel can contribute to
* the time stamp for the sample. It can potentially contribute if:
* o .firstInBuf is SYD_B_FULL or SYD_B_RESTART or SYD_B_MISSING, or
* o .firstInBUf is SYD_B_SAMPLED and the next buffer is SYD_B_FULL
*
* For SYD_TY_CA, some buffer checks are performed. If a channel is
* about to lose data (because buffer is nearly full), then the `now'
* flag is set to force generating a partial sample. If a channel has
* no data, then the `later' flag is set to gently request returning a
* status of S_syd_noDataNow; this is also done if the channel has only
* one value and its status is SYD_B_SAMPLED.
*
* the time stamp for the sample is the smallest time stamp of all
* the channels which can contribute. If no SYD_B_FULL or SYD_B_RESTART or
* SYD_B_MISSING buffers are available, then .nextTs will be 0 .
*----------------------------------------------------------------------------*/
i = pSChan->firstInBuf;
inStatus_i = pSChan->inStatus[i];
full_i = inStatus_i == SYD_B_FULL || inStatus_i == SYD_B_RESTART ||
inStatus_i == SYD_B_SNAP_BEGIN ||
inStatus_i == SYD_B_SNAP_SINGLE || inStatus_i == SYD_B_SNAP_END;
i1 = NEXT_INBUF(pSChan, i);
inStatus_i1 = pSChan->inStatus[i1];
full_i1 = inStatus_i1 == SYD_B_FULL || inStatus_i1 == SYD_B_RESTART ||
inStatus_i1 == SYD_B_SNAP_BEGIN ||
inStatus_i1 == SYD_B_SNAP_SINGLE || inStatus_i1 == SYD_B_SNAP_END;
if (i < 0)
later = 1;
else if (full_i) {
if (pSspec->type == SYD_TY_CA) {
j = pSChan->lastInBuf;
if (j < pSChan->firstInBuf)
j += pSChan->nInBufs;
if (pSChan->nInBufs - (j - pSChan->firstInBuf +1) < 3)
now = 1; /* 3 or fewer empty slots left */
}
if (nextTs.secPastEpoch == 0 || TsCmpStampsLT(&BUFiTS, &nextTs))
nextTs = BUFiTS;
}
else if (inStatus_i == SYD_B_MISSING) {
if (TsCmpStampsLT(&BUFiTS, &nextTs))
nextTs = BUFiTS;
}
else if (full_i1) {
if (pSspec->type == SYD_TY_CA) {
j = pSChan->lastInBuf;
if (j < pSChan->firstInBuf)
j += pSChan->nInBufs;
if (pSChan->nInBufs - (j - pSChan->firstInBuf +1) < 3)
now = 1; /* 3 or fewer empty slots left */
}
if (nextTs.secPastEpoch == 0 || TsCmpStampsLT(&BUFi1TS, &nextTs))
nextTs = BUFi1TS;
}
else if (inStatus_i1 == SYD_B_MISSING) {
if (TsCmpStampsLT(&BUFi1TS, &nextTs))
nextTs = BUFi1TS;
}
else if (i == pSChan->lastInBuf && inStatus_i == SYD_B_SAMPLED) {
later = 1;
}
else
later = 1;
}
/*----------------------------------------------------------------------------
* now that we have as much info as possible to play with, figure out how
* to put together a sample to return to the caller. If no data at all
* are available, just return an EOF notification (or, for SYD_TY_CA, give
* a noDataNow notification).
*----------------------------------------------------------------------------*/
if (pSspec->type == SYD_TY_CA && now == 0 && later == 1) {
return S_syd_noDataNow;
}
if (nextTs.secPastEpoch == 0)
if (pSspec->type != SYD_TY_CA)
return S_syd_EOF;
else
return S_syd_noDataNow;
if (pSspec->refTs.secPastEpoch == 0 || pSspec->sampleCount == 0) {
/*----------------------------------------------------------------------------
* It is necessary to initialize the reference time stamp in the Sspec
* descriptor. The reference time stamp starts on a one second boundary.
*----------------------------------------------------------------------------*/
pSspec->refTs.secPastEpoch = nextTs.secPastEpoch;
pSspec->refTs.nsec = 0;
pSspec->restrictRefTs = pSspec->refTs;
}
/*----------------------------------------------------------------------------
* A channel may be included in the sample if either its oldest or its
* second oldest buffer has a valid time stamp. A buffer will have a
* valid time stamp if its status is SYD_B_FULL, SYD_B_RESTART, or
* SYD_B_MISSING.
*
* The oldest buffer is included in the sample a) if its time stamp is
* <= to the retrieval time stamp; or b) if its time stamp is less
* than the retrieval time stamp and the second oldest buffer isn't
* eligible to include in the sample. If the oldest buffer IS included,
* the sample status will be `partial' if the buffer status is either
* SYD_B_MISSING or SYD_B_EOF. If a previously sampled value is used for
* the current sample, then .reused is set to 1 .
*
* If the oldest buffer isn't included in the sample, then the second
* oldest buffer will be included if its time stamp is <= to the retrieval
* time stamp. If the second oldest buffer is included in the sample,
* then the oldest buffer is flagged as empty.
*
* For Channel Access retrievals, the moreFlag will be reset if:
* o a channel doesn't contribute to the sample; or
* o a channel doesn't have one more SYD_B_FULL buffer
*----------------------------------------------------------------------------*/
pSspec->priorTs = pSspec->sampleTs;
pSspec->sampleTs = nextTs;
pSspec->partial = 0;
TsDiffAsDouble(&pSspec->sampleSec, &pSspec->sampleTs, &pSspec->refTs);
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
i = pSChan->firstInBuf;
inStatus_i = pSChan->inStatus[i];
full_i = inStatus_i == SYD_B_FULL || inStatus_i == SYD_B_RESTART ||
inStatus_i == SYD_B_SNAP_BEGIN ||
inStatus_i == SYD_B_SNAP_SINGLE || inStatus_i == SYD_B_SNAP_END;
i1 = NEXT_INBUF(pSChan, i);
inStatus_i1 = pSChan->inStatus[i1];
full_i1 = inStatus_i1 == SYD_B_FULL || inStatus_i1 == SYD_B_RESTART ||
inStatus_i1 == SYD_B_SNAP_BEGIN ||
inStatus_i1 == SYD_B_SNAP_SINGLE || inStatus_i1 == SYD_B_SNAP_END;
if (i < 0) {
pSChan->sampInBuf = -1;
moreFlag = 0;
pSspec->partial = 1;
retStat = S_syd_partial;
}
else if (full_i && TsCmpStampsLE(&BUFiTS, &nextTs)) {
pSChan->sampInBuf = i;
if (inStatus_i1 != SYD_B_FULL)
moreFlag = 0;
pSChan->reused = 0;
}
else if (pSChan->inStatus[i] == SYD_B_SAMPLED ||
pSChan->inStatus[i] == SYD_B_MISSING) {
if ((full_i1 || inStatus_i1 == SYD_B_MISSING) &&
TsCmpStampsLE(&BUFi1TS, &nextTs)) {
pSChan->inStatus[i] = SYD_B_EMPTY;
pSChan->firstInBuf = i1;
pSChan->reused = 0;
pSChan->sampInBuf = i1;
i2 = NEXT_INBUF(pSChan, i1);
inStatus_i2 = pSChan->inStatus[i2];
full_i2 = inStatus_i2 == SYD_B_FULL ||
inStatus_i2 == SYD_B_RESTART ||
inStatus_i2 == SYD_B_SNAP_BEGIN ||
inStatus_i2 == SYD_B_SNAP_SINGLE ||
inStatus_i2 == SYD_B_SNAP_END;
if (!full_i2)
moreFlag = 0;
}
else {
if (pSChan->inStatus[i] == SYD_B_SAMPLED)
pSChan->reused = 1;
pSChan->sampInBuf = i;
if (!full_i1)
moreFlag = 0;
}
}
else if (inStatus_i == SYD_B_EOF) {
pSChan->sampInBuf = i;
moreFlag = 0;
pSspec->eofCount++;
pSspec->partial = 1;
retStat = S_syd_partial;
}
else {
pSChan->sampInBuf = -1;
moreFlag = 0;
pSspec->partial = 1;
retStat = S_syd_partial;
}
if (pSChan->sampInBuf >= 0 &&
pSChan->inStatus[pSChan->sampInBuf] == SYD_B_MISSING) {
moreFlag = 0;
pSspec->partial = 1;
retStat = S_syd_partial;
}
}
if (pMoreFlag != NULL)
*pMoreFlag = moreFlag;
return retStat;
}
/*+/internal******************************************************************
* NAME sydInputGetIn - get new input data, possibly discarding old
*
* DESCRIPTION
* Checks input buffers, discarding old buffers, if possible. Then,
* except for SYD_TY_CA, an attempt is made to fill 2 input buffers.
* Finally, the time stamps are checked for the first 2 buffers--if
* either is 0, it is set to 1.
*
* RETURNS
* NULL
*
* BUGS
* o text
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
static void
sydInputGetIn(pSspec)
SYD_SPEC *pSspec; /* IO pointer to synchronous set spec */
{
long stat; /* status return from calls */
SYD_CHAN *pSChan; /* pointer to channel in Sspec */
int i, i1, discard;
enum sydBStatus inStatus_i1;
int full_i1;
assert(pSspec != NULL);
assert(pSspec->pChanHead != NULL);
/*----------------------------------------------------------------------------
* try to get both buffers for the channel to be SYD_B_FULL, performing
* reads as necessary. When processing is complete, buffer status will be
* either SYD_B_FULL, SYD_B_SAMPLED, SYD_B_MISSING, SYD_B_EMPTY,
* or SYD_B_EOF.
*----------------------------------------------------------------------------*/
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
/*-----------------------------------------------------------------------------
* see if the oldest input can be thrown away. This is possible if:
* o buffer status is SYD_B_SAMPLED and one of the following is also true:
* - channel is SYD_SY_NONF, or
* - channel is SYD_SY_FILLED, and either:
* . the next buffer's status is SYD_B_FULL (or RESTART), and the
* next buffer's time stamp is less than or equal to the previous
* sample time stamp; or,
* . the next buffer's status is SYD_B_MISSING, and the next
* buffer's time stamp is less than or equal to the previous
* sample time stamp; or,
* o buffer status is SYD_B_MISSING and:
* - the next buffer's status is SYD_B_FULL (or RESTART), and the next
* buffer's time stamp is less than or equal to the previous
* sample time stamp
*
*----------------------------------------------------------------------------*/
if ((i = pSChan->firstInBuf) >= 0) {
discard = 0;
i1 = NEXT_INBUF(pSChan, i);
inStatus_i1 = pSChan->inStatus[i1];
full_i1 = inStatus_i1==SYD_B_FULL || inStatus_i1==SYD_B_RESTART ||
inStatus_i1==SYD_B_SAMPLED || inStatus_i1==SYD_B_SNAP_BEGIN ||
inStatus_i1==SYD_B_SNAP_SINGLE || inStatus_i1==SYD_B_SNAP_END;
if (pSChan->inStatus[i] == SYD_B_SAMPLED) {
if (pSChan->sync == SYD_SY_NONF)
discard = 1;
else if (inStatus_i1 == SYD_B_MISSING || full_i1) {
if (TsCmpStampsLE(&BUFi1TS, &pSspec->sampleTs))
discard = 1;
}
}
else if (pSChan->inStatus[i] == SYD_B_MISSING) {
if (pSChan->sync == SYD_SY_NONF &&
TsCmpStampsLE(&BUFiTS, &pSspec->sampleTs)) {
discard = 1;
}
else if (full_i1) {
if (TsCmpStampsLE(&BUFi1TS, &pSspec->sampleTs))
discard = 1;
}
else if (inStatus_i1 == SYD_B_MISSING)
discard = 1;
}
if (discard) {
pSChan->inStatus[i] = SYD_B_EMPTY;
if (i == pSChan->lastInBuf) {
pSChan->lastInBuf = -1;
pSChan->firstInBuf = -1;
}
else
pSChan->firstInBuf = NEXT_INBUF(pSChan, i);
}
}
}
/*-----------------------------------------------------------------------------
* now try to get 2 buffers of input data (except for CA), then check for
* zero time stamps. If a zero time stamp is found, it is set to 1
* second past the epoch. (This is done because a lot of the code depends
* on non-zero time stamp as a way of detecting valid data.)
*----------------------------------------------------------------------------*/
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
if (pSspec->type != SYD_TY_CA) {
if (pSChan->firstInBuf < 0)
stat = (pSspec->pFunc)(pSspec, pSChan, SYD_FC_READ, NULL);
if (pSChan->firstInBuf == pSChan->lastInBuf)
stat = (pSspec->pFunc)(pSspec, pSChan, SYD_FC_READ, NULL);
skipSecondRead:
;
}
if ((i = pSChan->firstInBuf) >= 0) {
if (BUFiTS.secPastEpoch == 0)
BUFiTS.secPastEpoch = 86401;
i = NEXT_INBUF(pSChan, i);
if (i >= 0 && BUFiTS.secPastEpoch == 0)
BUFiTS.secPastEpoch = 86401;
}
if (pSspec->type == SYD_TY_SSF)
break;
}
}
/*+/subr**********************************************************************
* NAME sydInputReset - reset synchronous sample buffers for channels
*
* DESCRIPTION
* This routine flags as EMPTY the input buffers for the channels in a
* synchronous set spec. Several routines are available:
*
* sydInputReset flags as empty all buffers for all channels
* This routine is appropriate to use after a
* positioning operation, since such an operation
* invalidates both FULL and SAMPLED buffers.
*
* sydInputResetKeepNewest flags as empty all buffers except the
* newest one, which is flagged as SAMPLED
* This routine is primarily intended for use with
* Channel Access. Keeping the newest buffer and
* flagging it as SAMPLED allows: a) using it if
* no new value comes in; or b) throwing it away
* if a new value does come in.
*
* sydInputResetSampled flags as empty only those buffers which
* are flagged as SAMPLED
* This routine is appropriate to use between
* successive retrievals. Since
* there is no "gap" between retrievals, the FULL
* buffers must be available to the next retrieval.
* Making SAMPLED buffers available for the next
* retrieval could result in the next retrieval
* having the same retrieval time stamp as the
* prior retrieval.
*
* RETURNS
* S_syd_OK
*
* BUGS
* o text
*
* NOTES
* 1. For retrievals from Channel Access, sydInputReset retains the
* newest buffer for filled channels.
*
*-*/
void
sydInputReset(pSspec)
SYD_SPEC *pSspec; /* IO pointer to synchronous set spec */
{
SYD_CHAN *pSChan;
int i;
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
i = pSChan->firstInBuf;
while (i >= 0 && i != pSChan->lastInBuf) {
pSChan->inStatus[i] = SYD_B_EMPTY;
i = NEXT_INBUF(pSChan, i);
pSChan->firstInBuf = i;
}
if ((i=pSChan->firstInBuf) >= 0) {
if (pSspec->type == SYD_TY_CA && pSChan->sync == SYD_SY_FILLED)
pSChan->inStatus[i] = SYD_B_FULL;
else {
pSChan->inStatus[i] = SYD_B_EMPTY;
pSChan->firstInBuf = pSChan->lastInBuf = -1;
}
}
pSChan->sampInBuf = -1;
}
pSspec->sampleTs.secPastEpoch = 0;
}
/*+/subr**********************************************************************
* NAME sydInputResetKeepNewest
*
*-*/
void
sydInputResetKeepNewest(pSspec)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
{
SYD_CHAN *pSChan;
int i;
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
i = pSChan->firstInBuf;
while (i >= 0 && i != pSChan->lastInBuf) {
pSChan->inStatus[i] = SYD_B_EMPTY;
i = NEXT_INBUF(pSChan, i);
pSChan->firstInBuf = i;
}
if ((i=pSChan->firstInBuf) > 0)
pSChan->inStatus[i] = SYD_B_SAMPLED;
pSChan->sampInBuf = -1;
}
pSspec->sampleTs.secPastEpoch = 0;
}
/*+/subr**********************************************************************
* NAME sydInputResetSampled
*
*-*/
void
sydInputResetSampled(pSspec)
SYD_SPEC *pSspec; /* IO pointer to synchronous set spec */
{
SYD_CHAN *pSChan;
int i;
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
i = pSChan->firstInBuf;
while (i >= 0) {
if (pSChan->inStatus[i] == SYD_B_SAMPLED) {
pSChan->inStatus[i] = SYD_B_EMPTY;
if (pSChan->lastInBuf == i) {
pSChan->lastInBuf = -1;
pSChan->firstInBuf = -1;
i = -1;
}
else if (pSChan->firstInBuf == i)
pSChan->firstInBuf = NEXT_INBUF(pSChan, i);
}
if (i == pSChan->lastInBuf)
i = -1; /* leave the loop */
else
i = NEXT_INBUF(pSChan, i);
}
pSChan->sampInBuf = -1;
}
pSspec->sampleTs.secPastEpoch = 0;
}
/*+/subr**********************************************************************
* NAME sydInputStoreInSet - store a sample into the sample set
*
* DESCRIPTION
* Stores a sample acquired by sydInputGet into the next available
* sample set buffer. Flags for the sample set buffer are set
* appropriately. The minDataVal and maxDataVal for the channel
* are updated (unless the alarm severity indicates INVALID).
*
* If a test condition has been established by sydTestAddFromText,
* the condition is tested. If the condition is false, then the
* sample isn't stored.
*
* If the `ignorePartial' flag is set, then some special processing
* is done to avoid losing interesting status information for
* synchronous channels.
*
* RETURNS
* void
*
* BUGS
* o text
*
* NOTES
* 1. If the retrieval source is Channel Access, and if the time stamp
* for this sample is earlier than the previous sample, then this
* sample is ignored.
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
void
sydInputStoreInSet(pSspec, ignorePartial)
SYD_SPEC *pSspec;
int ignorePartial; /* I 0,1 to store,ignore partial samples */
{
int sub; /* subscript of sample */
chtype type;
long stat;
SYD_CHAN *pSChan;
int i, i1, el;
int useVal; /* indicates if sample data is to be stored */
short alStat, alSev;
int ignore=0;
#define Si pSChan->inStatus[i]
#define Si1 pSChan->inStatus[i1]
sub = pSspec->lastData;
if (ignorePartial && pSspec->partial)
ignore = 1;
else if (!sydTest(pSspec))
ignore = 1;
else if (sub >= 0 && pSspec->type == SYD_TY_CA &&
TsCmpStampsLE(&pSspec->sampleTs, &pSspec->pTimeStamp[sub])) {
ignore = 1;
}
if (ignore) {
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
i = pSChan->sampInBuf;
if (i != pSChan->lastInBuf) {
i1 = i + 1;
if (i1 >= pSChan->nInBufs)
i1 = 0;
if (Si1==SYD_B_MISSING || Si1==SYD_B_EOF || Si1==SYD_B_EMPTY)
i1 = -1;
}
else
i1 = -1;
if (i >= 0) {
if (i1 >= 0) {
if (Si == SYD_B_RESTART)
pSChan->inStatus[i1] = SYD_B_RESTART;
else if (Si == SYD_B_SNAP_BEGIN) {
if (Si1 == SYD_B_SNAP_END)
pSChan->inStatus[i1] = SYD_B_SNAP_SINGLE;
else if (Si1 == SYD_B_SNAP_SINGLE)
pSChan->inStatus[i1] = SYD_B_SNAP_BEGIN;
}
}
if (Si != SYD_B_MISSING)
pSChan->inStatus[i] = SYD_B_SAMPLED;
}
}
return;
}
if (++pSspec->lastData >= pSspec->dataDim)
pSspec->lastData = 0;
sub = pSspec->lastData;
pSspec->pDeltaSec[sub] = pSspec->sampleSec;
pSspec->pTimeStamp[sub] = pSspec->sampleTs;
pSspec->pPartial[sub] = pSspec->partial;
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
if (pSChan->pData == NULL || pSChan->dataChan == 0)
; /* no action if no malloc's or if not data channel */
else if (pSChan->dataChan) {
bzero((char *)&pSChan->pFlags[sub], sizeof(*pSChan->pFlags));
pSChan->pDataCodeL[sub] = '#';
pSChan->pDataCodeR[sub] = ' ';
type = pSChan->dbrType;
if ((i = pSChan->sampInBuf) < 0) {
pSChan->pFlags[sub].missing = 1;
pSChan->pDataCodeL[sub] = 'M';
}
else if (pSChan->inStatus[i] == SYD_B_EOF) {
pSChan->pFlags[sub].missing = 1;
pSChan->pFlags[sub].eof = 1;
pSChan->pDataCodeL[sub] = 'M';
pSChan->pDataCodeR[sub] = 'E';
}
else if (pSChan->inStatus[i] == SYD_B_MISSING) {
pSChan->pFlags[sub].missing = 1;
pSChan->pDataCodeL[sub] = 'M';
pSChan->pDataCodeR[sub] = 'N';
}
else {
/*-----------------------------------------------------------------------------
* store the channel's alarm status and severity
*----------------------------------------------------------------------------*/
pSChan->pDataAlStat[sub] = alStat =
pSChan->pInBuf[i]->tstrval.status;
pSChan->pDataAlSev[sub] = alSev =
pSChan->pInBuf[i]->tstrval.severity;
/*-----------------------------------------------------------------------------
* check out the channel's status, storing it into the status array.
* If the status indicates a bad value, reset the useVal flag, so
* that the value won't be used in computing the maximum and minimum
* for the channel.
*----------------------------------------------------------------------------*/
useVal = 1;
if (alSev == INVALID_ALARM) {
useVal = 0;
pSChan->pDataCodeL[sub] = 'I';
}
else if (pSChan->reused) {
pSChan->pFlags[sub].filled = 1;
pSChan->pDataCodeL[sub] = 'F';
}
else if (pSChan->inStatus[i] == SYD_B_SNAP_BEGIN) {
pSChan->pFlags[sub].restart = 1;
pSChan->pFlags[sub].snapstart = 1;
pSChan->pDataCodeL[sub] = 'S';
}
else if (pSChan->inStatus[i] == SYD_B_SNAP_SINGLE) {
pSChan->pFlags[sub].restart = 1;
pSChan->pFlags[sub].snapstart = 1;
pSChan->pFlags[sub].snapend = 1;
pSChan->pDataCodeL[sub] = 'S';
}
else if (pSChan->inStatus[i] == SYD_B_SNAP_END) {
pSChan->pFlags[sub].ok = 1;
pSChan->pFlags[sub].snapend = 1;
pSChan->pDataCodeL[sub] = 'O';
}
else if (pSChan->inStatus[i] == SYD_B_RESTART) {
pSChan->pFlags[sub].restart = 1;
pSChan->pDataCodeL[sub] = 'R';
}
else {
pSChan->pFlags[sub].ok = 1;
pSChan->pDataCodeL[sub] = 'O';
}
if (alStat == NO_ALARM)
; /* no action */
else if (alStat == READ_ALARM)
pSChan->pDataCodeR[sub] = 'R';
else if (alStat == HIHI_ALARM)
pSChan->pDataCodeR[sub] = 'H';
else if (alStat == HIGH_ALARM)
pSChan->pDataCodeR[sub] = 'h';
else if (alStat == LOLO_ALARM)
pSChan->pDataCodeR[sub] = 'L';
else if (alStat == LOW_ALARM)
pSChan->pDataCodeR[sub] = 'l';
else if (alSev == MAJOR_ALARM)
pSChan->pDataCodeR[sub] = 'M';
else if (alSev == MINOR_ALARM)
pSChan->pDataCodeR[sub] = 'm';
/*-----------------------------------------------------------------------------
* now go ahead and store the sample into the set. Also adjust the
* maximum and minimum, as needed.
*----------------------------------------------------------------------------*/
if (type == DBR_TIME_FLOAT) {
float *pSrc, *pDest;
pSrc = &pSChan->pInBuf[i]->tfltval.value;
pDest = ((float *)pSChan->pData) + sub * pSChan->elCount;
if (useVal && pSChan->minMaxNeedInit) {
pSChan->maxDataVal = pSChan->minDataVal = (double)*pSrc;
pSChan->minMaxNeedInit = 0;
}
for (el=0; el<(int) pSChan->elCount; el++) {
if (useVal) {
if ((double)*pSrc > pSChan->maxDataVal)
pSChan->maxDataVal = (double)*pSrc;
if ((double)*pSrc < pSChan->minDataVal)
pSChan->minDataVal = (double)*pSrc;
}
*pDest++ = *pSrc++;
}
}
else if (type == DBR_TIME_SHORT) {
short *pSrc, *pDest;
double dbl;
pSrc = &pSChan->pInBuf[i]->tshrtval.value;
pDest = ((short *)pSChan->pData) + sub * pSChan->elCount;
if (useVal && pSChan->minMaxNeedInit) {
if (pSChan->isRVAL)
dbl = (double)(*(unsigned short *)pSrc);
else
dbl = (double)(*pSrc);
pSChan->maxDataVal = pSChan->minDataVal = dbl;
pSChan->minMaxNeedInit = 0;
}
for (el=0; el<(int) pSChan->elCount; el++) {
if (useVal) {
if (pSChan->isRVAL)
dbl = (double)(*(unsigned short *)pSrc);
else
dbl = (double)(*pSrc);
if (dbl > pSChan->maxDataVal)
pSChan->maxDataVal = dbl;
if (dbl < pSChan->minDataVal)
pSChan->minDataVal = dbl;
}
*pDest++ = *pSrc++;
}
}
else if (type == DBR_TIME_DOUBLE) {
double *pSrc, *pDest;
pSrc = &pSChan->pInBuf[i]->tdblval.value;
pDest = ((double *)pSChan->pData) + sub * pSChan->elCount;
if (useVal && pSChan->minMaxNeedInit) {
pSChan->maxDataVal = pSChan->minDataVal = *pSrc;
pSChan->minMaxNeedInit = 0;
}
for (el=0; el<(int) pSChan->elCount; el++) {
if (useVal) {
if (*pSrc > pSChan->maxDataVal)
pSChan->maxDataVal = *pSrc;
if (*pSrc < pSChan->minDataVal)
pSChan->minDataVal = *pSrc;
}
*pDest++ = *pSrc++;
}
}
else if (type == DBR_TIME_LONG) {
long *pSrc, *pDest;
double dbl;
pSrc = &pSChan->pInBuf[i]->tlngval.value;
pDest = ((long *)pSChan->pData) + sub * pSChan->elCount;
if (useVal && pSChan->minMaxNeedInit) {
if (pSChan->isRVAL)
dbl = (double)(*(unsigned long *)pSrc);
else
dbl = (double)(*pSrc);
pSChan->maxDataVal = pSChan->minDataVal = (double)*pSrc;
pSChan->minMaxNeedInit = 0;
}
for (el=0; el<(int) pSChan->elCount; el++) {
if (useVal) {
if (pSChan->isRVAL)
dbl = (double)(*(unsigned long *)pSrc);
else
dbl = (double)(*pSrc);
if (dbl > pSChan->maxDataVal)
pSChan->maxDataVal = dbl;
if (dbl < pSChan->minDataVal)
pSChan->minDataVal = dbl;
}
*pDest++ = *pSrc++;
}
}
else if (type == DBR_TIME_STRING) {
char *pSrc, *pDest;
pSrc = pSChan->pInBuf[i]->tstrval.value;
pDest = ((char *)pSChan->pData) +
sub * db_strval_dim * pSChan->elCount;
for (el=0; el<(int) pSChan->elCount; el++) {
strcpy(pDest, pSrc);
pDest += db_strval_dim;
pSrc += db_strval_dim;
}
}
else if (type == DBR_TIME_CHAR) {
unsigned char *pSrc, *pDest;
pSrc = &pSChan->pInBuf[i]->tchrval.value;
pDest = ((unsigned char *)pSChan->pData) +
sub * pSChan->elCount;
if (useVal && pSChan->minMaxNeedInit) {
pSChan->maxDataVal = pSChan->minDataVal = (double)*pSrc;
pSChan->minMaxNeedInit = 0;
}
for (el=0; el<(int) pSChan->elCount; el++) {
if (useVal) {
if ((double)*pSrc > pSChan->maxDataVal)
pSChan->maxDataVal = (double)*pSrc;
if ((double)*pSrc < pSChan->minDataVal)
pSChan->minDataVal = (double)*pSrc;
}
*pDest++ = *pSrc++;
}
}
else if (type == DBR_TIME_ENUM) {
unsigned short *pSrc, *pDest;
pSrc = &pSChan->pInBuf[i]->tenmval.value;
pDest = ((unsigned short *)pSChan->pData) + sub * pSChan->elCount;
if (useVal && pSChan->minMaxNeedInit) {
pSChan->maxDataVal = pSChan->minDataVal = (double)*pSrc;
pSChan->minMaxNeedInit = 0;
}
for (el=0; el<(int) pSChan->elCount; el++) {
if (useVal) {
if ((double)*pSrc > pSChan->maxDataVal)
pSChan->maxDataVal = (double)*pSrc;
if ((double)*pSrc < pSChan->minDataVal)
pSChan->minDataVal = (double)*pSrc;
}
*pDest++ = *pSrc++;
}
}
else
assertAlways(0);
}
if (i >= 0 && pSChan->inStatus[i] != SYD_B_MISSING)
pSChan->inStatus[i] = SYD_B_SAMPLED;
pSChan->restrictMaxDataVal = pSChan->maxDataVal;
pSChan->restrictMinDataVal = pSChan->minDataVal;
}
}
if (pSspec->firstData < 0 || pSspec->firstData == pSspec->lastData) {
if (++pSspec->firstData >= pSspec->dataDim)
pSspec->firstData = 0;
pSspec->restrictFirstData = pSspec->firstData;
}
if (pSspec->sampleCount < pSspec->dataDim)
pSspec->sampleCount++;
pSspec->restrictLastData = pSspec->lastData;
}
/*+/subr**********************************************************************
* NAME sydInputSync - synchronize input buffers with disk for `by channel'
*
* DESCRIPTION
* Get in sync with an archiver `by channel' data file, which may
* have had information written to it since the sydOpen call or
* the last sydInputSync call.
*
* RETURNS
* S_syd_OK, or
* other error code
*
* BUGS
* o text
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
sydInputSync(pSspec)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
{
long retStat=S_syd_OK;
long stat;
assert(pSspec != NULL);
if (pSspec->type != SYD_TY_CF)
return S_syd_OK;
stat = (pSspec->pFunc)(pSspec, NULL, SYD_FC_READ_SYNC, NULL);
if (stat != S_syd_OK)
retStat = stat;
return retStat;
}
/*+/subr**********************************************************************
* NAME sydMonitorStart - start monitoring channels for user monitoring
*
* DESCRIPTION
* For each channel in the sync set spec, add a Channel Access monitor.
* The monitor will use the deadband specified by the SYD_ATTR_DEADBAND
* attribute (which defaults to .ADEL).
*
* RETURNS
* S_syd_OK
*
* SEE ALSO
* sydChanOpen, sydSetAttr, sydMonitorStop
*
*-*/
long
sydMonitorStart(pSspec)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
{
long retStat=S_syd_OK;/* return status to caller */
long stat; /* status return from calls */
SYD_CHAN *pSChan; /* pointer to channel in Sspec */
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext)
stat = (pSspec->pFunc)(pSspec, pSChan, SYD_FC_READ, NULL);
return retStat;
}
/*+/subr**********************************************************************
* NAME sydMonitorStop - stop monitoring channels for user monitoring
*
* DESCRIPTION
* For each channel in the sync set spec, delete the Channel Access
* monitor. If the sync set spec isn't being monitored by a user
* routine, this routine does nothing.
*
* RETURNS
* S_syd_OK
*
* SEE ALSO
* sydChanOpen, sydSetAttr, sydMonitorStart
*
*-*/
long
sydMonitorStop(pSspec)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
{
long retStat=S_syd_OK;/* return status to caller */
long stat; /* status return from calls */
SYD_CHAN *pSChan; /* pointer to channel in Sspec */
if (pSspec->monFn != NULL) {
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext)
stat = (pSspec->pFunc)(pSspec, pSChan, SYD_FC_STOP, NULL);
}
return retStat;
}
/*+/macro*********************************************************************
* NAME sydOpen - create and initialize an empty synchronous set spec
*
* DESCRIPTION
* This routine builds an empty synchronous set spec structure. In
* order to use the SYD_SPEC, channels for synchronous samples must
* be specified. Retrieval will be for synchronous samples of data,
* in which all channels have the same time stamp; retrieval begins
* with the oldest data available and continues to the most recent.
* Several versions of this routine are available, corresponding
* to the source of the data:
*
* sydOpenCA(&pSspec, NULL) init for Channel Access
* sydOpenCF(&pSspec, filePath) init for "by channel" file
* sydOpenSSF(&pSspec, filePath) init for "sample set" file
*
* RETURNS
* S_syd_OK, or
* S_syd_noMem if memory isn't available to store set descriptor
*
* BUGS
* o should set up, in the sync set spec, the number of input buffers,
* rather than having that job be done in sydChanOpen.
*
* SEE ALSO
* sydChanOpen, sydAddTest
* sydInputGet, sydMonitorStart
*
* EXAMPLE
*
*-*/
long
sydOpen(ppSspec)
SYD_SPEC **ppSspec; /* O pointer to synchronous set spec pointer */
{
assert(ppSspec != NULL);
assert(*ppSspec != NULL);
(*ppSspec)->pChanHead = (*ppSspec)->pChanTail = NULL;
(*ppSspec)->pAccept = NULL;
(*ppSspec)->refTs.secPastEpoch = (*ppSspec)->refTs.nsec = 0;
(*ppSspec)->priorTs = (*ppSspec)->refTs;
(*ppSspec)->sampleTs = (*ppSspec)->refTs;
(*ppSspec)->chanCount = 0;
(*ppSspec)->disconCount = 0;
(*ppSspec)->needGrCount = 0;
(*ppSspec)->eofCount = 0;
(*ppSspec)->dataDim = 0;
(*ppSspec)->sampleCount = 0;
(*ppSspec)->pDeltaSec = NULL;
(*ppSspec)->pTimeStamp = NULL;
(*ppSspec)->statDim = (*ppSspec)->statCount = 0;
(*ppSspec)->pStatDeltaSec = NULL;
(*ppSspec)->pStatTimeStamp = NULL;
(*ppSspec)->pStatPopCount = NULL;
(*ppSspec)->pPartial = NULL;
(*ppSspec)->lastData = (*ppSspec)->firstData = -1;
(*ppSspec)->restrictLastData = (*ppSspec)->restrictFirstData = -1;
(*ppSspec)->restrictDeltaSecSubtract = 0.;
(*ppSspec)->roundNsec = 0;
(*ppSspec)->deadband = DBE_LOG;
(*ppSspec)->useStats = (*ppSspec)->useMeans = 0;
(*ppSspec)->monFn = NULL;
return S_syd_OK;
}
/*+/subr**********************************************************************
* NAME sydPosition - position the data file
*
* DESCRIPTION
*
* RETURNS
*
* BUGS
* o doesn't report EOF if that condition occurs
* o doesn't provide a way for the caller to find out the actual position
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
sydPosition(pSspec, pStamp)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
TS_STAMP *pStamp; /* I stamp at which to position; NULL to rewind */
{
SYD_CHAN *pSChan;
long stat;
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
stat = (pSspec->pFunc)(pSspec, pSChan, SYD_FC_POSITION, pStamp);
if (pSspec->type == SYD_TY_SSF)
break;
}
sydInputReset(pSspec);
pSspec->sampleTs.secPastEpoch = pSspec->sampleTs.nsec = 0;
return S_syd_OK;
}
/*+/subr**********************************************************************
* NAME sydSampleExport
*
* DESCRIPTION
*
* RETURNS
*
* BUGS
* o options should be #defined symbols
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
sydSampleExport(pSspec, out, option, samp)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
FILE *out; /* IO stream pointer for output */
int option; /* I filtering option */
/* 1 spreadsheet; delta time and values */
/* 2 spreadsheet; delta time and values and status */
/* 3 spreadsheet; delta time, count, and values */
/* 4 spreadsheet; as for 3, with status */
int samp; /* I sample number in synchronous set */
{
SYD_CHAN *pSChan; /* pointer to channel in synchronous set */
char stampText[28];
int i;
/*-----------------------------------------------------------------------------
* generate headings, depending on option:
* 1==> "mm/dd/yy hh:mm:ss.msc"
* date time name1 name2 ...
* stamp seconds egu egu ...
*
* 2==> "mm/dd/yy hh:mm:ss.msc"
* date time stat name1 stat name2 ...
* stamp seconds stat egu stat egu ...
*
* 3==> "mm/dd/yy hh:mm:ss.msc"
* date time nEl name1 nEl name2 ...
* stamp seconds nEl egu nEl egu ...
*
* 4==> "mm/dd/yy hh:mm:ss.msc"
* date time stat nEl name1 stat nEl name2 ...
* stamp seconds stat nEl egu stat nEl egu ...
*----------------------------------------------------------------------------*/
if (samp == pSspec->restrictFirstData) {
(void)fprintf(out, "\"%s\"\n\"date\"\t\"time\"", tsStampToText(
&pSspec->restrictRefTs, TS_TEXT_MMDDYY, stampText));
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan = pSChan->pNext) {
if (pSChan->dataChan) {
if (option == 2 || option == 4)
(void)fprintf(out, "\t\"stat\"");
if (option == 3 || option == 4)
(void)fprintf(out, "\t\"nEl\"");
(void)fprintf(out, "\t\"%s\"", pSChan->name);
}
}
(void)fprintf(out, "\n\"stamp\"\t\"seconds\"");
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan = pSChan->pNext) {
if (pSChan->dataChan) {
if (option == 2 || option == 4)
(void)fprintf(out, "\t\"stat\"");
if (option == 3 || option == 4)
(void)fprintf(out, "\t\"nEl\"");
(void)fprintf(out, "\t\"%s\"", pSChan->EGU);
}
}
(void)fprintf(out, "\n");
}
/*-----------------------------------------------------------------------------
* print the value for each channel for this sample.
*----------------------------------------------------------------------------*/
tsStampToText(&pSspec->pTimeStamp[samp], TS_TEXT_MMDDYY, stampText);
(void)fprintf(out, "\"%s\"%c%.3f", stampText,'\t',
pSspec->pDeltaSec[samp] - pSspec->restrictDeltaSecSubtract);
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
if (pSChan->dataChan) {
if (option == 1)
sydSamplePrint1(pSChan, out, '\t', USE_QUO, 0, samp);
else if (option == 2)
sydSamplePrint1(pSChan, out, '\t', PRE_FL|USE_QUO, 0, samp);
else if (option == 3)
sydSamplePrint1(pSChan, out, '\t', USE_QUO|SHOW_AR, 0, samp);
else if (option == 4)
sydSamplePrint1(pSChan,out,'\t',PRE_FL|USE_QUO|SHOW_AR,0,samp);
}
}
(void)fprintf(out, "\n");
pSChan = pSspec->pChanHead;
if (pSChan->pFlags[samp].snapend && !pSChan->pFlags[samp].snapstart)
(void)fprintf(out, "\n"); /* blank line after snapshot */
}
/*+/subr**********************************************************************
* NAME sydSampleExportStats
*
* DESCRIPTION
* If the `useMeans' flag is set in conjunction with `useStats', then
* only the means are printed.
*
* RETURNS
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
sydSampleExportStats(pSspec, out, snap)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
FILE *out; /* IO stream pointer for output */
int snap; /* I snapshot number in synchronous set */
{
SYD_CHAN *pSChan; /* pointer to channel in synchronous set */
char stampText[28];
double dblVal;
/*-----------------------------------------------------------------------------
* generate heading
* "mm/dd/yy hh:mm:ss.msc"
* date time sample name1 egu name2 egu ...
* stamp seconds count mean stdDev mean stdDev ...
*
* "mm/dd/yy hh:mm:ss.msc"
* date time sample name1 name2 ...
* stamp seconds count egu egu ...
*----------------------------------------------------------------------------*/
if (snap == 0 && pSspec->useMeans == 0) {
(void)fprintf(out, "\"%s\"\n\"date\"\t\"time\"\t\"sample\"",
tsStampToText(&pSspec->restrictRefTs,TS_TEXT_MMDDYY,stampText));
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan = pSChan->pNext) {
if (pSChan->dataChan)
(void)fprintf(out,"\t\"%s\"\t\"%s\"",pSChan->name,pSChan->EGU);
}
(void)fprintf(out, "\n\"stamp\"\t\"seconds\"\t\"count\"");
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan = pSChan->pNext) {
if (pSChan->dataChan)
(void)fprintf(out, "\t\"mean\"\t\"stdDev\"");
}
(void)fprintf(out, "\n");
}
if (snap == 0 && pSspec->useMeans) {
(void)fprintf(out, "\"%s\"\n\"date\"\t\"time\"\t\"sample\"",
tsStampToText(&pSspec->restrictRefTs,TS_TEXT_MMDDYY,stampText));
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan = pSChan->pNext) {
if (pSChan->dataChan)
(void)fprintf(out,"\t\"%s\"",pSChan->name);
}
(void)fprintf(out, "\n\"stamp\"\t\"seconds\"\t\"count\"");
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan = pSChan->pNext) {
if (pSChan->dataChan)
(void)fprintf(out,"\t\"%s\"",pSChan->EGU);
}
(void)fprintf(out, "\n");
}
/*-----------------------------------------------------------------------------
* print the statistics for each channel for this snapshot.
*----------------------------------------------------------------------------*/
tsStampToText(&pSspec->pStatTimeStamp[snap], TS_TEXT_MMDDYY, stampText);
(void)fprintf(out, "\"%s\"\t%.3f", stampText,
pSspec->pStatDeltaSec[snap] - pSspec->restrictDeltaSecSubtract);
(void)fprintf(out, "\t%d", pSspec->pStatPopCount[snap]);
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
if (pSChan->dataChan) {
dblVal = pSChan->pStats[snap].mean;
if (dblVal == 0.)
(void)fprintf(out, "\t0");
else if (dblVal > -.1 && dblVal < .1)
(void)fprintf(out, "\t%.*E", pSChan->precision, dblVal);
else
(void)fprintf(out, "\t%.*f", pSChan->precision, dblVal);
if (pSspec->useMeans == 0) {
dblVal = pSChan->pStats[snap].stdDev;
if (dblVal == 0.)
(void)fprintf(out, "\t0");
else if (dblVal > -.1 && dblVal < .1)
(void)fprintf(out, "\t%.*E", pSChan->precision, dblVal);
else
(void)fprintf(out, "\t%.*f", pSChan->precision, dblVal);
}
}
}
(void)fprintf(out, "\n");
}
/*+/internal******************************************************************
* NAME widthPrint
*
*-*/
static void
widthPrint(out, pre, text, width, pass, pMore)
FILE *out;
char *pre, *text;
int width, pass;
int *pMore;
{
int L=strlen(text);
int sub=pass*width;
if (pre[0] != '\0')
(void)fprintf(out, "%s", pre);
if (sub < L) {
if (pass == 0 && L <= width)
(void)fprintf(out, "%*.*s", width, width, &text[sub]);
else
(void)fprintf(out, "%-*.*s", width, width, &text[sub]);
}
else
(void)fprintf(out, "%*s", width, " ");
if (sub + width < L)
*pMore = 1;
}
/*+/subr**********************************************************************
* NAME sydSamplePrint
*
* DESCRIPTION
* Print the values for all channels in a particular sample. Various
* aspects of formatting can be specified with arguments, including
* the printing of column headings, number of columns, column width,
* and the inclusion of printer control characters.
*
* For array channels, only the first value of the array is printed
* on the line with the time stamp--the full array is printed on
* following lines.
*
* RETURNS
* S_syd_OK
*
* BUGS
* o
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
sydSamplePrint(pSspec, out, formatFlag, headerFlag, nCol, colWidth, samp)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
FILE *out; /* I stream pointer for output */
int formatFlag; /* I ==1 causes page formatting for printing */
int headerFlag; /* I ==1 causes printing of column headings */
int nCol; /* I >0 specifies number of table columns,
with folded lines to accomodate excess
channels */
int colWidth; /* I >0 specifies column width, in characters */
int samp; /* I sample number in synchronous set */
{
SYD_CHAN *pSChan; /* pointer to channel in synchronous set */
char stampText[28];
int i, tmpWid;
int more, pass, colNum;
SYD_CHAN *pNext;
if (colWidth < 1)
colWidth = 10;
/*-----------------------------------------------------------------------------
* print a heading line with channel names; if this isn't the first page,
* put a ^L prior to the heading.
*----------------------------------------------------------------------------*/
#define FMT_INDENT if (formatFlag) (void)fprintf(out, " ")
#define SKIP_STAMP (void)fprintf(out, " %21s", " ")
if (headerFlag) {
if (formatFlag) {
if (samp != pSspec->restrictFirstData)
(void)fprintf(out, "\f");
(void)fprintf(out, "\n\n"); /* 2 line top margin */
}
pNext = pSspec->pChanHead;
tmpWid = colWidth;
if (tmpWid <= 2)
tmpWid = 1;
while (1) {
pass = 0;
while (1) {
more = 0;
pSChan = pNext;
FMT_INDENT; SKIP_STAMP;
for (colNum=0; colNum<nCol; colNum++) {
if (pSChan->dataChan)
widthPrint(out, " ", pSChan->name,tmpWid,pass,&more);
pSChan = pSChan->pNext;
if (pSChan == NULL)
break;
}
(void)fprintf(out, "\n");
if (more == 0)
break;
pass++;
}
if (tmpWid > 2) {
pass = 0;
while (1) {
more = 0;
pSChan = pNext;
FMT_INDENT; SKIP_STAMP;
for (colNum=0; colNum<nCol; colNum++) {
if (pSChan->dataChan)
widthPrint(out," ",pSChan->EGU,tmpWid,pass,&more);
pSChan = pSChan->pNext;
if (pSChan == NULL)
break;
}
(void)fprintf(out, "\n");
if (more == 0)
break;
pass++;
}
}
(void)fprintf(out, "\n");
if ((pNext = pSChan) == NULL)
break;
}
}
/*-----------------------------------------------------------------------------
* print the value for each channel for this sample. Print the status
* flags following values, but print only the first element for array
* channels.
*----------------------------------------------------------------------------*/
FMT_INDENT;
if (pSspec->pPartial[samp]) (void)fprintf(out, "*");
else (void)fprintf(out, " ");
(void)fprintf(out, " %s", tsStampToText(
&pSspec->pTimeStamp[samp], TS_TEXT_MMDDYY, stampText));
colNum = 0;
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
if (pSChan->dataChan) {
if (colNum >= nCol) {
(void)fprintf(out, "\n");
FMT_INDENT; SKIP_STAMP;
colNum = 0;
}
sydSamplePrint1(pSChan, out, ' ', POST_FL|ENF_WID, colWidth, samp);
colNum++;
}
}
(void)fprintf(out, "\n");
/*-----------------------------------------------------------------------------
* if any of the channels is an array channel, print the full array following
* the time-stamped line (which had only the first value)
*----------------------------------------------------------------------------*/
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
if (pSChan->dataChan && pSChan->elCount > 1) {
(void)fprintf(out, "%s", pSChan->name);
for (i=0; i<(int) pSChan->elCount; i++) {
if (i%nCol == 0)
(void)fprintf(out, "\n%5d", i);
(void)fputc(' ', out);
sydSamplePrintVal(out, pSChan, samp, i, ENF_WID, colWidth);
}
(void)fprintf(out, "\n");
}
}
return S_syd_OK;
}
/*+/internal******************************************************************
* NAME sydSamplePrint1 - print a value for a channel
*
* DESCRIPTION
*
* RETURNS
* void
*
*-*/
static void
sydSamplePrint1(pSChan, out, sep, flags, colWidth, sampNum)
SYD_CHAN *pSChan; /* I pointer to sync channel */
FILE *out; /* I file pointer for writing value */
char sep; /* I character to use a a prefix for each field,
as a separator; usually ' ' or '\t' */
int flags; /* I flags, or'd together, or 0:
PRE_FL print status flag, prior to value
POST_FL print status flag, after value
SHOW_AR print all array values, preceded by
array element count
ENF_WID enforce column width
USE_QUO put quotes around text items
*/
int colWidth; /* I >0 specifies column width, in characters;
== 2 requests only printing status code*/
int sampNum; /* I sample number in sync set */
{
int i;
chtype type; /* type of value */
char text[100];
char *special; /* != NULL says print "msg", not value */
type = pSChan->dbrType;
if (colWidth == 2) {
if (pSChan->pData == NULL)
(void)fprintf(out, " %c", sep,'M');
else
(void)fprintf(out, " %c", pSChan->pDataCodeL[sampNum]);
return;
}
if (flags&PRE_FL || flags&POST_FL) {
if (colWidth > 3)
colWidth -= 3;
}
if (flags&PRE_FL) {
if (pSChan->pData == NULL) {
if (flags&USE_QUO)
(void)fprintf(out, "%c\"%c%c\"", sep,'M','D');
else
(void)fprintf(out, "%c%c%c", sep,'M','D');
}
else {
if (flags&USE_QUO) {
(void)fprintf(out, "%c\"%c%c\"", sep,
pSChan->pDataCodeL[sampNum], pSChan->pDataCodeR[sampNum]);
}
else {
(void)fprintf(out, "%c%c%c", sep,
pSChan->pDataCodeL[sampNum], pSChan->pDataCodeR[sampNum]);
}
}
}
(void)fputc(sep, out);
special = NULL;
if (pSChan->pData == NULL)
special = "no_data";
else if (pSChan->pFlags[sampNum].eof)
special = "EOF";
else if (pSChan->pFlags[sampNum].missing)
special = "no_data";
else if (pSChan->pDataCodeL[sampNum] == 'I' &&
flags&PRE_FL == 0 && flags&POST_FL == 0) {
special = "invalid";
}
if (special == NULL) {
if (flags&SHOW_AR) {
(void)fprintf(out, "%d", pSChan->elCount);
for (i=0; i<(int) pSChan->elCount; i++) {
(void)fputc(sep, out);
sydSamplePrintVal(out, pSChan, sampNum, i, flags, colWidth);
}
}
else
sydSamplePrintVal(out, pSChan, sampNum, 0, flags, colWidth);
}
else {
if (flags&SHOW_AR)
(void)fprintf(out, "1%c", sep);
if (flags&USE_QUO) {
if (flags&ENF_WID)
(void)fprintf(out, "\"%*.*s\"", colWidth, colWidth, special);
else
(void)fprintf(out, "\"%s\"", special);
}
else {
if (flags&ENF_WID)
(void)fprintf(out, "%*.*s", colWidth, colWidth, special);
else
(void)fprintf(out, "%s", special);
}
}
if (flags&POST_FL) {
if (pSChan->pData == NULL) {
if (flags&USE_QUO)
(void)fprintf(out, "%c\"%c%c\"", sep,'M','D');
else
(void)fprintf(out, "%c%c%c", sep,'M','D');
}
else {
if (flags&USE_QUO) {
(void)fprintf(out, "%c\"%c%c\"", sep,
pSChan->pDataCodeL[sampNum], pSChan->pDataCodeR[sampNum]);
}
else {
(void)fprintf(out, "%c%c%c", sep,
pSChan->pDataCodeL[sampNum], pSChan->pDataCodeR[sampNum]);
}
}
}
}
/*+/internal******************************************************************
* NAME sydSamplePrintVal - print a single value
*
*-*/
static void
sydSamplePrintVal(out, pSChan, sampNum, sub, flags, colWidth)
FILE *out;
SYD_CHAN *pSChan;
int sampNum;
int sub; /* subscript for array channels */
int flags;
int colWidth;
{
int myType=0; /* 0,1,2 for lng, dbl, str */
chtype type; /* type of value */
char text[100];
long lngVal=0;
double dblVal=0.0;
char *strVal=NULL;
type = pSChan->dbrType;
if (type == DBR_TIME_STRING) {
myType = 2;
strVal = ((char *)pSChan->pData) +
sampNum * db_strval_dim * pSChan->elCount + sub * db_strval_dim;
}
else if (type == DBR_TIME_FLOAT) {
float *p;
myType = 1;
p = ((float *)pSChan->pData) + sampNum * pSChan->elCount + sub;
dblVal = (double)(*p);
}
else if (type == DBR_TIME_SHORT) {
short *p;
myType = 0;
p = ((short *)pSChan->pData) + sampNum * pSChan->elCount + sub;
if (pSChan->isRVAL)
lngVal = (long)(*(unsigned short *)p);
else
lngVal = (long)(*p);
}
else if (type == DBR_TIME_DOUBLE) {
double *p;
myType = 1;
p = ((double *)pSChan->pData) + sampNum * pSChan->elCount + sub;
dblVal = *p;
}
else if (type == DBR_TIME_LONG) {
long *p;
myType = 0;
p = ((long *)pSChan->pData) + sampNum * pSChan->elCount + sub;
if (pSChan->isRVAL)
lngVal = *(unsigned long *)p;
else
lngVal = *p;
}
else if (type == DBR_TIME_CHAR) {
char *p;
myType = 0;
p = ((char *)pSChan->pData) + sampNum * pSChan->elCount + sub;
lngVal = (long)(*p);
}
else if (type == DBR_TIME_ENUM) {
short *p;
myType = 0;
p = ((short *)pSChan->pData) + sampNum * pSChan->elCount + sub;
lngVal = (long)(*p);
if (lngVal < pSChan->grBuf.genmval.no_str)
myType = 2, strVal = pSChan->grBuf.genmval.strs[lngVal];
}
if (flags&ENF_WID) {
if (myType == 0)
strVal=text, cvtLngToTxt(text, colWidth, lngVal);
else if (myType == 1)
strVal=text,cvtDblToTxt(text, colWidth, dblVal, pSChan->precision);
(void)fprintf(out, "%*.*s", colWidth, colWidth, strVal);
}
else {
if (myType == 0)
(void)fprintf(out, "%*d", colWidth, lngVal);
else if (myType == 1) {
if (dblVal == 0.)
(void)fprintf(out, "0");
else if (dblVal > -.1 && dblVal < .1)
(void)fprintf(out, "%*.*E",colWidth,pSChan->precision,dblVal);
else
(void)fprintf(out, "%*.*f",colWidth,pSChan->precision,dblVal);
}
else
(void)fprintf(out, "%*s", colWidth, strVal);
}
}
/*+/internal******************************************************************
* NAME sydSamplePrintStats
*
* DESCRIPTION
*
* RETURNS
* S_syd_OK
*
* BUGS
* o
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long sydSamplePrintStats(pSspec, out, formatFlag, headerFlag, nCol, colWidth, snap)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
FILE *out; /* I stream pointer for output */
int formatFlag; /* I ==1 causes page formatting for printing */
int headerFlag; /* I ==1 causes printing of column headings */
int nCol; /* I >0 specifies number of table columns,
with folded lines to accomodate excess
channels */
int colWidth; /* I >0 specifies column width, in characters */
int snap; /* I snapshot number in synchronous set */
{
SYD_CHAN *pSChan; /* pointer to channel in synchronous set */
char stampText[28];
int i;
int prec;
char text[100];
int more, pass, colNum;
SYD_CHAN *pNext;
if (colWidth < 6)
colWidth = 6;
/*-----------------------------------------------------------------------------
* print a heading line with channel names; if this isn't the first page,
* put a ^L prior to the heading.
*----------------------------------------------------------------------------*/
if (headerFlag) {
if (formatFlag) {
if (snap != 0)
(void)fprintf(out, "\f");
(void)fprintf(out, "\n\n"); /* 2 line top margin */
}
#undef SKIP_STAMP
#define SKIP_STAMP (void)fprintf(out, " %21s %4s", " ", " ")
pNext = pSspec->pChanHead;
while (1) {
pass = 0;
while (1) {
more = 0;
pSChan = pNext;
FMT_INDENT; SKIP_STAMP;
for (colNum=0; colNum<nCol; colNum++) {
if (pSChan->dataChan)
widthPrint(out, " ", pSChan->name,colWidth,pass,&more);
if (pSChan->dataChan && pSspec->useMeans == 0)
widthPrint(out, " ", pSChan->EGU,colWidth,pass,&more);
pSChan = pSChan->pNext;
if (pSChan == NULL)
break;
}
(void)fprintf(out, "\n");
if (more == 0)
break;
pass++;
}
if (pSspec->useMeans == 0)
(void)fprintf(out, "\n");
if ((pNext = pSChan) == NULL)
break;
}
pSChan = pSspec->pChanHead;
FMT_INDENT; SKIP_STAMP;
for (colNum=0; colNum<nCol; colNum++) {
if (pSChan->dataChan && pSspec->useMeans == 0) {
(void)fprintf(out, " %*s %*s", colWidth, "mean",
colWidth, "stdDev");
}
else if (pSChan->dataChan && pSspec->useMeans)
(void)fprintf(out, " %*s", colWidth, pSChan->EGU);
pSChan = pSChan->pNext;
if (pSChan == NULL)
break;
}
(void)fprintf(out, "\n");
if (pSspec->useMeans == 0) {
pSChan = pSspec->pChanHead;
FMT_INDENT; SKIP_STAMP;
for (colNum=0; colNum<nCol; colNum++) {
if (pSChan->dataChan) {
fprintf(out, " %.*s", colWidth*2+1,
"--------------------------------------------------");
}
pSChan = pSChan->pNext;
if (pSChan == NULL)
break;
}
}
(void)fprintf(out, "\n");
}
/*-----------------------------------------------------------------------------
* print the stats for each channel for this snapshot.
*----------------------------------------------------------------------------*/
FMT_INDENT;
(void)fprintf(out, " ");
(void)fprintf(out, " %s", tsStampToText(
&pSspec->pStatTimeStamp[snap], TS_TEXT_MMDDYY, stampText));
(void)fprintf(out, " %4d", pSspec->pStatPopCount[snap]);
colNum = 0;
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
if (pSChan->dataChan) {
if (colNum >= nCol) {
(void)fprintf(out, "\n");
FMT_INDENT; SKIP_STAMP;
colNum = 0;
}
prec = pSChan->precision;
cvtDblToTxt(text, colWidth, pSChan->pStats[snap].mean, prec);
fprintf(out, " %*.*s", colWidth, colWidth, text);
if (pSChan->dataChan && pSspec->useMeans == 0) {
cvtDblToTxt(text, colWidth, pSChan->pStats[snap].stdDev, prec);
fprintf(out, " %*.*s", colWidth, colWidth, text);
}
colNum++;
}
}
(void)fprintf(out, "\n");
return S_syd_OK;
}
/*+/subr**********************************************************************
* NAME sydSampleWriteSSF
*
* DESCRIPTION
*
* RETURNS
* S_syd_OK
*
* BUGS
* o
*
* SEE ALSO
*
* EXAMPLE
* sydSampleWriteSSF(pSspec, out, "PROGRAM: rfqTScan", "sample button",
* sampNum);
*
*-*/
long
sydSampleWriteSSF(pSspec, pFile, reqDesc, sampDesc, samp)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
FILE *pFile; /* I stream pointer for writing */
char *reqDesc; /* I description of "request", or NULL */
char *sampDesc; /* I description for "sample", or NULL */
int samp; /* I sample number in synchronous set */
{
SYD_CHAN *pSChan; /* pointer to channel in synchronous set */
char stampText[28];
if (reqDesc != NULL)
(void)fprintf(pFile, "%s\n", reqDesc);
(void)fprintf(pFile, "SAMPLE: at %s", tsStampToText(
&pSspec->pTimeStamp[samp], TS_TEXT_MMDDYY, stampText));
if (sampDesc != NULL)
(void)fprintf(pFile, "--%s", sampDesc);
(void)fprintf(pFile, "\n%%endHeader%%\n");
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
if (pSChan->dataChan) {
(void)fprintf(pFile, "%s %s", SydChanName(pSChan),
dbf_type_to_text(SydChanDbfType(pSChan)));
(void)fprintf(pFile, " %d",
pSChan->pDataAlSev != NULL ? pSChan->pDataAlSev[samp] : 0);
(void)fprintf(pFile, " %d",
pSChan->pDataAlStat != NULL ? pSChan->pDataAlStat[samp] : 0);
sydSamplePrint1(pSChan, pFile, ' ', SHOW_AR, 0, samp);
(void)fprintf(pFile, "\n");
}
}
(void)fprintf(pFile, "%%endData%%\n");
(void)fflush(pFile);
return S_syd_OK;
}
/*+/subr**********************************************************************
* NAME sydSampleSetAlloc - acquire memory for holding a sample set
*
* DESCRIPTION
* Conditionally allocates memory for holding a sample set. If memory
* has already been allocated to hold at least the requested number
* of samples, then nothing is done.
*
* This routine must be called prior to calling other sydSampleSet...
* routines. (But AFTER calling sydChanOpen for all channels of
* interest!)
*
* If memory has already been allocated, but it isn't sufficient to
* hold the requested number of samples, then that memory is free'd
* and larger memory is allocated.
*
* The allocated memory is automatically free'd by sydClose.
*
* RETURNS
* S_syd_OK, or
* S_syd_noMem if sufficient memory is unavailable
*
* BUGS
* o text
*
* SEE ALSO
* sydClose
*
* EXAMPLE
*
*-*/
long
sydSampleSetAlloc(pSspec, reqCount)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
int reqCount; /* I number of samples in the set */
{
SYD_CHAN *pSChan;
if (reqCount > pSspec->dataDim)
sydSampleSetFree(pSspec);
if (pSspec->dataDim <= 0) {
pSspec->pDeltaSec = (double *)malloc(reqCount * sizeof(double));
if (pSspec->pDeltaSec == NULL)
goto sampleMallocErr;
pSspec->pTimeStamp = (TS_STAMP *)malloc(reqCount * sizeof(TS_STAMP));
if (pSspec->pTimeStamp == NULL)
goto sampleMallocErr;
pSspec->pPartial = (char *)malloc(reqCount * sizeof(char));
if (pSspec->pPartial == NULL)
goto sampleMallocErr;
pSspec->dataDim = reqCount;
}
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
if (pSChan->conn == 0)
; /* no action if never been connected */
else if (pSChan->pData == NULL) {
pSChan->pData = (void *)malloc(reqCount *
dbr_value_size[pSChan->dbrType] * pSChan->elCount);
if (pSChan->pData == NULL)
goto sampleMallocErr;
pSChan->pDataAlStat = (void *)malloc(reqCount *
sizeof(*pSChan->pDataAlStat));
if (pSChan->pDataAlStat == NULL)
goto sampleMallocErr;
pSChan->pDataAlSev = (void *)malloc(reqCount *
sizeof(*pSChan->pDataAlSev));
if (pSChan->pDataAlSev == NULL)
goto sampleMallocErr;
pSChan->pDataCodeL = (void *)malloc(reqCount *
sizeof(*pSChan->pDataCodeL));
if (pSChan->pDataCodeL == NULL)
goto sampleMallocErr;
pSChan->pDataCodeR = (void *)malloc(reqCount *
sizeof(*pSChan->pDataCodeR));
if (pSChan->pDataCodeR == NULL)
goto sampleMallocErr;
pSChan->pFlags = (void *)malloc(reqCount *
sizeof(*pSChan->pFlags));
if (pSChan->pFlags == NULL)
goto sampleMallocErr;
}
}
pSspec->reqCount = reqCount;
pSspec->firstData = pSspec->lastData = -1;
pSspec->sampleCount = 0;
pSspec->refTs.secPastEpoch = pSspec->refTs.nsec = 0;
pSspec->restrictFirstData = pSspec->restrictLastData = -1;
pSspec->restrictRefTs = pSspec->refTs;
pSspec->restrictDeltaSecSubtract = 0.;
pSChan = pSspec->pChanHead;
return S_syd_OK;
sampleMallocErr:
(void)printf("couldn't get memory to store data\n");
sydSampleSetFree(pSspec);
return S_syd_noMem;
}
/*+/subr**********************************************************************
* NAME sydSampleSetExport - export sample set data to output
*
* DESCRIPTION
* Exports the sample set data with the desired filtering.
*
* RETURNS
*
* BUGS
* o text
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
sydSampleSetExport(pSspec, out, option)
FILE *out; /* I stream pointer for output */
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
int option; /* I filtering option */
{
int samp; /* sample number in synchronous set */
if (pSspec->useStats) {
for (samp=0; samp<pSspec->statCount; samp++)
sydSampleExportStats(pSspec, out, samp);
return S_syd_OK;
}
samp = pSspec->restrictFirstData;
while (samp >= 0) {
sydSampleExport(pSspec, out, option, samp);
if (samp == pSspec->restrictLastData)
samp = -1;
else if (++samp >= pSspec->dataDim)
samp = 0;
}
return S_syd_OK;
}
/*+/subr**********************************************************************
* NAME sydSampleSetFree - free the array storage for holding a sample set
*
* DESCRIPTION
* Free's the sample set arrays for a synchronous sample spec. The
* underlying, channel-dependent items in the synchronous sample spec
* aren't altered.
*
* This routine is intended to be called when the number of synchronous
* samples is increased--free the current sample set storage using this
* routine and then allocate new storage using sydSampleSetAlloc.
*
* RETURNS
*
* BUGS
* o text
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
sydSampleSetFree(pSspec)
SYD_SPEC *pSspec;
{
SYD_CHAN *pSChan;
assert(pSspec != NULL);
if (pSspec->pDeltaSec != NULL) {
free((char *)pSspec->pDeltaSec);
pSspec->pDeltaSec = NULL;
}
if (pSspec->pTimeStamp != NULL) {
free((char *)pSspec->pTimeStamp);
pSspec->pTimeStamp = NULL;
}
if (pSspec->pPartial != NULL) {
free((char *)pSspec->pPartial);
pSspec->pPartial = NULL;
}
if (pSspec->pStatTimeStamp != NULL) {
free((char *)pSspec->pStatTimeStamp);
pSspec->pStatTimeStamp = NULL;
}
if (pSspec->pStatDeltaSec != NULL) {
free((char *)pSspec->pStatDeltaSec);
pSspec->pStatDeltaSec = NULL;
}
if (pSspec->pStatPopCount != NULL) {
free((char *)pSspec->pStatPopCount);
pSspec->pStatPopCount = NULL;
}
pSspec->dataDim = 0;
pSspec->sampleCount = 0;
pSspec->firstData = pSspec->lastData = -1;
pSspec->restrictFirstData = pSspec->restrictLastData = -1;
pSspec->statDim = pSspec->statCount = 0;
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext)
sydChanFreeArrays(pSChan);
return S_syd_OK;
}
/*+/subr**********************************************************************
* NAME sydSampleSetGet - get the samples for a sample set
*
* DESCRIPTION
*
* RETURNS
* S_syd_OK, or
* S_syd_EOF if end of file is encountered, or
* S_syd_chanNotConn if none of the channels is connected
*
* BUGS
* o text
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
sydSampleSetGet(pSspec)
SYD_SPEC *pSspec;
{
long stat=S_syd_OK;
SYD_CHAN *pSChan;
int n=0;
assert(pSspec != NULL);
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
if (pSChan->discon == 0)
n++;
sydChanPrep(pSspec, pSChan);
}
pSspec->firstData = pSspec->lastData = -1;
pSspec->restrictFirstData = pSspec->restrictLastData = -1;
pSspec->restrictDeltaSecSubtract = 0.;
pSspec->sampleCount = 0;
if (n <= 0) {
(void)printf("sydSampleSetGet: none of the channels is connected\n");
return S_syd_chanNotConn;
}
while (stat != S_syd_EOF && pSspec->sampleCount < pSspec->reqCount) {
if ((stat = sydInputGet(pSspec, NULL)) != S_syd_EOF)
sydInputStoreInSet(pSspec, 0);
}
return stat;
}
/*+/subr**********************************************************************
* NAME sydSampleSetPrint - print the samples in a sample set
*
* DESCRIPTION
*
* If the `useStats' flag is set in the sync set spec, then means
* and standard deviations are printed instead of the actual data
* values. (The caller must already have called sydSampleSetStats.)
*
* If the `useMeans' flag is set in conjunction with `useStats', then
* only the means are printed.
*
* RETURNS
* S_syd_OK
*
* BUGS
* o text
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
sydSampleSetPrint(pSspec, out, formatFlag, nCol, colWidth)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
FILE *out; /* IO stream pointer for output */
int formatFlag; /* I ==1 causes page formatting for printing */
int nCol; /* I >0 causes that many table columns, folded lines */
int colWidth; /* I >0 specifies column width, in characters */
{
int samp; /* sample number in synchronous set */
int lineCount=0;
int headerFlag=1;
assert(pSspec != NULL);
if (pSspec->useStats) {
for (samp=0; samp<pSspec->statCount; samp++) {
if (lineCount == 0 && nCol <= 0)
headerFlag = 1;
sydSamplePrintStats(pSspec, out,
formatFlag, headerFlag, nCol, colWidth, samp);
headerFlag = 0;
if (++lineCount > 60)
lineCount = 0;
}
return S_syd_OK;
}
samp = pSspec->restrictFirstData;
while (samp >= 0) {
if (lineCount == 0 && nCol <= 0)
headerFlag = 1;
sydSamplePrint(pSspec,out,formatFlag,headerFlag,nCol,colWidth,samp);
headerFlag = 0;
if (++lineCount > 60)
lineCount = 0;
if (samp == pSspec->restrictLastData)
samp = -1;
else if (++samp >= pSspec->dataDim)
samp = 0;
}
return S_syd_OK;
}
/*+/subr**********************************************************************
* NAME sydSampleSetRestrict - set for processing restricted range of time
*
* DESCRIPTION
* Sets up for processing a restricted range of the samples in the
* sample set, with the restriction based on time. The range starts
* with the first sample whose stamp is >= the desired begin stamp.
* The range ends with the last sample whose stamp is <= the desired
* end stamp.
*
* If the begin stamp pointer is NULL, then the range starts with
* the first sample. The end stamp pointer is treated similarly.
*
* RETURNS
* S_syd_OK, or
* S_syd_ERROR if 1) begin stamp is after time range in set; or
* 2) end stamp is before time range in set; or 3) begin
* stamp is equal to or after end stamp
*
*-*/
long
sydSampleSetRestrict(pSspec, pTsBegin, pTsEnd)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
TS_STAMP *pTsBegin; /* I pointer to stamp to begin, or NULL */
TS_STAMP *pTsEnd; /* I pointer to stamp to end, or NULL */
{
SYD_CHAN *pSChan;
TS_STAMP tsBegin, tsEnd;
int i, beg=-1, end=-1, first;
int statCount=0, sampCount;
double val;
assert(pSspec != NULL);
if (pSspec->sampleCount <= 1)
return S_syd_OK;
if (pTsBegin == NULL) tsBegin = pSspec->pTimeStamp[pSspec->firstData];
else tsBegin = *pTsBegin;
if (pTsEnd == NULL) tsEnd = pSspec->pTimeStamp[pSspec->lastData];
else tsEnd = *pTsEnd;
if (TsCmpStampsGT(&tsBegin, &pSspec->pTimeStamp[pSspec->lastData]))
return S_syd_ERROR;
if (TsCmpStampsLT(&tsEnd, &pSspec->pTimeStamp[pSspec->firstData]))
return S_syd_ERROR;
if (TsCmpStampsLE(&tsEnd, &tsBegin))
return S_syd_ERROR;
/*-----------------------------------------------------------------------------
* find the appropriate spots to start and end
*----------------------------------------------------------------------------*/
i = pSspec->firstData;
while (beg < 0) {
if (TsCmpStampsGE(&pSspec->pTimeStamp[i], &tsBegin)) {
beg = i;
break;
}
if (++i >= pSspec->dataDim)
i = 0;
}
assertAlways(beg >= 0);
i = pSspec->lastData;
while (end < 0) {
if (TsCmpStampsLE(&pSspec->pTimeStamp[i], &tsEnd) || i == beg) {
end = i;
break;
}
if (--i < 0)
i = pSspec->dataDim - 1;
}
assertAlways(end >= 0);
/*-----------------------------------------------------------------------------
* set up the `restrict' structure items
*----------------------------------------------------------------------------*/
pSspec->restrictFirstData = beg;
pSspec->restrictLastData = end;
pSspec->restrictRefTs = pSspec->pTimeStamp[beg];
pSspec->restrictRefTs.nsec = 0;
TsDiffAsDouble(&pSspec->restrictDeltaSecSubtract,
&pSspec->restrictRefTs, &pSspec->refTs);
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
i = beg;
first = 1;
while (1) {
if (sydValAsDbl(pSChan, i, &val)) {
if (pSChan->restrictMinDataVal > val || first)
pSChan->restrictMinDataVal = val;
if (pSChan->restrictMaxDataVal < val || first)
pSChan->restrictMaxDataVal = val;
first = 0;
}
if (i == end)
break;
if (++i >= pSspec->dataDim)
i = 0;
}
}
return S_syd_OK;
}
/*+/subr**********************************************************************
* NAME sydSampleSetStats - compute statistics for sample set
*
*-*/
long
sydSampleSetStats(pSspec)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
{
long stat=S_syd_OK;
SYD_CHAN *pSChan;
int i, i1, n, beg, end;
int statCount=0, sampCount;
assert(pSspec != NULL);
/*-----------------------------------------------------------------------------
* first, find out how many snapshots there are and set up the
* appropriate structures for statistics; assume that all channels
* are the same.
*----------------------------------------------------------------------------*/
pSChan = pSspec->pChanHead;
i = pSspec->restrictFirstData;
while (1) {
i1 = i + 1 < pSspec->dataDim ? i + 1 : 0;
if (pSChan->pFlags[i].snapend || pSChan->pFlags[i].eof ||
i == pSspec->restrictLastData || pSChan->pFlags[i1].restart) {
statCount++;
}
if (i == pSspec->restrictLastData) break;
if (++i >= pSspec->dataDim) i = 0;
}
if (statCount > pSspec->statDim) {
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
if (!pSChan->dataChan)
continue;
if (pSspec->statDim > 0) {
free((char *)pSChan->pStats);
free((char *)pSspec->pStatTimeStamp);
free((char *)pSspec->pStatDeltaSec);
free((char *)pSspec->pStatPopCount);
}
pSChan->pStats=(SYD_STATS *)malloc(statCount*sizeof(SYD_STATS));
assertAlways(pSChan->pStats != NULL);
pSspec->pStatDeltaSec =
(double *)malloc(statCount*sizeof(double));
assertAlways(pSspec->pStatDeltaSec != NULL);
pSspec->pStatTimeStamp =
(TS_STAMP *)malloc(statCount*sizeof(TS_STAMP));
assertAlways(pSspec->pStatTimeStamp != NULL);
pSspec->pStatPopCount =
(long *)malloc(statCount*sizeof(long));
assertAlways(pSspec->pStatPopCount != NULL);
}
pSspec->statDim = statCount;
}
pSspec->statCount = statCount;
/*-----------------------------------------------------------------------------
* then, do the statistics
*----------------------------------------------------------------------------*/
n = sampCount = 0;
i = beg = end = pSspec->restrictFirstData;
pSChan = pSspec->pChanHead;
while (1) {
i1 = i + 1 < pSspec->dataDim ? i + 1 : 0;
if (pSChan->pFlags[i].snapend || pSChan->pFlags[i].eof ||
i == pSspec->restrictLastData || pSChan->pFlags[i1].restart) {
if (!pSChan->pFlags[i].eof) {
end = i;
sampCount++;
}
if (sampCount > 0)
sydSampleSetStats_1(pSspec, n, beg, end, sampCount);
sampCount = 0;
beg = -1;
n++;
}
else {
end = i;
sampCount++;
}
if (i == pSspec->restrictLastData) break;
if (++i >= pSspec->dataDim) i = 0;
if (beg < 0)
beg = end = i;
}
return stat;
}
/*+/internal******************************************************************
* NAME sydSampleSetStats_1 - do stats for one snapshot
*
*-*/
sydSampleSetStats_1(pSspec, statNum, sampBeg, sampEnd, sampCount)
SYD_SPEC *pSspec;
int statNum, sampBeg, sampEnd, sampCount;
{
SYD_CHAN *pSChan;
double mean, stdDev, diff, val;
int i;
pSspec->pStatDeltaSec[statNum] = pSspec->pDeltaSec[sampBeg];
pSspec->pStatTimeStamp[statNum] = pSspec->pTimeStamp[sampBeg];
pSspec->pStatPopCount[statNum] = sampCount;
for (pSChan=pSspec->pChanHead; pSChan!=NULL; pSChan=pSChan->pNext) {
if (!pSChan->dataChan)
continue;
mean = stdDev = 0.;
i = sampBeg;
while (1) {
if (sydValAsDbl(pSChan, i, &val))
mean += val;
if (i == sampEnd) break;
if (++i >= pSspec->dataDim) i = 0;
}
if (sampCount > 0)
mean /= sampCount;
i = sampBeg;
while (1) {
if (sydValAsDbl(pSChan, i, &val)) {
diff = val - mean;
stdDev += diff * diff;
}
if (i == sampEnd) break;
if (++i >= pSspec->dataDim) i = 0;
}
if (sampCount > 1) {
stdDev /= (double)(sampCount - 1);
stdDev = sqrt(stdDev);
}
else
stdDev = 0.;
pSChan->pStats[statNum].mean = mean;
pSChan->pStats[statNum].stdDev = stdDev;
}
}
/*+/subr**********************************************************************
* NAME sydSampleSetWriteSSF - write sample set data to `sample set' file
*
* DESCRIPTION
*
* RETURNS
*
* BUGS
* o text
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
sydSampleSetWriteSSF(pSspec, pFile, reqDesc, sampDesc)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
FILE *pFile; /* I stream pointer for writing */
char *reqDesc; /* I description of "request", or NULL */
char *sampDesc; /* I description for "sample", or NULL */
{
int samp; /* sample number in synchronous set */
samp = pSspec->restrictFirstData;
while (samp >= 0) {
sydSampleWriteSSF(pSspec, pFile, reqDesc, sampDesc, samp);
if (samp == pSspec->restrictLastData)
samp = -1;
else if (++samp >= pSspec->dataDim)
samp = 0;
}
return S_syd_OK;
}
/*+/subr**********************************************************************
* NAME sydSetAttr - set attributes for synchronous set spec
*
* DESCRIPTION
* Set various attributes for a synchronous set specification.
*
* Set the default deadband to use in monitoring channels. This
* attribute must be set before adding any channels to the sync set spec.
* sydSetAttr(pSspec, SYD_ATTR_DEADBAND, 0, text)
* text can be "ADEL" or "MDEL"
*
* Set a callback routine to be called each time a channel receives a
* monitor from Channel Access (must be done prior to adding any
* channels to the sync set spec):
* sydSetAttr(pSspec, SYD_ATTR_MON_FN, 0, callbackFn);
*
* When this attribute is set, the normal sydSubr mechanism for handling
* data is disabled, and the caller is entirely responsible for all
* activities. The callback function has the form:
* void callbackFn(arg)
* struct event_handler_args arg;
* {
* arg.user is the pArg from the sydChanOpen call
* }
*
* Set so that printing, plotting, and exporting use statistics (i.e.,
* mean and standard deviation) rather than raw data:
* sydSetAttr(pSspec, SYD_ATTR_USE_STATS, 1, NULL);
* (A value of 0 causes raw data to be used.)
*
* Set so that printing, plotting, and exporting use sample means
* rather than raw data:
* sydSetAttr(pSspec, SYD_ATTR_USE_MEANS, 1, NULL);
* (A value of 0 causes raw data to be used.)
*
*
* RETURNS
* S_syd_OK
*
* BUGS
* o text
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
sydSetAttr(pSspec, attr, value, pArg)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
SYD_ATTR attr; /* I attribute key; one of the SYD_ATTR_xxx */
int value; /* I value for numeric attributes */
void *pArg; /* I pointer to value for non-numeric attributes */
{
if (attr == SYD_ATTR_DEADBAND) {
assert(pSspec->pChanHead == NULL);
if (strcmp((char *)pArg, "ADEL") == 0)
pSspec->deadband = DBE_LOG;
else if (strcmp((char *)pArg, "MDEL") == 0)
pSspec->deadband = DBE_VALUE;
else
assertAlways(0);
}
else if (attr == SYD_ATTR_MON_FN)
pSspec->monFn = pArg;
else if (attr == SYD_ATTR_USE_STATS) {
pSspec->useStats = value;
pSspec->useMeans = 0;
}
else if (attr == SYD_ATTR_USE_MEANS) {
pSspec->useStats = value;
pSspec->useMeans = value;
}
else
assertAlways(0);
return S_syd_OK;
}
/*+/subr**********************************************************************
* NAME sydTest - test the condition for the sync set spec
*
* DESCRIPTION
* For the sample most recently acquired by sydInputGet, tests the
* condition established by sydTestAddFromText.
*
* If no condition was established, this routine returns 1.
*
* RETURNS
* 1 if the condition is true for the present value of the channel
* 0 otherwise
*
*-*/
long
sydTest(pSspec)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
{
SYD_CHAN *pSChan;
SYD_TEST *pTest;
int i, change;
chtype type;
double dblVal=0.0;
long lngVal=0;
char *strVal=NULL;
int myType=0; /* 0,1,2 for double, long, string */
if ((pTest = pSspec->pAccept) == NULL)
return 1;
pSChan = pTest->pSChan;
i = pSChan->sampInBuf;
if (i < 0 || pSChan->inStatus[i] == SYD_B_EMPTY ||
pSChan->inStatus[i] == SYD_B_EOF ||
pSChan->inStatus[i] == SYD_B_MISSING)
return 0;
type = pSChan->dbrType;
#define TEST_IT(val, item,ty) val = pSChan->pInBuf[i]->item.value, myType = ty
if (type == DBR_TIME_FLOAT) TEST_IT(dblVal, tfltval, 0);
else if (type == DBR_TIME_SHORT) TEST_IT(lngVal, tshrtval, 1);
else if (type == DBR_TIME_DOUBLE) TEST_IT(dblVal, tdblval, 0);
else if (type == DBR_TIME_LONG) TEST_IT(lngVal, tlngval, 1);
else if (type == DBR_TIME_STRING)
strcpy(strVal, pSChan->pInBuf[i]->tstrval.value), myType = 2;
else if (type == DBR_TIME_CHAR) TEST_IT(lngVal, tchrval, 1);
else if (type == DBR_TIME_ENUM) {
#if 0
jjj
#endif
}
change = 0;
if (myType == 0) {
if (dblVal < pTest->dblVal) change = -1;
else if (dblVal > pTest->dblVal) change = 1;
}
else if (myType == 1) {
if (lngVal < pTest->lngVal) change = -1;
else if (lngVal > pTest->lngVal) change = 1;
}
#define COND_IS(ty) pTest->cond == SYD_TSTC_##ty
if (change == 0) {
if (COND_IS(EQ) || COND_IS(LE) || COND_IS(GE))
return 1;
}
else if (change == -1) {
if (COND_IS(NE) || COND_IS(LE) || COND_IS(LT))
return 1;
}
else {
if (COND_IS(NE) || COND_IS(GE) || COND_IS(GT))
return 1;
}
return 0;
}
/*+/subr**********************************************************************
* NAME sydTestAddFromText - add a sample test from a text string
*
* DESCRIPTION
* Scans a text string which specifies a test condition and
* builds a test structure for the synchronous set spec
*
* if chanName condition
*
* This routine adds a synchronous channel structure to the synchronous
* set spec, flagging the sync channel struct as a `test channel'.
* If the sync set spec already has a sync channel struct for the
* channel, then that sync channel struct will be flagged as both
* a `data channel' and also a `test channel'. Programs using
* test channels which aren't also data channels must distinguish
* between the two in their handling of samples.
*
* RETURNS
* S_syd_OK, or
* S_syd_noMem if malloc failed
* S_syd_ERROR if an error is detected
*
* BUGS
* o text
*
* SEE ALSO
*
* NOTES
* 1. The test structure must be closed by calling sydTestClose.
* (This happens automatically when sydClose is called.)
*
* EXAMPLE
*
*-*/
long
sydTestAddFromText(pSspec, text)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
char *text; /* I pointer to string with test specification */
{
char *myText=NULL;
char *pText, *pFld, delim, *pMsg;
SYD_TEST *pTest=NULL;
char *name;
enum sydChanSync sync=SYD_SY_NONF;
int i;
long stat;
SYD_CHAN *pSChan;
assert(pSspec != NULL);
assert(text != NULL);
if (pSspec->pAccept != NULL) {
(void)printf("sydTestAddFromText: sync set spec already has trigger\n");
return S_syd_ERROR;
}
if ((myText = (char *)malloc(strlen(text))) == NULL) {
(void)printf("sydTestAddFromText: couldn't malloc for text temp\n");
return S_syd_noMem;
}
pText = myText;
strcpy(myText, text);
if ((pTest = (SYD_TEST *)malloc(sizeof(SYD_TEST))) == NULL) {
(void)printf("sydTestAddFromText: couldn't malloc for test struct\n");
free(myText);
return S_syd_noMem;
}
pSspec->pAccept = pTest;
pTest->type = pTest->cond = 0;
pTest->pSChan = NULL;
if (nextAlphField(&pText, &pFld, &delim) <= 1) {
pMsg = "no trigger keyword found";
goto trigScanErr;
}
if (strcmp(pFld, "if") == 0)
pTest->type = SYD_TST_IF;
else {
pMsg = "illegal test type";
goto trigScanErr;
}
if (pTest->type == SYD_TST_IF) {
/*-----------------------------------------------------------------------------
* if chanName condition
*----------------------------------------------------------------------------*/
if (nextChanNameField(&pText, &name, &delim) <= 1) {
pMsg = "no channel name found";
goto trigScanErr;
}
if (strcmp(name, "F") == 0) {
sync = SYD_SY_FILLED;
if (nextChanNameField(&pText, &name, &delim) <= 1) {
pMsg = "no channel name found";
goto trigScanErr;
}
}
stat = sydChanOpen(pSspec, &pSChan, name, sync, NULL, 1);
if (stat != S_syd_OK && stat != S_syd_chanNotConn) {
pMsg = "couldn't find channel";
goto trigScanErr;
}
pTest->pSChan = pSChan;
if (nextNonSpaceField(&pText, &pFld, &delim) <= 1) {
pMsg = "condition expected";
goto trigScanErr;
}
for (i=1; i<SYD_TSTC_PAST; i++) {
if (strcmp(pFld, glSydTestText[i]) == 0)
break;
}
if (i >= SYD_TSTC_PAST || i == SYD_TSTC_CHANGE) {
pMsg = "condition expected";
goto trigScanErr;
}
pTest->cond = i;
if (nextNonSpaceField(&pText, &pFld, &delim) <= 1) {
pMsg = "comparison value expected";
goto trigScanErr;
}
if (strlen(pFld) >= db_strval_dim) {
pMsg = "comparison value too long";
goto trigScanErr;
}
strcpy(pTest->strVal, pFld);
if (sscanf(pFld, "%lf", &pTest->dblVal) != 1)
pTest->dblVal = 0.;
pTest->lngVal = pTest->dblVal;
}
free(myText);
pSspec->pAccept = pTest;
return S_syd_OK;
trigScanErr:
(void)printf("sydTestAddFromText: %s\n%s\n", pMsg, myText);
sydTestClose(pSspec);
free(myText);
return S_syd_ERROR;
}
/*+/subr**********************************************************************
* NAME sydTestClose - add a sample test condition
*
* DESCRIPTION
* Closes a test condition.
*
* RETURNS
* S_syd_OK
*
* BUGS
* o text
*
* SEE ALSO
*
* NOTES
* 1. The test structure must be closed by calling sydTestClose.
* (This happens automatically when sydClose is called.)
*
* EXAMPLE
*
*-*/
long
sydTestClose(pSspec)
SYD_SPEC *pSspec; /* I pointer to synchronous set spec */
{
SYD_TEST *pTest;
assert(pSspec != NULL);
if ((pTest = pSspec->pAccept) == NULL)
return S_syd_OK;
if (pTest->pSChan == NULL) {
free((char *)pTest);
pSspec->pAccept = NULL;
}
else if (pTest->pSChan->dataChan == 0)
sydChanClose(pSspec, pTest->pSChan);
else {
pTest->pSChan->testChan = 0;
free((char *)pTest);
pSspec->pAccept = NULL;
}
return S_syd_OK;
}
/*+/subr**********************************************************************
* NAME sydTsRound - round time stamp to nearest millisecond
*
*-*/
sydTsRound(pStamp, roundNsec)
TS_STAMP *pStamp;
int roundNsec;
{
unsigned long roundTemp;
roundTemp = pStamp->nsec;
roundTemp = ( (roundTemp + roundNsec/2) / roundNsec ) * roundNsec;
if (roundTemp < 1000000000)
pStamp->nsec = roundTemp;
else {
pStamp->nsec = roundTemp - 1000000000;
pStamp->secPastEpoch++;
}
}
/*+/subr**********************************************************************
* NAME sydValAsDbl - fetch value from channel as a double
*
* RETURNS
* 1, or
* 0 if the value is missing or EOF, or if the type is invalid
*
*-*/
int
sydValAsDbl(pSChan, sampNum, pDbl)
SYD_CHAN *pSChan; /* I sync channel pointer */
int sampNum; /* I sample number */
double *pDbl; /* O value */
{
if (pSChan->pFlags[sampNum].missing || pSChan->pFlags[sampNum].eof)
return 0;
if (pSChan->dbfType == DBF_FLOAT)
*pDbl = (double)(((float *)pSChan->pData)[sampNum]);
else if (pSChan->dbfType == DBF_DOUBLE)
*pDbl = ((double *)pSChan->pData)[sampNum];
else if (pSChan->dbfType == DBF_SHORT) {
if (pSChan->isRVAL)
*pDbl = (double)(((unsigned short *)pSChan->pData)[sampNum]);
else
*pDbl = (double)(((short *)pSChan->pData)[sampNum]);
}
else if (pSChan->dbfType == DBF_LONG) {
if (pSChan->isRVAL)
*pDbl = (double)(((unsigned long *)pSChan->pData)[sampNum]);
else
*pDbl = (double)(((long *)pSChan->pData)[sampNum]);
}
else if (pSChan->dbfType == DBF_CHAR)
*pDbl = (double)(((char *)pSChan->pData)[sampNum]);
else
return 0;
return 1;
}