- Added a drivable adapter to scriptcontext nodes - Added subsampling to simulated histograms (and as a general option) in order to support Gumtree testing.
641 lines
17 KiB
C
641 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;
|
|
self->updateFlag = 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);
|
|
}
|
|
memset(self->localBuffer,0,size*sizeof(HistInt));
|
|
}
|
|
/*----------------------------------------------------------------------*/
|
|
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->updateFlag = 1;
|
|
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++){
|
|
sprintf(pValue,"dim%1.1d",i);
|
|
status = StringDictGetAsNumber(pOpt,pValue,&fVal);
|
|
if(!status){
|
|
sprintf(pValue,"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;
|
|
}
|
|
|
|
/*
|
|
note: remove update request in histmem.c
|
|
*/
|
|
if(self->updateIntervall > 0){
|
|
/*
|
|
we do buffer
|
|
*/
|
|
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->updateFlag = 1;
|
|
}
|
|
/*--------------------------------------------------------------------
|
|
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(self->updateFlag == 1 && 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;
|
|
self->updateFlag = 0;
|
|
break;
|
|
} else{
|
|
status = hist->pDriv->GetError(hist->pDriv,&iErr,pError,79);
|
|
sprintf(pBueffel,"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]) ) {
|
|
sprintf(pBueffel,"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]) ){
|
|
sprintf(pBueffel,"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:
|
|
sprintf(pBueffel,
|
|
"ERROR: summing in %d dimensions not yet implemented",
|
|
myrank);
|
|
SCWrite(pCon,pBueffel,eError);
|
|
return -1;
|
|
break;
|
|
}
|
|
if(lSum < 0){
|
|
lSum = -lSum;
|
|
}
|
|
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(int));
|
|
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){
|
|
int 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){
|
|
strncpy(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", 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", 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){
|
|
strncpy(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 {
|
|
strncpy(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){
|
|
strncpy(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;
|
|
}
|