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
523 lines
14 KiB
C
523 lines
14 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
|
|
/*=============== 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;
|
|
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;
|
|
}
|
|
|
|
for (i = 0; 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;
|
|
}
|
|
}
|
|
pCount->isUpToDate = 0;
|
|
pCount->tStart = time(NULL);
|
|
InvokeCallBack(pCount->pCall, COUNTSTART, pCon);
|
|
return OKOK;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static int MMCCStatus(void *pData, SConnection * pCon)
|
|
{
|
|
int status, 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;
|
|
}
|
|
|
|
status = self->slaves[0]->CheckCountStatus(self->slaveData[0], pCon);
|
|
pMaster = (pCounter)self->slaveData[0];
|
|
pCount->pDriv->fLastCurrent = GetControlValue(pMaster);
|
|
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);
|
|
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++;
|
|
}
|
|
|
|
/*
|
|
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;
|
|
}
|