/*	$Id$
 *	Author:	Roger A. Cole
 *	Date:	12-04-90
 *
 *	Experimental Physics and Industrial Control System (EPICS)
 *
 *	Copyright 1991-92, the Regents of the University of California,
 *	and the University of Chicago Board of Governors.
 *
 *	This software was produced under  U.S. Government contracts:
 *	(W-7405-ENG-36) at the Los Alamos National Laboratory,
 *	and (W-31-109-ENG-38) at Argonne National Laboratory.
 *
 *	Initial development by:
 *		The Controls and Automation Group (AT-8)
 *		Ground Test Accelerator
 *		Accelerator Technology Division
 *		Los Alamos National Laboratory
 *
 *	Co-developed with
 *		The Controls and Computing Group
 *		Accelerator Systems Division
 *		Advanced Photon Source
 *		Argonne National Laboratory
 *
 * Modification Log:
 * -----------------
 *  .00 12-04-90 rac	initial version
 *  .01 06-18-91 rac	installed in SCCS
 *  .02 06-19-91 rac	replace <fields.h> with <alarm.h>
 *  .03 08-06-91 rac	allow specifying file name
 *  .04 09-11-91 rac	finish allowing user specified name
 *  .05 02-27-92 rac	do ts rounding here instead of sydSubr.c
 *  .06 09-14-92 rac	discontinue use of special malloc routines
 *
 * make options
 *	-DvxWorks	makes a version for VxWorks
 *	-DNDEBUG	don't compile assert() checking
 *      -DDEBUG         compile various debug code
 */
/*+/mod***********************************************************************
* TITLE	sydSubrCF.c - acquire synchronous samples from `by channel' AR file
*
* DESCRIPTION
*	
*-***************************************************************************/
#include <genDefs.h>
#define SYD_PRIVATE
#include <sydDefs.h>
#include <alarm.h>
#ifndef INC_arAccessLib_h
#   include <arAccessLib.h>
#endif
#ifndef INC_tsDefs_h
#   include <tsDefs.h>
#endif
#ifndef INC_arCS_h
#   include <arCS.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>
#else
#   include <stdio.h>
#   include <sys/file.h>	/* for O_RDWR and O_RDONLY definitions */
#endif
long sydCFFunc();
long sydCFFuncGetGR();


/*+/subr**********************************************************************
* NAME	sydOpenCF - open an AR `by channel' file for retrieval
*
* DESCRIPTION
*	Open an archiver `by channel' file for synchronous data retrieval.
*
* RETURNS
*	S_syd_OK, or
*	S_syd_ERROR, or
*	other code indicating error
*
* BUGS
* o	text
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
sydOpenCF(ppSspec, filePath)
SYD_SPEC **ppSspec;	/* O pointer to synchronous set spec pointer */
char	*filePath;	/* I path name for `by channel' archive file */
{
    long	stat;

    assert(ppSspec != NULL);

    if ((*ppSspec = (SYD_SPEC *)malloc(sizeof(SYD_SPEC))) == NULL)
	return S_syd_noMem;
    (*ppSspec)->pFunc = sydCFFunc;
    (*ppSspec)->type = SYD_TY_CF;
    if ((stat = sydCFFunc(*ppSspec, NULL, SYD_FC_INIT, filePath)) != S_syd_OK){
	free((char *)*ppSspec);
	*ppSspec = NULL;
	return stat;
    }
    (*ppSspec)->nInBufs = 2;
    return sydOpen(ppSspec);
}
/*+/subr**********************************************************************
* NAME	sydCFFunc - handle "by channel" data file interactions
*
* DESCRIPTION
*
* sydCFFunc(pSspec, NULL, SYD_FC_INIT, filePath)  open "by channel" file
* sydCFFunc(pSspec, pSChan, SYD_FC_OPEN, NULL)    chanName already in pSChan
* sydCFFunc(pSspec, pSChan, SYD_FC_READ, NULL)
* sydCFFunc(pSspec, NULL, SYD_FC_READ_SYNC, NULL) sync "by channel" file
* sydCFFunc(pSspec, pSChan, SYD_FC_POSITION, &stamp)
* sydCFFunc(pSspec, pSChan, SYD_FC_CLOSE, NULL)
* sydCFFunc(pSspec, NULL, SYD_FC_FILEINFO, outFile)
* sydCFFunc(pSspec, NULL, SYD_FC_WRAPUP, NULL)	  close "by channel" file
*
* RETURNS
*	S_syd_OK, or
*	S_syd_EOF, or
*	other code indicating error
*
* BUGS
* o	doesn't detect or report EOF
* o	doesn't do anything (or even detect) overwriting un-sampled
*	buffers (for SYD_FC_READ)
*
* SEE ALSO
*
* EXAMPLE
*
*-*/
long
sydCFFunc(pSspec, pStruct, funcCode, pArg)
SYD_SPEC *pSspec;	/* IO pointer to synchronous set spec */
void	*pStruct;	/* IO pointer to data struct used by funcCode */
enum sydFuncCode funcCode;/* I function code */
void	*pArg;		/* I pointer to arg, as required by funcCode */
{
    SYD_CHAN	*pSChan;	/* pointer to syncSet channel descriptor */
    AR_CHAN_DESC *pChanDesc=NULL;/* pointer to chanDesc for channel */
    AR_CF_DESC	*pCfDesc;	/* pointer to cfDesc for channel file */
    long	retStat=S_syd_OK;
    long	stat, inStatus;
    int		i;

    pSChan = (SYD_CHAN *)pStruct;
    if (pSChan != NULL)
	pChanDesc = (AR_CHAN_DESC *)pSChan->pHandle;
    if (pSspec != NULL)
	pCfDesc = (AR_CF_DESC *)pSspec->pHandle;

    if (funcCode ==					SYD_FC_INIT) {
	pCfDesc = arCFOpen(pArg, O_RDONLY);
	if (pCfDesc == NULL) {
	    (void)printf("couldn't open %s\n", pArg);
	    retStat = S_syd_ERROR;
	}
	pSspec->pHandle = (void *)pCfDesc;
    }
    else if (funcCode ==				SYD_FC_OPEN) {
	pChanDesc = arCFChanOpen(pCfDesc, pSChan->name, O_RDONLY);
	if (pChanDesc == NULL)
	    retStat = S_syd_chanNotFound;
	else {
	    pSChan->pHandle = (void *)pChanDesc;
	    pSChan->dbfType = ArCFChanFieldType(pChanDesc);
	    pSChan->dbrType = dbf_type_to_DBR_TIME(pSChan->dbfType);
	    pSChan->elCount = ArCFChanElementCount(pChanDesc);
	    sydCFFuncGetGR(pSChan, pChanDesc);
	}
    }
    else if (funcCode ==				SYD_FC_READ) {
	i = NEXT_INBUF(pSChan, pSChan->lastInBuf);
	pSChan->inStatus[i] = inStatus = SYD_B_EMPTY;
	while (pSChan->inStatus[i] == SYD_B_EMPTY) {
	    stat = arCFChanRead(pChanDesc,
		pSChan->dbrType, pSChan->pInBuf[i], pSChan->elCount);
	    if (stat == OK) {
		if (inStatus == SYD_B_EMPTY)
		    pSChan->inStatus[i] = SYD_B_FULL;
		else
		    pSChan->inStatus[i] = inStatus;
		if (pSspec->roundNsec > 0) {
		    sydTsRound(&pSChan->pInBuf[i]->tfltval.stamp,
							pSspec->roundNsec);
		}
	    }
	    else if ((pChanDesc->flags & AR_CDESC_EOF) != 0)
		pSChan->inStatus[i] = SYD_B_EOF;
	    else if ((pChanDesc->flags & AR_CDESC_SNAP_BEGIN) != 0) {
		if ((pChanDesc->flags & AR_CDESC_SNAP_END) != 0)
		    inStatus = SYD_B_SNAP_SINGLE;
		else
		    inStatus = SYD_B_SNAP_BEGIN;
	    }
	    else if ((pChanDesc->flags & AR_CDESC_SNAP_END) != 0)
		inStatus = SYD_B_SNAP_END;
	    else if ((pChanDesc->flags & AR_CDESC_BEGIN) != 0)
		inStatus = SYD_B_RESTART;
	}
	pSChan->lastInBuf = i;
	if (pSChan->firstInBuf < 0)
	    pSChan->firstInBuf = i;
    }
    else if (funcCode ==				SYD_FC_STOP) {
    }
    else if (funcCode ==				SYD_FC_READ_SYNC) {
	/* synchronize file buf in memory with data on disk */
	stat = arCFSyncRead(pCfDesc);
	if (stat != OK)
	    retStat = S_syd_ERROR;
    }
    else if (funcCode ==				SYD_FC_POSITION) {
	stat = arCFChanPosition(pChanDesc, (TS_STAMP *)pArg);
	if (stat != OK) {
	    if ((pChanDesc->flags & AR_CDESC_EOF) == 0) {
		(void)printf("error for %s\n", pSChan->name);
		assertAlways(0);
	    }
	}
    }
    else if (funcCode ==				SYD_FC_CLOSE) {
	if (pSChan->pHandle != NULL)
	    retStat = arCFChanClose((AR_CHAN_DESC **)&pSChan->pHandle);
    }
    else if (funcCode ==				SYD_FC_FILEINFO) {
	TS_STAMP	oldestStamp, newestStamp;
	char		oldestStampText[28], newestStampText[28];
	char		name[db_name_dim];
	int		j;

	assert(pCfDesc != NULL);
	if (ArB0MIHead(pCfDesc) != 0) {
	    AR_MI_BUF *pMICurrent;
	    AR_CHAN_HDR *pChanHdr;

	    assert(ArB0MIHead(pCfDesc) > 0);
	    assert(ArB0MITail(pCfDesc) > 0);
	    assert(ArB0MINblk(pCfDesc) > 0);
	    assert(pCfDesc->pMIHead != NULL);
	    assert(pCfDesc->pMITail != NULL);

	    pMICurrent = pCfDesc->pMIHead;
	    while (pMICurrent != NULL) {
		int i;

		for (i=0; i<AR_CF_NHDR; i++) {
		    pChanHdr = &pMICurrent->chanHdr[i];

		    if (pChanHdr->name[0] != '\0') {
			strcpy(name, pChanHdr->name);
			j = strlen(name);
			if (j > 4) {
			    if (strncmp(&name[j-4], ".VAL", 4) == 0)
				name[j-4] = '\0';
			}
			oldestStamp.secPastEpoch = pChanHdr->oldestSecPastEpoch;
			oldestStamp.nsec = 0;
			newestStamp.secPastEpoch = pChanHdr->newestSecPastEpoch;
			newestStamp.nsec = 0;
			(void)fprintf((FILE *)pArg, "%s %d %s %s  %s\n",
                                   name, pChanHdr->elCount,
                                   dbf_type_to_text(pChanHdr->chanType),
                                   tsStampToText(&oldestStamp, TS_TEXT_MMDDYY,
                                                        oldestStampText),
                                   tsStampToText(&newestStamp, TS_TEXT_MMDDYY,
                                                        newestStampText) );
		    }
		}    
		pMICurrent = pMICurrent->pNextMI;
	    }
	}
	else
	    (void)fprintf((FILE *)pArg, "no channels in file\n");
    }
    else if (funcCode ==				SYD_FC_WRAPUP) {
	if (pCfDesc != NULL) {
	    if ((stat = arCFClose(&pCfDesc)) != OK) {
		(void)printf("trouble closing arChan/arChanData.bin\n");
		retStat = S_syd_ERROR;
	    }
	}
	pSspec->pHandle = NULL;
    }

    return retStat;
}

long
sydCFFuncGetGR(pSChan, pChanDesc)
SYD_CHAN *pSChan;	/* pointer to syncSet channel descriptor */
AR_CHAN_DESC *pChanDesc;	/* pointer to chanDesc for channel */
{
    int		i;

    if (pSChan->dbrType == DBR_TIME_FLOAT) {
#define FLT_SRC ArCDChanHdr(pChanDesc).graphics.floatGr
#define FLT_DEST pSChan->grBuf.gfltval
	FLT_DEST.status = NO_ALARM;
	FLT_DEST.severity = NO_ALARM;
	FLT_DEST.precision = FLT_SRC.precision;
	(void)strcpy(FLT_DEST.units, FLT_SRC.units);
	FLT_DEST.upper_disp_limit = FLT_SRC.upDispLim;
	FLT_DEST.lower_disp_limit = FLT_SRC.lowDispLim;
	FLT_DEST.upper_alarm_limit = FLT_SRC.upAlmLim;
	FLT_DEST.lower_alarm_limit = FLT_SRC.lowAlmLim;
	FLT_DEST.upper_warning_limit = FLT_SRC.upWarnLim;
	FLT_DEST.lower_warning_limit = FLT_SRC.lowWarnLim;
	pSChan->dbrGrType = DBR_GR_FLOAT;
    }
    else if (pSChan->dbrType == DBR_TIME_SHORT) {
#define SHRT_SRC ArCDChanHdr(pChanDesc).graphics.shortGr
#define SHRT_DEST pSChan->grBuf.gshrtval
	SHRT_DEST.status = NO_ALARM;
	SHRT_DEST.severity = NO_ALARM;
	(void)strcpy(SHRT_DEST.units, SHRT_SRC.units);
	SHRT_DEST.upper_disp_limit = SHRT_SRC.upDispLim;
	SHRT_DEST.lower_disp_limit = SHRT_SRC.lowDispLim;
	SHRT_DEST.upper_alarm_limit = SHRT_SRC.upAlmLim;
	SHRT_DEST.lower_alarm_limit = SHRT_SRC.lowAlmLim;
	SHRT_DEST.upper_warning_limit = SHRT_SRC.upWarnLim;
	SHRT_DEST.lower_warning_limit = SHRT_SRC.lowWarnLim;
	pSChan->dbrGrType = DBR_GR_SHORT;
    }
    else if (pSChan->dbrType == DBR_TIME_DOUBLE) {
#define DBL_SRC ArCDChanHdr(pChanDesc).graphics.doubleGr
#define DBL_DEST pSChan->grBuf.gdblval
	DBL_DEST.status = NO_ALARM;
	DBL_DEST.severity = NO_ALARM;
	DBL_DEST.precision = DBL_SRC.precision;
	(void)strcpy(DBL_DEST.units, DBL_SRC.units);
	DBL_DEST.upper_disp_limit = DBL_SRC.upDispLim;
	DBL_DEST.lower_disp_limit = DBL_SRC.lowDispLim;
	DBL_DEST.upper_alarm_limit = DBL_SRC.upAlmLim;
	DBL_DEST.lower_alarm_limit = DBL_SRC.lowAlmLim;
	DBL_DEST.upper_warning_limit = DBL_SRC.upWarnLim;
	DBL_DEST.lower_warning_limit = DBL_SRC.lowWarnLim;
	pSChan->dbrGrType = DBR_GR_DOUBLE;
    }
    else if (pSChan->dbrType == DBR_TIME_LONG) {
#define LNG_SRC ArCDChanHdr(pChanDesc).graphics.longGr
#define LNG_DEST pSChan->grBuf.glngval
	LNG_DEST.status = NO_ALARM;
	LNG_DEST.severity = NO_ALARM;
	(void)strcpy(LNG_DEST.units, LNG_SRC.units);
	LNG_DEST.upper_disp_limit = LNG_SRC.upDispLim;
	LNG_DEST.lower_disp_limit = LNG_SRC.lowDispLim;
	LNG_DEST.upper_alarm_limit = LNG_SRC.upAlmLim;
	LNG_DEST.lower_alarm_limit = LNG_SRC.lowAlmLim;
	LNG_DEST.upper_warning_limit = LNG_SRC.upWarnLim;
	LNG_DEST.lower_warning_limit = LNG_SRC.lowWarnLim;
	pSChan->dbrGrType = DBR_GR_LONG;
    }
    else if (pSChan->dbrType == DBR_TIME_STRING) {
#define STR_DEST pSChan->grBuf.gstrval
	STR_DEST.status = NO_ALARM;
	STR_DEST.severity = NO_ALARM;
	pSChan->dbrGrType = DBR_GR_STRING;
    }
    else if (pSChan->dbrType == DBR_TIME_ENUM) {
#define ENM_SRC ArCDChanHdr(pChanDesc).graphics.enumGr
#define ENM_DEST pSChan->grBuf.genmval
	ENM_DEST.status = NO_ALARM;
	ENM_DEST.severity = NO_ALARM;
	ENM_DEST.no_str = ArCFChanNStates(pChanDesc);
	for (i=0; i<ENM_DEST.no_str; i++)
	    (void)strcpy(ENM_DEST.strs[i], ArCFChanStates(pChanDesc)[i]);
	pSChan->dbrGrType = DBR_GR_ENUM;
    }
    else if (pSChan->dbrType == DBR_TIME_CHAR) {
#define CHR_SRC ArCDChanHdr(pChanDesc).graphics.charGr
#define CHR_DEST pSChan->grBuf.gchrval
	CHR_DEST.status = NO_ALARM;
	CHR_DEST.severity = NO_ALARM;
	(void)strcpy(CHR_DEST.units, CHR_SRC.units);
	CHR_DEST.upper_disp_limit = CHR_SRC.upDispLim;
	CHR_DEST.lower_disp_limit = CHR_SRC.lowDispLim;
	CHR_DEST.upper_alarm_limit = CHR_SRC.upAlmLim;
	CHR_DEST.lower_alarm_limit = CHR_SRC.lowAlmLim;
	CHR_DEST.upper_warning_limit = CHR_SRC.upWarnLim;
	CHR_DEST.lower_warning_limit = CHR_SRC.lowWarnLim;
	pSChan->dbrGrType = DBR_GR_CHAR;
    }
    return S_syd_OK;
}
