578 lines
15 KiB
C
578 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
|
|
*
|
|
* extended to forward parameter setting requests to the single counter driver
|
|
*
|
|
* Mark Koennecke, February 2009
|
|
*/
|
|
#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
|
|
|
|
/*
|
|
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;
|
|
}
|