Files
sics/multicounter.c
koennecke f2aa76b396 Fixed a wrong sequence of starts in multicounter
Suppressed excessive connection messages from asynnet
2016-11-10 14:41:38 +01:00

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;
}