/** * 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 * * extended to forward parameter setting requests to the single counter driver * * Mark Koennecke, February 2009 */ #include #include #include #include #include "multicounter.h" #include "counter.h" #include "HistMem.h" #include "macro.h" #include "splitter.h" #define MAXSLAVE 16 #define NOCOUNTERS -2727 /* checkSlaves codes */ #define WAITMASTER 100 #define WAITSLAVE 200 #define FINISHED 300 /*=============== code for the driver ======================================*/ typedef struct { void *slaveData[MAXSLAVE]; pICountable slaves[MAXSLAVE]; char *transferScript; int nSlaves; int checkSlaves; } 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]); ReleaseCountLock(self->slaves[i]); if (status != OKOK) retVal = status; } ReleaseCountLock(pCount->pCountInt); return retVal; } /*-------------------------------------------------------------------------*/ static int MMCCStart(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); if (!GetCountLock(pCount->pCountInt, pCon)) { return HWFault; } /* start slaves */ for (i = 1; i < self->nSlaves; i++) { ReleaseCountLock(self->slaves[i]); self->slaves[i]->SetCountParameters(self->slaveData[i], pCount->pDriv->fPreset, pCount->pDriv->eMode); status = self->slaves[i]->StartCount(self->slaveData[i], pCon); if (status != OKOK) { MMCCHalt(pData); ReleaseCountLock(pCount->pCountInt); return status; } } /* start master */ self->slaves[0]->SetCountParameters(self->slaveData[0], pCount->pDriv->fPreset, pCount->pDriv->eMode); status = self->slaves[0]->StartCount(self->slaveData[0], pCon); if (status != OKOK) { MMCCHalt(pData); ReleaseCountLock(pCount->pCountInt); return status; } pCount->isUpToDate = 0; pCount->tStart = time(NULL); InvokeCallBack(pCount->pCall, COUNTSTART, pCon); self->checkSlaves = WAITMASTER; return OKOK; } /*-------------------------------------------------------------------------*/ static int MMCCStatus(void *pData, SConnection * pCon) { int status = HWIdle, i; pCounter pCount = NULL, pMaster = NULL;; pMultiCounter self = NULL; pDummy pDum = NULL; pCount = (pCounter) pData; if (pCount != NULL) { self = (pMultiCounter) pCount->pDriv->pData; } assert(self); if (self->nSlaves == 0) { pCount->pDriv->iErrorCode = NOCOUNTERS; ReleaseCountLock(pCount->pCountInt); return HWFault; } if(self->checkSlaves == WAITMASTER) { status = self->slaves[0]->CheckCountStatus(self->slaveData[0], pCon); if (status == HWIdle || status == HWFault) { /* stop counting on slaves when finished or when an error occurred. */ MMCCHalt(pData); ReleaseCountLock(pCount->pCountInt); self->checkSlaves = WAITSLAVE; status = HWBusy; } pCount->pDriv->fLastCurrent = GetControlValue(self->slaveData[0]); } else if(self->checkSlaves == WAITSLAVE) { /* * wait for the detectors to report finish too. Otherwise, with the second * generation HM data may not be fully transferred. */ for(i = 1; i < self->nSlaves; i++){ status = self->slaves[i]->CheckCountStatus(self->slaveData[i], pCon); if(!(status == HWIdle || status == HWFault)){ return status; } } /* Warning: this assumes that slaves 1 - MAXSLAVE are histogram memories. If this assumption does not hold, change this code to check if this is really a histogram memory. */ for (i = 1; i < self->nSlaves; i++) { if (self->slaves[i] != NULL) { pDum = (pDummy)self->slaveData[i]; if (strcmp(pDum->pDescriptor->name, "HistMem") == 0) { HistDirty((pHistMem) self->slaveData[i]); } ReleaseCountLock(self->slaves[i]); } } status = HWIdle; InvokeCallBack(pCount->pCall, COUNTEND, pCon); self->checkSlaves = FINISHED; } else if(self->checkSlaves == FINISHED){ status = HWIdle; } 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); 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; pMultiCounter self = NULL; int tclStatus; pCount = (pCounter) pData; if (pCount != NULL) { self = (pMultiCounter) pCount->pDriv->pData; } assert(self); for (i = 0; i < self->nSlaves; i++) { status = self->slaves[i]->TransferData(self->slaveData[i], pCon); if (status != OKOK) { retVal = status; snprintf(pBueffel,sizeof(pBueffel)-1, "WARNING: slave histogram %d failed to transfer data", i); SCWrite(pCon, pBueffel, eWarning); } } 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 *pCount, char *name, int iCter, float fVal) { pDummy pDum; int i; pMultiCounter self = NULL; pCounter pCter; self = (pMultiCounter) pCount->pData; assert(self); for (i = 0; i < self->nSlaves; i++) { pDum = (pDummy)self->slaveData[i]; if(strcmp(pDum->pDescriptor->name, "SingleCounter") == 0){ pCter = (pCounter)self->slaveData[i]; if(pCter->pDriv != NULL){ return pCter->pDriv->Set(pCter->pDriv, name, iCter, fVal); } else { return 0; } } } return 0; } /*-----------------------------------------------------------------------*/ static int MultiCounterGet(struct __COUNTER *pCount, char *name, int iCter, float *fVal) { pDummy pDum; int i; pMultiCounter self = NULL; pCounter pCter; pHdb node; hdbValue v; self = (pMultiCounter) pCount->pData; assert(self); for (i = 0; i < self->nSlaves; i++) { pDum = (pDummy)self->slaveData[i]; if(strcmp(pDum->pDescriptor->name, "SingleCounter") == 0){ pCter = (pCounter)self->slaveData[i]; if(pCter->pDriv != NULL){ return pCter->pDriv->Get(pCter->pDriv, name, iCter, fVal); } else { return 0; } } } return 0; } /*-----------------------------------------------------------------------*/ static int MultiCounterSend(struct __COUNTER *pCount, char *pText, char *reply, int replylen) { pDummy pDum; int i; pMultiCounter self = NULL; pCounter pCter; self = (pMultiCounter) pCount->pData; assert(self); for (i = 0; i < self->nSlaves; i++) { pDum = (pDummy)self->slaveData[i]; if(strcmp(pDum->pDescriptor->name, "SingleCounter") == 0){ pCter = (pCounter)self->slaveData[i]; return pCter->pDriv->Send(pCter->pDriv,pText, reply, replylen); } } return 0; } /*---------------------------------------------------------------------*/ static int MultiCounterError(struct __COUNTER *pDriv, int *iCode, char *error, int errlen) { if (pDriv->iErrorCode == NOCOUNTERS) { strlcpy(error, "NO counters configured!", errlen); } else { strlcpy(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; pDriv->iNoOfMonitors = MAXCOUNT; 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 loop through the remaining arguments, thereby entering them into the slave list. */ for (i = 2; i < argc; i++) { pCom = FindCommand(pSics, argv[i]); if (!pCom) { snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: object %s not found in MakeMultiCounter", argv[i]); SCWrite(pCon, pBueffel, eError); continue; } pCount = GetCountableInterface(pCom->pData); if (!pCount) { snprintf(pBueffel,sizeof(pBueffel)-1, "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++; } self->checkSlaves = FINISHED; /* now install our action command and we are done */ status = AddCommand(pSics, argv[1], MultiCounterAction, DeleteCounter, pNew); if (!status) { snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: duplicate command %s not created", argv[1]); SCWrite(pCon, pBueffel, eError); DeleteCounter(pNew); return 0; } return 1; }