469 lines
12 KiB
C
469 lines
12 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 "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);
|
|
}
|
|
free(self); /* M.Z. */
|
|
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) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "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;
|
|
}
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "%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 */
|
|
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "%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 {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: %s no valid command to varlog", subcommand);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return 0;
|
|
}
|
|
}
|