/** * 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 /*=============== 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]); ReleaseCountLock(self->slaves[i]); if (status != OKOK) retVal = status; } ReleaseCountLock(pCount->pCountInt); 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); if (!GetCountLock(pCount->pCountInt, pCon)) { return HWFault; } 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++) { ReleaseCountLock(self->slaves[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); ReleaseCountLock(pCount->pCountInt); 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, pMaster = 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; ReleaseCountLock(pCount->pCountInt); return HWFault; } status = self->slaves[0]->CheckCountStatus(self->slaveData[0], pCon); pMaster = (pCounter)self->slaveData[0]; pCount->pDriv->fLastCurrent = GetControlValue(pMaster); 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]); } ReleaseCountLock(self->slaves[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; snprintf(pBueffel,sizeof(pBueffel)-1, "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 *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; /* now loop through the remaining arguments, thereby entering them into the slave list. */ self->nSlaves = 0; 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++; } 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) { snprintf(pBueffel,sizeof(pBueffel)-1, "ERROR: duplicate command %s not created", argv[1]); SCWrite(pCon, pBueffel, eError); DeleteCounter(pNew); return 0; } return 1; }