unless one of the other counters is in fault, nobeam or paused. In timer mode the multicounter should stop when all the counters are idle unless one of the other counters is in fault, nobeam or paused. r3627 | ffr | 2012-07-06 07:49:07 +1000 (Fri, 06 Jul 2012) | 5 lines
501 lines
15 KiB
C
501 lines
15 KiB
C
/**
|
|
* The MultiCounter is another counter which coordinates multiple
|
|
* counting objects, counters and histogram memories. It also calls a
|
|
* script function after TransferData which collects counters and monitors.
|
|
* The purpose is to have a flexible counter abstraction for upper level
|
|
* code such as maximizers and scan functions. The script can deal with
|
|
* counting on monitors or on sums of histogram memories.
|
|
*
|
|
* This is a bit unclean. The counter driver is of no use, therefore its
|
|
* private data structure is used to hold the other counters and the name
|
|
* of the script. It would have been better to inherit from counter but
|
|
* that would have required lost of type casts. I am to lazy for this.
|
|
*
|
|
* copyright: see file COPYRIGHT
|
|
*
|
|
* Mark Koennecke, September 2006
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <time.h>
|
|
#include <tcl.h>
|
|
#include "multicounter.h"
|
|
#include "counter.h"
|
|
#include "HistMem.h"
|
|
#include "macro.h"
|
|
#include "splitter.h"
|
|
|
|
#define MAXSLAVE 16
|
|
#define NOCOUNTERS -2727
|
|
/*=============== code for the driver ======================================*/
|
|
typedef struct {
|
|
void *slaveData[MAXSLAVE];
|
|
pICountable slaves[MAXSLAVE];
|
|
char *transferScript;
|
|
int nSlaves;
|
|
}MultiCounter, *pMultiCounter;
|
|
/*--------------------------------------------------------------------------*/
|
|
static void KillMultiDriver(struct __COUNTER *data){
|
|
pMultiCounter self = (pMultiCounter)data->pData;
|
|
if(self == NULL){
|
|
return;
|
|
}
|
|
if(self->transferScript != NULL){
|
|
free(self->transferScript);
|
|
}
|
|
free(self);
|
|
}
|
|
/*============== countable interface functions ============================*/
|
|
static int MMCCHalt(void *pData){
|
|
int i, retVal = OKOK, status;
|
|
pCounter pCount = NULL;
|
|
pMultiCounter self = NULL;
|
|
|
|
pCount = (pCounter)pData;
|
|
if(pCount != NULL){
|
|
self = (pMultiCounter)pCount->pDriv->pData;
|
|
}
|
|
assert(self);
|
|
|
|
for(i = 0; i < self->nSlaves; i++){
|
|
status = self->slaves[i]->Halt(self->slaveData[i]);
|
|
if(status != OKOK)
|
|
retVal = status;
|
|
}
|
|
return retVal;
|
|
}
|
|
/*-------------------------------------------------------------------------*/
|
|
static int MMCCStart(void *pData, SConnection *pCon)
|
|
{
|
|
int i, status, controlMonitor;
|
|
int slavePreset, oneYear=32000000;
|
|
pCounter pCount = NULL;
|
|
pMultiCounter self = NULL;
|
|
char buffer[128];
|
|
CounterMode slaveMode;
|
|
|
|
pCount = (pCounter)pData;
|
|
if(pCount != NULL){
|
|
self = (pMultiCounter)pCount->pDriv->pData;
|
|
}
|
|
assert(self);
|
|
controlMonitor = GetControlMonitor((pCounter)pCount);
|
|
if (pCount->pDriv->eMode == ePreset) {
|
|
slaveMode = eTimer;
|
|
slavePreset = oneYear;
|
|
} else {
|
|
slaveMode = pCount->pDriv->eMode;
|
|
slavePreset = pCount->pDriv->fPreset;
|
|
}
|
|
for(i = 0; i < self->nSlaves; i++){
|
|
if (i == controlMonitor) {
|
|
self->slaves[i]->SetCountParameters(self->slaveData[i],
|
|
pCount->pDriv->fPreset, pCount->pDriv->eMode);
|
|
} else {
|
|
self->slaves[i]->SetCountParameters(self->slaveData[i],
|
|
slavePreset, slaveMode);
|
|
}
|
|
status = self->slaves[i]->StartCount(self->slaveData[i],pCon);
|
|
if(status != OKOK){
|
|
MMCCHalt(pData);
|
|
return status;
|
|
}
|
|
}
|
|
pCount->isUpToDate = 0;
|
|
pCount->tStart = time(NULL);
|
|
InvokeCallBack(pCount->pCall,COUNTSTART,pCon);
|
|
return OKOK;
|
|
}
|
|
/*-------------------------------------------------------------------------*/
|
|
static int MMCCStatus(void *pData, SConnection *pCon){
|
|
int status, ctrStatus, i, controlMonitor;
|
|
pCounter pCountController = NULL, pSlaveCounter = NULL;
|
|
pCounter pCount = NULL;
|
|
pMultiCounter self = NULL;
|
|
pDummy pDum = NULL;
|
|
enum {
|
|
eIdle,
|
|
eBusy,
|
|
ePause,
|
|
eNoBeam,
|
|
eFault
|
|
} statusLevel;
|
|
|
|
pCount = (pCounter)pData;
|
|
if(pCount != NULL){
|
|
self = (pMultiCounter)pCount->pDriv->pData;
|
|
}
|
|
assert(self);
|
|
|
|
if(self->nSlaves == 0) {
|
|
pCount->pDriv->iErrorCode = NOCOUNTERS;
|
|
return HWFault;
|
|
}
|
|
|
|
controlMonitor = GetControlMonitor((pCounter)pCount);
|
|
pCountController = (pCounter)self->slaveData[controlMonitor];
|
|
|
|
/* counter states = HWIdle, HWBusy, HWPause, HWNoBeam, HWFault */
|
|
status = HWIdle;
|
|
statusLevel = eIdle;
|
|
for (i = 0; i < self->nSlaves; i++) {
|
|
pSlaveCounter = (pCounter)self->slaveData[i];
|
|
ctrStatus = self->slaves[i]->CheckCountStatus(pSlaveCounter,pCon);
|
|
if (statusLevel >= eFault)
|
|
continue;
|
|
switch (ctrStatus) {
|
|
case HWFault:
|
|
statusLevel = eFault;
|
|
status = HWFault;
|
|
break;
|
|
case HWNoBeam:
|
|
if (statusLevel < eNoBeam)
|
|
statusLevel = eNoBeam;
|
|
status = HWNoBeam;
|
|
break;
|
|
case HWPause:
|
|
if (statusLevel < ePause)
|
|
statusLevel = ePause;
|
|
status = HWPause;
|
|
break;
|
|
default:
|
|
if ( pCountController->pDriv->eMode == ePreset && i == controlMonitor && ctrStatus == HWIdle) {
|
|
statusLevel = eBusy; /* Allow transition to HWPause or higher */
|
|
status = HWIdle;
|
|
} else if (statusLevel < eBusy && ctrStatus != HWIdle) {
|
|
/* ffr: We expect !HWIdle means HWBusy, if not the existing code should handle the exception */
|
|
statusLevel = eBusy;
|
|
status = ctrStatus;
|
|
}
|
|
}
|
|
}
|
|
if(status == HWIdle || status == HWFault){
|
|
/*
|
|
stop counting on slaves when finished or when an error
|
|
occurred.
|
|
*/
|
|
InvokeCallBack(pCount->pCall,COUNTEND,pCon);
|
|
MMCCHalt(pCount);
|
|
}
|
|
for(i = 1; i < MAXSLAVE; i++){
|
|
if(self->slaves[i] != NULL){
|
|
pDum = (pDummy)self->slaveData[i];
|
|
if(strcmp(pDum->pDescriptor->name,"HistMem") == 0){
|
|
HistDirty((pHistMem)self->slaveData[i]);
|
|
}
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
/*-------------------------------------------------------------------------*/
|
|
static int MMCCPause(void *pData, SConnection *pCon){
|
|
int i, status;
|
|
pCounter pCount = NULL;
|
|
pMultiCounter self = NULL;
|
|
|
|
pCount = (pCounter)pData;
|
|
if(pCount != NULL){
|
|
self = (pMultiCounter)pCount->pDriv->pData;
|
|
}
|
|
assert(self);
|
|
|
|
for(i = 0; i < self->nSlaves; i++){
|
|
status = self->slaves[i]->Pause(self->slaveData[i],pCon);
|
|
if(status != OKOK){
|
|
MMCCHalt(pCount);
|
|
return status;
|
|
}
|
|
}
|
|
return OKOK;
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static int MMCCContinue(void *pData, SConnection *pCon){
|
|
int i, status;
|
|
pCounter pCount = NULL;
|
|
pMultiCounter self = NULL;
|
|
|
|
pCount = (pCounter)pData;
|
|
if(pCount != NULL){
|
|
self = (pMultiCounter)pCount->pDriv->pData;
|
|
}
|
|
assert(self);
|
|
|
|
for(i = 0; i < self->nSlaves; i++){
|
|
status = self->slaves[i]->Continue(self->slaveData[i],pCon);
|
|
if(status != OKOK){
|
|
MMCCHalt(pCount);
|
|
return status;
|
|
}
|
|
}
|
|
return OKOK;
|
|
}
|
|
/*------------------------------------------------------------------------*/
|
|
static char *getNextMMCCNumber(char *pStart, char pNumber[80]){
|
|
int charCount = 0;
|
|
pNumber[0] = '\0';
|
|
|
|
/* advance to first digit */
|
|
while(isspace(*pStart) && *pStart != '\0'){
|
|
pStart++;
|
|
}
|
|
if(*pStart == '\0'){
|
|
return NULL;
|
|
}
|
|
|
|
/* copy */
|
|
while(!isspace(*pStart) && *pStart != '\0' && charCount < 78){
|
|
pNumber[charCount] = *pStart;
|
|
pStart++;
|
|
charCount++;
|
|
}
|
|
pNumber[charCount] = '\0';
|
|
return pStart;
|
|
}
|
|
/*-------------------------------------------------------------------------*/
|
|
static void loadCountData(pCounter pCount, const char *data){
|
|
char *pPtr = NULL;
|
|
char pNumber[80];
|
|
int i = 0;
|
|
|
|
pPtr = (char *)data;
|
|
pPtr = getNextMMCCNumber(pPtr,pNumber);
|
|
// SICS-195 get time from controlling monitor
|
|
// pCount->pDriv->fTime = atof(pNumber);
|
|
while(pPtr != NULL && i < MAXCOUNT){
|
|
pPtr = getNextMMCCNumber(pPtr,pNumber);
|
|
pCount->pDriv->lCounts[i] = atoi(pNumber);
|
|
i++;
|
|
}
|
|
}
|
|
/*--------------------------------------------------------------------------*/
|
|
static int MMCCTransfer(void *pData, SConnection *pCon){
|
|
int i, retVal = OKOK, status;
|
|
char pBueffel[132];
|
|
pCounter pCount = NULL;
|
|
pCounter pCountController = NULL, pCountSlave;
|
|
pMultiCounter self = NULL;
|
|
int tclStatus;
|
|
int controlMonitor;
|
|
double avCntRt;
|
|
|
|
pCount = (pCounter)pData;
|
|
if(pCount != NULL){
|
|
self = (pMultiCounter)pCount->pDriv->pData;
|
|
}
|
|
assert(self);
|
|
controlMonitor = GetControlMonitor(pCount);
|
|
pCountController = (pCounter)self->slaveData[controlMonitor];
|
|
|
|
for(i = 0; i < self->nSlaves; i++){
|
|
status = self->slaves[i]->TransferData(self->slaveData[i], pCon);
|
|
if(status != OKOK){
|
|
retVal = status;
|
|
sprintf(pBueffel,"WARNING: slave histogram %d failed to transfer data",
|
|
i);
|
|
SCWrite(pCon,pBueffel,eWarning);
|
|
} else if (pCountController->pDriv->eMode == ePreset) {
|
|
if (i != controlMonitor) {
|
|
pCountSlave = (pCounter) self->slaveData[i];
|
|
avCntRt = pCountSlave->pDriv->lCounts[0] / pCountSlave->pDriv->fTime;
|
|
pCountSlave->pDriv->lCounts[0] = avCntRt * pCountController->pDriv->fTime;
|
|
pCountSlave->pDriv->fTime = pCountController->pDriv->fTime;
|
|
}
|
|
}
|
|
}
|
|
pCount->pDriv->fTime = pCountController->pDriv->fTime;
|
|
if(self->transferScript != NULL){
|
|
MacroPush(pCon);
|
|
tclStatus = Tcl_Eval(InterpGetTcl(pServ->pSics), self->transferScript);
|
|
if(tclStatus != TCL_OK){
|
|
snprintf(pBueffel,131,"ERROR: TransferScript returned: %s",
|
|
Tcl_GetStringResult(InterpGetTcl(pServ->pSics)));
|
|
SCWrite(pCon,pBueffel,eError);
|
|
MacroPop();
|
|
return HWFault;
|
|
}
|
|
MacroPop();
|
|
loadCountData(pCount,Tcl_GetStringResult(InterpGetTcl(pServ->pSics)));
|
|
}
|
|
return retVal;
|
|
}
|
|
/*-------------------------------------------------------------------------*/
|
|
static void MMCCParameter(void *pData, float fPreset, CounterMode eMode ){
|
|
int i;
|
|
pCounter pCount = NULL;
|
|
pMultiCounter self = NULL;
|
|
|
|
pCount = (pCounter)pData;
|
|
if(pCount != NULL){
|
|
self = (pMultiCounter)pCount->pDriv->pData;
|
|
}
|
|
assert(self);
|
|
|
|
for(i = 0; i < self->nSlaves; i++){
|
|
self->slaves[i]->SetCountParameters(self->slaveData[i], fPreset,
|
|
eMode);
|
|
}
|
|
}
|
|
/*======================= Driver Interface ==============================*/
|
|
static int MultiCounterSet(struct __COUNTER *self, char *name,
|
|
int iCter, float fVal){
|
|
|
|
return 0;
|
|
}
|
|
/*-----------------------------------------------------------------------*/
|
|
static int MultiCounterGet(struct __COUNTER *self, char *name,
|
|
int iCter, float *fVal){
|
|
|
|
return 0;
|
|
}
|
|
/*-----------------------------------------------------------------------*/
|
|
static int MultiCounterSend(struct __COUNTER *self, char *pText,
|
|
char *reply, int replylen){
|
|
|
|
strncpy(reply,"NOT Implemented",replylen);
|
|
return 0;
|
|
}
|
|
/*---------------------------------------------------------------------*/
|
|
static int MultiCounterError(struct __COUNTER *pDriv, int *iCode,
|
|
char *error, int errlen){
|
|
|
|
|
|
if(pDriv->iErrorCode == NOCOUNTERS){
|
|
strncpy(error,"NO counters configured!",errlen);
|
|
} else {
|
|
strncpy(error,"Not Implemented", errlen);
|
|
}
|
|
return COTERM;
|
|
}
|
|
/*----------------------------------------------------------------------*/
|
|
static int MultiCounterFix(struct __COUNTER *self, int iCode){
|
|
return COTERM;
|
|
}
|
|
/*=============== Interpreter Interface ================================ */
|
|
int MultiCounterAction(SConnection *pCon, SicsInterp *pSics,
|
|
void *pData, int argc, char *argv[]){
|
|
pMultiCounter self = NULL;
|
|
pCounter pCount = NULL;
|
|
char buffer[256];
|
|
|
|
if(argc > 1){
|
|
strtolower(argv[1]);
|
|
if(strcmp(argv[1],"transferscript") == 0){
|
|
pCount = (pCounter)pData;
|
|
self = (pMultiCounter)pCount->pDriv->pData;
|
|
if(argc < 3){
|
|
SCPrintf(pCon,eValue,"%s.transferscript = %s",
|
|
argv[0],self->transferScript);
|
|
return 1;
|
|
} else {
|
|
if(!SCMatchRights(pCon,usUser)){
|
|
return 0;
|
|
}
|
|
if(self->transferScript != NULL){
|
|
free(self->transferScript);
|
|
}
|
|
Arg2Text(argc-2,&argv[2],buffer,255);
|
|
self->transferScript = strdup(buffer);
|
|
SCSendOK(pCon);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return CountAction(pCon,pSics,pData,argc,argv);
|
|
}
|
|
/*------------------------------------------------------------------------*/
|
|
int MakeMultiCounter(SConnection *pCon, SicsInterp *pSics,
|
|
void *pData, int argc, char *argv[]){
|
|
int i, status;
|
|
pCounter pNew = NULL;
|
|
char pBueffel[132];
|
|
CommandList *pCom;
|
|
pICountable pCount;
|
|
pMultiCounter self = NULL;
|
|
pCounterDriver pDriv = NULL;
|
|
|
|
/*
|
|
need at least two parameters
|
|
*/
|
|
if(argc < 3){
|
|
SCWrite(pCon,"ERROR: insufficient number of arguments to MakeMultiCounter",
|
|
eError);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
allocate our data structure
|
|
*/
|
|
self = malloc(sizeof(MultiCounter));
|
|
pDriv = malloc(sizeof(CounterDriver));
|
|
if(self == NULL || pDriv == NULL){
|
|
SCWrite(pCon,"ERROR: out of memory in MakeMultiCounter",eError);
|
|
return 0;
|
|
}
|
|
memset(self,0,sizeof(MultiCounter));
|
|
memset(pDriv,0,sizeof(CounterDriver));
|
|
pDriv->pData = self;
|
|
pDriv->KillPrivate = KillMultiDriver;
|
|
|
|
/*
|
|
now loop through the remaining arguments, thereby entering them into
|
|
the slave list.
|
|
*/
|
|
for(i = 2, self->nSlaves = 0; i < argc; i++){
|
|
pCom = FindCommand(pSics,argv[i]);
|
|
if(!pCom){
|
|
sprintf(pBueffel,"ERROR: object %s not found in MakeMultiCounter",
|
|
argv[i]);
|
|
SCWrite(pCon,pBueffel,eError);
|
|
continue;
|
|
}
|
|
pCount = GetCountableInterface(pCom->pData);
|
|
if(!pCount){
|
|
sprintf(pBueffel,"ERROR: object %s is NOT countable",
|
|
argv[i]);
|
|
SCWrite(pCon,pBueffel,eError);
|
|
continue;
|
|
}
|
|
self->slaves[self->nSlaves] = pCount;
|
|
self->slaveData[self->nSlaves] = pCom->pData;
|
|
self->nSlaves++;
|
|
}
|
|
pDriv->iNoOfMonitors = self->nSlaves;
|
|
pNew = CreateCounter(argv[1],pDriv);
|
|
if(pNew == NULL){
|
|
SCWrite(pCon,"ERROR: out of memory in MakeMultiCounter",eError);
|
|
return 0;
|
|
}
|
|
pDriv->Get = MultiCounterGet;
|
|
pDriv->GetError = MultiCounterError;
|
|
pDriv->TryAndFixIt = MultiCounterFix;
|
|
pDriv->Set = MultiCounterSet;
|
|
pDriv->Send
|
|
= MultiCounterSend;
|
|
|
|
/*
|
|
assign interface functions
|
|
*/
|
|
pNew->pCountInt->Halt = MMCCHalt;
|
|
pNew->pCountInt->StartCount = MMCCStart;
|
|
pNew->pCountInt->CheckCountStatus = MMCCStatus;
|
|
pNew->pCountInt->Pause = MMCCPause;
|
|
pNew->pCountInt->Continue = MMCCContinue;
|
|
pNew->pCountInt->TransferData = MMCCTransfer;
|
|
pNew->pCountInt->SetCountParameters = MMCCParameter;
|
|
|
|
/*
|
|
now install our action command and we are done
|
|
*/
|
|
status = AddCommand(pSics,argv[1],MultiCounterAction,DeleteCounter,
|
|
pNew);
|
|
if(!status){
|
|
sprintf(pBueffel,"ERROR: duplicate command %s not created",argv[1]);
|
|
SCWrite(pCon,pBueffel,eError);
|
|
DeleteCounter(pNew);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|