516 lines
13 KiB
C
516 lines
13 KiB
C
/*---------------------------------------------------------------------------
|
|
|
|
V A R L O G
|
|
|
|
A module for keeping a log of a variable.
|
|
|
|
Mark Koennecke, September 1997
|
|
|
|
Substantially revised to calculate running means and standard deviations
|
|
instead of storing data in a list. The module now supports running
|
|
averages and logging to file.
|
|
|
|
Mark Koennecke, April 2000
|
|
|
|
Added support for a circular buffer of logged values.
|
|
|
|
Mark Koennecke, December 2003
|
|
|
|
Copyright:
|
|
|
|
Labor fuer Neutronenstreuung
|
|
Paul Scherrer Institut
|
|
CH-5423 Villigen-PSI
|
|
|
|
|
|
The authors hereby grant permission to use, copy, modify, distribute,
|
|
and license this software and its documentation for any purpose, provided
|
|
that existing copyright notices are retained in all copies and that this
|
|
notice is included verbatim in any distributions. No written agreement,
|
|
license, or royalty fee is required for any of the authorized uses.
|
|
Modifications to this software may be copyrighted by their authors
|
|
and need not follow the licensing terms described here, provided that
|
|
the new terms are clearly indicated on the first page of each file where
|
|
they apply.
|
|
|
|
IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
|
|
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
|
|
ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
|
|
DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
|
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
|
|
IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
|
|
NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
|
|
MODIFICATIONS.
|
|
----------------------------------------------------------------------------*/
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <math.h>
|
|
#include "fortify.h"
|
|
#include "lld.h"
|
|
#include "sics.h"
|
|
#include "varlog.h"
|
|
#include "commandlog.h"
|
|
#include "circular.h"
|
|
#include "sicsdata.h"
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
/*
|
|
maximum values in the circular buffer
|
|
*/
|
|
#define MAXRING 1024
|
|
/*------------------------------------------------------------------------*/
|
|
typedef struct __VarLog
|
|
{
|
|
time_t tFrequency;
|
|
time_t tNext;
|
|
int iCount;
|
|
double dSum;
|
|
double dDeviation;
|
|
FILE *fd;
|
|
pCircular pTail;
|
|
}VarLog;
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
typedef struct {
|
|
time_t tTime;
|
|
float fVal;
|
|
}LogItem, *pLogItem;
|
|
/*--------------------------------------------------------------------------*/
|
|
int VarlogInit(pVarLog *self)
|
|
{
|
|
int i;
|
|
pVarLog pNew = NULL;
|
|
|
|
pNew = (pVarLog)malloc(sizeof(VarLog));
|
|
if(!pNew)
|
|
{
|
|
return 0;
|
|
}
|
|
pNew->tFrequency = 300; /* 5 minutes */
|
|
pNew->tNext = 0;
|
|
pNew->iCount = 0;
|
|
pNew->dSum = 0.;
|
|
pNew->dDeviation = 0.;
|
|
pNew->fd = NULL;
|
|
pNew->pTail = createCircular(MAXRING,free);
|
|
if(!pNew->pTail)
|
|
{
|
|
VarlogDelete(pNew);
|
|
return 0;
|
|
}
|
|
|
|
*self = pNew;
|
|
return 1;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
int VarlogDelete(pVarLog self)
|
|
{
|
|
if(self->fd != NULL)
|
|
{
|
|
fclose(self->fd);
|
|
}
|
|
if(self->pTail != NULL)
|
|
{
|
|
deleteCircular(self->pTail);
|
|
}
|
|
return 1;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
int VarlogClear(pVarLog self)
|
|
{
|
|
int iRet, iList;
|
|
|
|
self->iCount = 0;
|
|
self->dSum = 0.;
|
|
self->dDeviation = 0.;
|
|
self->tNext = 0;
|
|
return 1;
|
|
}
|
|
/*-------------------------------------------------------------------------*/
|
|
static void VLFormatTime(time_t tTime, char *pBuffer, int iBufLen)
|
|
{
|
|
struct tm *psTime;
|
|
|
|
/* make time string */
|
|
psTime = localtime(&tTime);
|
|
memset(pBuffer,0,iBufLen);
|
|
strftime(pBuffer,iBufLen,"%Y-%d-%m %H:%M:%S",psTime);
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
int VarlogAdd(pVarLog self, float fVal)
|
|
{
|
|
LogItem sItem;
|
|
time_t tCurrent;
|
|
int iFile = 0;
|
|
char pBuffer[80];
|
|
double dMean, dTmp;
|
|
pLogItem newLog = NULL;
|
|
|
|
assert(self);
|
|
|
|
tCurrent = time(NULL);
|
|
|
|
/* check if we are logging to file */
|
|
if(self->fd != NULL)
|
|
{
|
|
iFile = 1;
|
|
}
|
|
|
|
|
|
/* update the running mean */
|
|
self->dSum += fVal;
|
|
self->iCount++;
|
|
dMean = self->dSum/self->iCount;
|
|
dTmp = fVal - dMean;
|
|
self->dDeviation += dTmp*dTmp;
|
|
|
|
|
|
/* if not initialised, do it */
|
|
if(self->tNext < 1)
|
|
{
|
|
if(iFile)
|
|
{
|
|
VLFormatTime(tCurrent,pBuffer,79);
|
|
fprintf(self->fd," %s %f \n", pBuffer,fVal);
|
|
}
|
|
self->tNext = tCurrent + self->tFrequency;
|
|
return 1;
|
|
}
|
|
|
|
/* if, log time passed, write to file and into ring buffer */
|
|
if(tCurrent > self->tNext)
|
|
{
|
|
if(iFile)
|
|
{
|
|
VLFormatTime(tCurrent,pBuffer,79);
|
|
fprintf(self->fd," %s %f \n", pBuffer,fVal);
|
|
}
|
|
newLog = (pLogItem)malloc(sizeof(LogItem));
|
|
if(newLog != NULL)
|
|
{
|
|
newLog->tTime = tCurrent;
|
|
newLog->fVal = fVal;
|
|
setCircular(self->pTail,newLog);
|
|
nextCircular(self->pTail);
|
|
}
|
|
self->tNext = tCurrent + self->tFrequency;
|
|
return 1;
|
|
}
|
|
return 1;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
int VarlogLength(pVarLog self, int *iLength)
|
|
{
|
|
*iLength = self->iCount;
|
|
return 1;
|
|
}
|
|
/*---------------------------------------------------------------------------*/
|
|
int VarlogGetMean(pVarLog self, float *fMean, float *fStdDev)
|
|
{
|
|
double dMean, dStdDev;
|
|
int success;
|
|
|
|
if(self->iCount > 0)
|
|
{
|
|
dMean = self->dSum/(double)self->iCount;
|
|
dStdDev = sqrt(self->dDeviation/(double)self->iCount);
|
|
success = 1;
|
|
}
|
|
else
|
|
{
|
|
dMean = .0;
|
|
dStdDev = .0;
|
|
success = 0;
|
|
}
|
|
*fMean = (float)dMean;
|
|
*fStdDev = (float)dStdDev;
|
|
|
|
return success;
|
|
}
|
|
/*-----------------------------------------------------------------------*/
|
|
static int VarlogToSicsData(pVarLog self, pSICSData data)
|
|
{
|
|
int i, length;
|
|
int *dataPtr = NULL;
|
|
pLogItem log = NULL;
|
|
|
|
dataPtr = getSICSDataPointer(data,0,2*MAXRING+1);
|
|
if(!dataPtr)
|
|
{
|
|
return 0;
|
|
}
|
|
dataPtr[0] = MAXRING;
|
|
|
|
/*
|
|
skip back MAXRING steps
|
|
*/
|
|
for(i = 0; i < MAXRING; i++)
|
|
{
|
|
previousCircular(self->pTail);
|
|
}
|
|
/*
|
|
forward again and copy data
|
|
*/
|
|
for(i = 0; i < MAXRING; i++)
|
|
{
|
|
log = getCircular(self->pTail);
|
|
if(log != NULL)
|
|
{
|
|
dataPtr[i+1] = (int)log->tTime;
|
|
memcpy(&dataPtr[i+1+MAXRING],&log->fVal,sizeof(float));
|
|
}
|
|
nextCircular(self->pTail);
|
|
}
|
|
|
|
/*
|
|
assign data types
|
|
*/
|
|
assignSICSType(data,0,MAXRING+1,INTTYPE);
|
|
assignSICSType(data,MAXRING+1,2*MAXRING+1,FLOATTYPE);
|
|
|
|
return 1;
|
|
}
|
|
/*-----------------------------------------------------------------------*/
|
|
static void VarlogDump(pVarLog self, SConnection *pCon)
|
|
{
|
|
int i, length;
|
|
pLogItem log = NULL;
|
|
char timeBuffer[132], pBueffel[256];
|
|
|
|
/*
|
|
skip back MAXRING steps
|
|
*/
|
|
for(i = 0; i < MAXRING; i++)
|
|
{
|
|
previousCircular(self->pTail);
|
|
}
|
|
/*
|
|
forward again and print data
|
|
*/
|
|
for(i = 0; i < MAXRING; i++)
|
|
{
|
|
log = getCircular(self->pTail);
|
|
if(log != NULL)
|
|
{
|
|
if(log->tTime > 0)
|
|
{
|
|
VLFormatTime(log->tTime,timeBuffer,131);
|
|
snprintf(pBueffel,255,"%s %12.3f",timeBuffer,log->fVal);
|
|
SCWrite(pCon,pBueffel,eValue);
|
|
}
|
|
}
|
|
nextCircular(self->pTail);
|
|
}
|
|
}
|
|
/*-----------------------------------------------------------------------*/
|
|
static void VarLogDumpFile(pVarLog self, FILE *fd)
|
|
{
|
|
int i, length;
|
|
pLogItem log = NULL;
|
|
char timeBuffer[132], pBueffel[256];
|
|
|
|
/*
|
|
skip back MAXRING steps
|
|
*/
|
|
for(i = 0; i < MAXRING; i++)
|
|
{
|
|
previousCircular(self->pTail);
|
|
}
|
|
/*
|
|
forward again and print data
|
|
*/
|
|
for(i = 0; i < MAXRING; i++)
|
|
{
|
|
log = getCircular(self->pTail);
|
|
if(log != NULL)
|
|
{
|
|
if(log->tTime > 0)
|
|
{
|
|
VLFormatTime(log->tTime,timeBuffer,131);
|
|
fprintf(fd,"%s %12.3f",timeBuffer,log->fVal);
|
|
}
|
|
}
|
|
nextCircular(self->pTail);
|
|
}
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
int VarlogWrapper(pVarLog self, SConnection *pCon,
|
|
char *subcommand, char *sub2, char *pVarName)
|
|
{
|
|
float fMean, fStdDev, *fData = NULL;
|
|
time_t *pTArray = NULL;
|
|
int iLength, iRet, i, iList;
|
|
char pBueffel[256];
|
|
char *pData = NULL;
|
|
long lNew;
|
|
pSICSData data = NULL;
|
|
FILE *fd = NULL;
|
|
|
|
|
|
strtolower(subcommand);
|
|
/*--------- file */
|
|
if(strcmp(subcommand,"file") == 0)
|
|
{
|
|
if(self->fd != NULL)
|
|
{
|
|
fclose(self->fd);
|
|
self->fd = NULL;
|
|
}
|
|
self->fd = fopen(sub2,"w");
|
|
if(!self->fd)
|
|
{
|
|
sprintf(pBueffel,"ERROR: failed to open temperature log file: %s",
|
|
sub2);
|
|
SCWrite(pCon,pBueffel,eError);
|
|
return 0;
|
|
}
|
|
if(self->tFrequency < 20)
|
|
{
|
|
self->tFrequency = 300;
|
|
}
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
}
|
|
/*----- flush */
|
|
else if(strcmp(subcommand,"flush") == 0)
|
|
{
|
|
if(self->fd != NULL)
|
|
{
|
|
fflush(self->fd);
|
|
}
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
}
|
|
/*----- close */
|
|
else if(strcmp(subcommand,"close") == 0)
|
|
{
|
|
if(self->fd != NULL)
|
|
{
|
|
fclose(self->fd);
|
|
self->fd = NULL;
|
|
}
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
}
|
|
/*----- status */
|
|
else if(strcmp(subcommand,"status") == 0)
|
|
{
|
|
if(self->fd != NULL)
|
|
{
|
|
SCWrite(pCon,"Logging to file on",eValue);
|
|
}
|
|
else
|
|
{
|
|
SCWrite(pCon,"No Logging to file",eValue);
|
|
}
|
|
return 1;
|
|
}
|
|
/*--------- getmean */
|
|
else if(strcmp(subcommand,"getmean") == 0)
|
|
{
|
|
iRet = VarlogGetMean(self,&fMean,&fStdDev);
|
|
if(!iRet)
|
|
{
|
|
SCWrite(pCon,"WARNING: No mean and stddev log values available ",
|
|
eWarning);
|
|
return 0;
|
|
}
|
|
sprintf(pBueffel,"%s.Mean = %8.2f %s.StdDev = %8.2f",
|
|
pVarName,fMean,pVarName,fStdDev);
|
|
SCWrite(pCon,pBueffel,eValue);
|
|
return 1;
|
|
}
|
|
/*------------ clear */
|
|
else if(strcmp(subcommand,"clear") == 0)
|
|
{
|
|
return VarlogClear(self);
|
|
}
|
|
/*------------- frequency */
|
|
else if(strcmp(subcommand,"frequency") == 0)
|
|
{
|
|
if(sub2)
|
|
{
|
|
iRet = sscanf(sub2,"%ld",&lNew);
|
|
}
|
|
else
|
|
{
|
|
iRet = 0;
|
|
}
|
|
if(iRet == 1) /* new value */
|
|
{
|
|
self->tFrequency = lNew;
|
|
return 1;
|
|
}
|
|
else /* print */
|
|
{
|
|
sprintf(pBueffel,"%s.frequency = %d",pVarName,(int)self->tFrequency);
|
|
SCWrite(pCon,pBueffel,eValue);
|
|
return 1;
|
|
}
|
|
}
|
|
/*-------------- tosicsdata */
|
|
else if(strcmp(subcommand,"tosicsdata") == 0)
|
|
{
|
|
if(!sub2)
|
|
{
|
|
SCWrite(pCon,"ERROR: tosicsdata needs an argument",eError);
|
|
return 0;
|
|
}
|
|
data = FindCommandData(pServ->pSics,sub2,"SICSData");
|
|
if(!data)
|
|
{
|
|
snprintf(pBueffel,255,"ERROR: %s is no sicsdata object",sub2);
|
|
SCWrite(pCon,pBueffel,eError);
|
|
return 0;
|
|
}
|
|
iRet = VarlogToSicsData(self,data);
|
|
if(iRet == 0)
|
|
{
|
|
SCWrite(pCon,"ERROR: out of memory in VarlogToSicsData",eError);
|
|
return 0;
|
|
}
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
}
|
|
/* ---------------- dumpring */
|
|
else if(strcmp(subcommand,"dump") == 0)
|
|
{
|
|
VarlogDump(self,pCon);
|
|
return 1;
|
|
}
|
|
/*---------------- dumptofile */
|
|
else if(strcmp(subcommand,"dumptofile") == 0)
|
|
{
|
|
if(sub2 != NULL)
|
|
{
|
|
fd = fopen(sub2,"w");
|
|
}
|
|
if(fd == NULL)
|
|
{
|
|
snprintf(pBueffel,255,"ERROR: failed to open %s",sub2);
|
|
SCWrite(pCon,pBueffel,eError);
|
|
return 0;
|
|
}
|
|
VarLogDumpFile(self,fd);
|
|
fclose(fd);
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
}
|
|
/* command not recognized */
|
|
else
|
|
{
|
|
sprintf(pBueffel,"ERROR: %s no valid command to varlog",subcommand);
|
|
SCWrite(pCon,pBueffel,eError);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|