684 lines
17 KiB
C
684 lines
17 KiB
C
/*-----------------------------------------------------------------------
|
|
This is a data handling class for histogram memory data.
|
|
For more information see hmdata.tex.
|
|
|
|
copyright: see file COPYRIGHT
|
|
|
|
Mark Koennecke, January 2003
|
|
|
|
Added loading HM data from file, Mark Koennecke, November 2006
|
|
-------------------------------------------------------------------------*/
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
#include <time.h>
|
|
#include "splitter.h"
|
|
#include "fortify.h"
|
|
#include "hmdata.h"
|
|
#include <nxdataset.h>
|
|
#include "HistMem.h"
|
|
#include "HistMem.i"
|
|
#include "HistDriv.i"
|
|
#include "countdriv.h"
|
|
#include "stptok.h"
|
|
/*----------------------------------------------------------------------*/
|
|
pHMdata makeHMData(void)
|
|
{
|
|
pHMdata self = NULL;
|
|
|
|
self = (pHMdata) malloc(sizeof(HMdata));
|
|
if (self == NULL) {
|
|
return NULL;
|
|
}
|
|
memset(self, 0, sizeof(HMdata));
|
|
self->nTimeChan = 1;
|
|
|
|
return self;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
void killHMData(pHMdata self)
|
|
{
|
|
if (self->localBuffer != NULL) {
|
|
free(self->localBuffer);
|
|
}
|
|
free(self);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
void clearHMData(pHMdata self)
|
|
{
|
|
long size;
|
|
int i;
|
|
size = 1;
|
|
for (i = 0; i < self->rank; i++) {
|
|
size *= self->iDim[i];
|
|
}
|
|
if (self->tofMode) {
|
|
size *= getNoOfTimebins(self);
|
|
}
|
|
if(self->localBuffer != NULL){
|
|
memset(self->localBuffer, 0, size * sizeof(HistInt));
|
|
}
|
|
self->nextUpdate = time(NULL) + self->updateIntervall;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
int resizeBuffer(pHMdata self)
|
|
{
|
|
long size;
|
|
int i;
|
|
|
|
size = 1;
|
|
for (i = 0; i < self->rank; i++) {
|
|
size *= self->iDim[i];
|
|
}
|
|
if (self->tofMode) {
|
|
size *= getNoOfTimebins(self);
|
|
}
|
|
if (self->localBuffer != NULL) {
|
|
free(self->localBuffer);
|
|
self->localBuffer = NULL;
|
|
}
|
|
self->localBuffer = (HistInt *) malloc(size * sizeof(HistInt));
|
|
if (!self->localBuffer) {
|
|
return 0;
|
|
}
|
|
memset(self->localBuffer, 0, size * sizeof(HistInt));
|
|
self->nextUpdate = 0;
|
|
return 1;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
int configureHMdata(pHMdata self, pStringDict pOpt, SConnection * pCon)
|
|
{
|
|
int status, i;
|
|
float fVal;
|
|
char pValue[80];
|
|
pHistMem master = NULL;
|
|
|
|
if (self->nTimeChan > 2) {
|
|
self->tofMode = 1;
|
|
} else {
|
|
self->tofMode = 0;
|
|
}
|
|
status = StringDictGetAsNumber(pOpt, "rank", &fVal);
|
|
if (!status) {
|
|
SCWrite(pCon, "ERROR: critical configuration problem: no rank found",
|
|
eError);
|
|
return 0;
|
|
}
|
|
self->rank = (int) rint(fVal);
|
|
|
|
for (i = 0; i < self->rank; i++) {
|
|
snprintf(pValue,sizeof(pValue)-1, "dim%1.1d", i);
|
|
status = StringDictGetAsNumber(pOpt, pValue, &fVal);
|
|
if (!status) {
|
|
snprintf(pValue,sizeof(pValue)-1, "ERROR dimension %d not found!!", i);
|
|
return 0;
|
|
}
|
|
self->iDim[i] = (int) rint(fVal);
|
|
}
|
|
|
|
status = StringDictGetAsNumber(pOpt, "update", &fVal);
|
|
if (!status) {
|
|
self->updateIntervall = 0; /* no buffering */
|
|
} else {
|
|
self->updateIntervall = (int) rint(fVal);
|
|
}
|
|
|
|
status = StringDictGet(pOpt, "timeslave", pValue, 79);
|
|
if (status == 1) {
|
|
master = (pHistMem) FindCommandData(pServ->pSics, pValue, "HistMem");
|
|
if (master == NULL) {
|
|
SCWrite(pCon, "ERROR: timeslave requested, but master HM not found",
|
|
eError);
|
|
} else {
|
|
self->timeslave = master->pDriv->data;
|
|
self->tofMode = 1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
invalidate buffer
|
|
*/
|
|
if (self->localBuffer != NULL) {
|
|
free(self->localBuffer);
|
|
self->localBuffer = NULL;
|
|
}
|
|
|
|
status = resizeBuffer(self);
|
|
if (!status) {
|
|
SCWrite(pCon, "ERROR: failed to resize buffer", eError);
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
int genTimeBinning(pHMdata self, float start, float step, int noSteps)
|
|
{
|
|
int i;
|
|
|
|
if (noSteps >= MAXCHAN || self->timeslave != NULL) {
|
|
return 0;
|
|
}
|
|
for (i = 0; i < noSteps; i++) {
|
|
self->timeBinning[i] = start + i * step;
|
|
}
|
|
self->tofMode = 1;
|
|
self->nTimeChan = noSteps;
|
|
return resizeBuffer(self);
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
int setTimeBin(pHMdata self, int index, float value)
|
|
{
|
|
if (self->timeslave != NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (index >= 0 && index < MAXCHAN) {
|
|
self->timeBinning[index] = value;
|
|
} else {
|
|
return 0;
|
|
}
|
|
self->tofMode = 1;
|
|
if (index >= self->nTimeChan) {
|
|
self->nTimeChan = index + 1;
|
|
return resizeBuffer(self);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
int isInTOFMode(pHMdata self)
|
|
{
|
|
return self->tofMode;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
int getNoOfTimebins(pHMdata self)
|
|
{
|
|
if (self->timeslave != NULL) {
|
|
return getNoOfTimebins(self->timeslave);
|
|
} else {
|
|
return self->nTimeChan;
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
float *getTimeBinning(pHMdata self)
|
|
{
|
|
if (self->timeslave != NULL) {
|
|
return getTimeBinning(self->timeslave);
|
|
} else {
|
|
return self->timeBinning;
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------------------------*/
|
|
void clearTimeBinning(pHMdata self)
|
|
{
|
|
if (self->timeslave == NULL) {
|
|
self->nTimeChan = 1;
|
|
self->tofMode = 0;
|
|
resizeBuffer(self);
|
|
}
|
|
}
|
|
|
|
/*--------------------------------------------------------------------*/
|
|
void getHMDataDim(pHMdata self, int iDim[MAXDIM], int *rank)
|
|
{
|
|
memcpy(iDim, self->iDim, self->rank * sizeof(int));
|
|
*rank = self->rank;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
long getHMDataLength(pHMdata self)
|
|
{
|
|
long length = 1;
|
|
int i;
|
|
for (i = 0; i < self->rank; i++) {
|
|
length *= self->iDim[i];
|
|
}
|
|
if (self->tofMode) {
|
|
length *= getNoOfTimebins(self);
|
|
}
|
|
return length;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
void updateHMData(pHMdata self)
|
|
{
|
|
self->nextUpdate = 0;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------
|
|
The idea here is that upper level code sets the updateFlag through
|
|
updateHMData (above) whenever the HM changes (counts). If this flag is set
|
|
the next call to get getHMDataHistogram will read a new copy from the HM.
|
|
After reading nextUpdate is set to time + updateIntervall. In order to
|
|
prevent clients hammering the HM nextUpdate is checked as well.
|
|
updateIntervall can be set to a reasonable time intervall between updates in seconds. If updateIntervall is 0, a direct read is always perfomed.
|
|
If this system needs to be bypassed altogether (because there is no memory
|
|
to buffer all HM) use GetHistogramDirect (histogram.c) instead which acts
|
|
on the driver level.
|
|
--------------------------------------------------------------------------*/
|
|
static int mustUpdate(pHMdata self)
|
|
{
|
|
if (time(NULL) >= self->nextUpdate) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------*/
|
|
static int updateHMbuffer(pHistMem hist, int bank, SConnection * pCon)
|
|
{
|
|
int status, iErr, i;
|
|
char pError[80], pBueffel[256];
|
|
pHMdata self = hist->pDriv->data;
|
|
|
|
assert(self);
|
|
|
|
if (self->timeslave != NULL) {
|
|
resizeBuffer(self);
|
|
}
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
status = hist->pDriv->GetHistogram(hist->pDriv, pCon,
|
|
bank, 0, getHMDataLength(self),
|
|
self->localBuffer);
|
|
if (status == OKOK) {
|
|
self->nextUpdate = time(NULL) + self->updateIntervall;
|
|
tracePar(hist->pDes->name,"!!datachange!!");
|
|
break;
|
|
} else {
|
|
status = hist->pDriv->GetError(hist->pDriv, &iErr, pError, 79);
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: %s ", pError);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
status = hist->pDriv->TryAndFixIt(hist->pDriv, iErr);
|
|
if (status == COTERM) {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
if (status == OKOK) {
|
|
return 1;
|
|
} else {
|
|
return HWFault;
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------*/
|
|
int getHMDataHistogram(pHistMem hist, SConnection * pCon,
|
|
int bank, int start, int length, HistInt * lData)
|
|
{
|
|
int status;
|
|
pHMdata self = hist->pDriv->data;
|
|
HistInt *lStart;
|
|
|
|
assert(self);
|
|
|
|
if (self->localBuffer == NULL) {
|
|
resizeBuffer(self);
|
|
}
|
|
|
|
/*
|
|
update buffer if necessary
|
|
*/
|
|
if (mustUpdate(self)) {
|
|
status = updateHMbuffer(hist, bank, pCon);
|
|
if (status != OKOK) {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
/*
|
|
copy buffered data to lData
|
|
*/
|
|
lStart = self->localBuffer + start;
|
|
if (start + length > getHMDataLength(self)) {
|
|
length = getHMDataLength(self) - start - 1;
|
|
}
|
|
memcpy(lData, lStart, length * sizeof(HistInt));
|
|
return 1;
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------*/
|
|
HistInt *getHMDataBufferPointer(pHistMem hist, SConnection * pCon)
|
|
{
|
|
int status;
|
|
pHMdata self = hist->pDriv->data;
|
|
|
|
assert(self);
|
|
|
|
if (self->localBuffer == NULL || self->timeslave != NULL) {
|
|
resizeBuffer(self);
|
|
}
|
|
/*
|
|
update buffer if necessary
|
|
*/
|
|
if (mustUpdate(self)) {
|
|
status = updateHMbuffer(hist, 0, pCon);
|
|
if (status != OKOK) {
|
|
return NULL;
|
|
}
|
|
}
|
|
return self->localBuffer;
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*/
|
|
static long SumRow(HistInt * iData, int iDataLength, int iStart, int iEnd)
|
|
{
|
|
int i;
|
|
long lSum;
|
|
|
|
if (iEnd > iDataLength) {
|
|
return -1;
|
|
}
|
|
|
|
lSum = 0;
|
|
for (i = iStart; i < iEnd; i++) {
|
|
lSum += iData[i];
|
|
}
|
|
return lSum;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
long sumHMDataRectangle(pHistMem hist, SConnection * pCon,
|
|
int iStart[MAXDIM], int iEnd[MAXDIM])
|
|
{
|
|
HistInt *iData;
|
|
pHMdata self = hist->pDriv->data;
|
|
int i, iHistLength, status, iIndex, myrank;
|
|
char pBueffel[256];
|
|
unsigned long lSum, lRowSum;
|
|
|
|
assert(self);
|
|
|
|
/*
|
|
error checking
|
|
*/
|
|
myrank = self->rank;
|
|
if (isInTOFMode(self)) {
|
|
self->iDim[self->rank] = getNoOfTimebins(self);
|
|
myrank++;
|
|
}
|
|
for (i = 0; i < myrank; i++) {
|
|
if ((iStart[i] < 0) || (iStart[i] > self->iDim[i])) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: %d is out of data dimension range",
|
|
iStart[i]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return -1;
|
|
}
|
|
if ((iEnd[i] < 0) || (iEnd[i] > self->iDim[i])) {
|
|
snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: %d is out of data dimension range",
|
|
iEnd[i]);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (self->localBuffer == NULL) {
|
|
resizeBuffer(self);
|
|
}
|
|
|
|
/*
|
|
get an update of the HM if necessary
|
|
*/
|
|
if (mustUpdate(self)) {
|
|
status = updateHMbuffer(hist, 0, pCon);
|
|
if (status != OKOK) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
iHistLength = getHMDataLength(self);
|
|
/* actually sum */
|
|
switch (myrank) {
|
|
case 1:
|
|
lSum = SumRow(self->localBuffer, iHistLength, iStart[0], iEnd[0]);
|
|
break;
|
|
case 2:
|
|
if (isInTOFMode(self)) {
|
|
lSum = 0;
|
|
for (i = iStart[0]; i < iEnd[0]; i++) {
|
|
iIndex = i * self->iDim[1];
|
|
lRowSum = SumRow(self->localBuffer, iHistLength,
|
|
iIndex + iStart[1], iIndex + iEnd[1]);
|
|
lSum += lRowSum;
|
|
}
|
|
} else {
|
|
lSum = 0;
|
|
for (i = iStart[1]; i < iEnd[1]; i++) {
|
|
iIndex = i * self->iDim[0];
|
|
lRowSum = SumRow(self->localBuffer, iHistLength,
|
|
iIndex + iStart[0], iIndex + iEnd[0]);
|
|
lSum += lRowSum;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
snprintf(pBueffel,sizeof(pBueffel)-1,
|
|
"ERROR: summing in %d dimensions not yet implemented", myrank);
|
|
SCWrite(pCon, pBueffel, eError);
|
|
return -1;
|
|
break;
|
|
}
|
|
return lSum;
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
int loadHMData(pHMdata self, SConnection * pCon, char *filename)
|
|
{
|
|
FILE *fd = NULL;
|
|
char buffer[1024], pNumber[80], *pPtr;
|
|
long i = 0, length;
|
|
HistInt *data = NULL;
|
|
|
|
fd = fopen(filename, "r");
|
|
if (fd == NULL) {
|
|
snprintf(buffer, 1023, "ERROR: failed to open file %s", filename);
|
|
SCWrite(pCon, buffer, eError);
|
|
return 0;
|
|
}
|
|
length = getHMDataLength(self);
|
|
if (self->localBuffer == NULL || self->timeslave != NULL) {
|
|
resizeBuffer(self);
|
|
}
|
|
data = self->localBuffer;
|
|
if (data == NULL) {
|
|
SCWrite(pCon, "ERROR: failed to allocate HM", eError);
|
|
fclose(fd);
|
|
return 0;
|
|
}
|
|
while (i < length && fgets(buffer, 1024, fd) != NULL) {
|
|
pPtr = buffer;
|
|
while (pPtr != NULL) {
|
|
pPtr = sicsNextNumber(pPtr, pNumber);
|
|
if (pPtr != NULL) {
|
|
data[i] = atoi(pNumber);
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
if (i < length - 1) {
|
|
SCWrite(pCon, "WARNING: not enough data in file to fill HM", eWarning);
|
|
}
|
|
fclose(fd);
|
|
return 1;
|
|
}
|
|
|
|
/*==========================================================================
|
|
* subsampling was stolen from the SinqHTTP histogram memory code and
|
|
* thus contains some additional indirections.
|
|
* =========================================================================*/
|
|
static pNXDS hmDataToNXDataset(pHMdata self)
|
|
{
|
|
pNXDS result = NULL;
|
|
int i;
|
|
|
|
result = malloc(sizeof(NXDS));
|
|
if (result == NULL) {
|
|
return NULL;
|
|
}
|
|
memset(result, 0, sizeof(NXDS));
|
|
result->magic = MAGIC;
|
|
result->type = NX_INT32;
|
|
result->rank = self->rank;
|
|
if (isInTOFMode(self)) {
|
|
result->rank++;
|
|
}
|
|
result->dim = malloc(self->rank * sizeof(int64_t));
|
|
if (result->dim == NULL) {
|
|
free(result);
|
|
return NULL;
|
|
}
|
|
for (i = 0; i < self->rank; i++) {
|
|
result->dim[i] = self->iDim[i];
|
|
}
|
|
if (isInTOFMode(self)) {
|
|
result->dim[result->rank - 1] = getNoOfTimebins(self);
|
|
}
|
|
if (self->localBuffer == NULL) {
|
|
resizeBuffer(self);
|
|
}
|
|
result->u.iPtr = self->localBuffer;
|
|
return result;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static pNXDS subSampleCommand(pNXDS source, char *command,
|
|
char *error, int errLen)
|
|
{
|
|
int64_t startDim[NX_MAXRANK], endDim[NX_MAXRANK];
|
|
int dim = 0, start = 0, i;
|
|
char *pPtr = NULL, token[80];
|
|
|
|
|
|
pPtr = stptok(command, token, 79, ":\0");
|
|
while ((pPtr = stptok(pPtr, token, 79, ":\0")) != NULL) {
|
|
if (start == 0) {
|
|
startDim[dim] = atoi(token);
|
|
start = 1;
|
|
} else {
|
|
endDim[dim] = atoi(token);
|
|
start = 0;
|
|
dim++;
|
|
}
|
|
}
|
|
|
|
if (dim < source->rank - 1) {
|
|
strlcpy(error,
|
|
"ERROR: Not enough border values specified for subsampling",
|
|
errLen);
|
|
return NULL;
|
|
}
|
|
for (i = 0; i < source->rank; i++) {
|
|
if (startDim[i] < 0 || startDim[i] >= source->dim[i]) {
|
|
snprintf(error, errLen,
|
|
"ERROR: invalid start value %d for dimension %d",
|
|
(int)startDim[1], i);
|
|
return NULL;
|
|
}
|
|
if (endDim[i] < startDim[i] || endDim[i] >= source->dim[i]) {
|
|
snprintf(error, errLen,
|
|
"ERROR: invalid end value %d for dimension %d", (int)endDim[1],
|
|
i);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return cutNXDataset(source, startDim, endDim);
|
|
}
|
|
|
|
/*-----------------------------------------------------------------------------*/
|
|
static pNXDS sumCommand(pNXDS source, char *command, char *error,
|
|
int errlen)
|
|
{
|
|
int dimNo = -1, start = -1, end = -1;
|
|
char *pPtr = NULL;
|
|
char token[80];
|
|
|
|
pPtr = stptok(command, token, 79, ":\0");
|
|
pPtr = stptok(pPtr, token, 79, ":\0");
|
|
if (pPtr != NULL) {
|
|
dimNo = atoi(token);
|
|
}
|
|
pPtr = stptok(pPtr, token, 79, ":\0");
|
|
if (pPtr != NULL) {
|
|
start = atoi(token);
|
|
}
|
|
pPtr = stptok(pPtr, token, 79, ":\0");
|
|
if (pPtr != NULL) {
|
|
end = atoi(token);
|
|
}
|
|
if (dimNo < 0 || dimNo > source->rank - 1) {
|
|
snprintf(error, errlen, "ERROR: invalid dimension %d requestd to sum",
|
|
dimNo);
|
|
return NULL;
|
|
}
|
|
if (end < 0 || end > source->dim[dimNo] || start < 0 || start > end) {
|
|
snprintf(error, errlen,
|
|
"ERROR: invalid summing limits %d to %d requested", start,
|
|
end);
|
|
return NULL;
|
|
}
|
|
|
|
return sumNXDataset(source, dimNo, start, end);
|
|
}
|
|
|
|
/*--------------------------------------------------------------------------*/
|
|
HistInt *subSample(pHMdata self, char *command, char *error, int errLen)
|
|
{
|
|
pNXDS source = NULL, start = NULL;
|
|
pNXDS result = NULL;
|
|
char *pPtr = NULL;
|
|
char subCommand[132];
|
|
HistInt *data = NULL;
|
|
int length;
|
|
|
|
|
|
start = hmDataToNXDataset(self);
|
|
if (start == NULL) {
|
|
strlcpy(error, "Out-Of-Memory or no data while subsampling ", errLen);
|
|
return NULL;
|
|
}
|
|
|
|
source = start;
|
|
pPtr = command;
|
|
while ((pPtr = stptok(pPtr, subCommand, 131, ";\0\r\n")) != NULL) {
|
|
if (strstr(subCommand, "sample") != NULL) {
|
|
result = subSampleCommand(source, subCommand, error, errLen);
|
|
} else if (strstr(subCommand, "sum") != NULL) {
|
|
result = sumCommand(source, subCommand, error, errLen);
|
|
} else {
|
|
strlcpy(error, "ERROR: invalid subcommand to process requested",
|
|
errLen);
|
|
return NULL;
|
|
}
|
|
if (result == NULL) {
|
|
return NULL;
|
|
}
|
|
if (source != start) {
|
|
dropNXDataset(source);
|
|
}
|
|
source = result;
|
|
}
|
|
length = getNXDatasetLength(result);
|
|
data = malloc((length + 1) * sizeof(int));
|
|
if (data == NULL) {
|
|
strlcpy(error, "Out-Of-Mmeory in Subsample", errLen);
|
|
dropNXDataset(result);
|
|
return NULL;
|
|
}
|
|
data[0] = length;
|
|
memcpy(data + 1, result->u.iPtr, length * sizeof(int));
|
|
dropNXDataset(result);
|
|
return data;
|
|
}
|