Files
sics/multicountersec.c

639 lines
18 KiB
C

/**
* This is a second generation multicounter. It coordinates counting multiple
* HM/CCD with the counter box. It also allows to override transfer such that
* such a counter can be used in a scan. As with the ordinary multicounter.
* This one is supposed to become the standard multicounter replacing the old
* multicounter and the hmcontrol module. The other difference is that the task
* mechanism will be used to keep track of counters.
*
* copyright: see file COPYRIGHT
*
* Mark Koennecke, October 2013
*/
#include <time.h>
#include <sics.h>
#include <counter.h>
#include <stptok.h>
#include <macro.h>
#include <status.h>
#include "sicshipadaba.h"
#define slaveSTARTING -100
/*---------------------------------------------------------------------------*/
typedef struct {
float fPreset;
float fCurrent;
char *pName;
} MonEvent, *pMonEvent;
/*-------------------------------------------------------------------------*/
static void SecCounterSetError(pCounter self, char *text)
{
hdbValue v;
pHdb node;
node = GetHipadabaNode(self->pDes->parNode, "error");
if(node != NULL){
v = MakeHdbText(strdup(text));
UpdateHipadabaPar(node,v,NULL);
ReleaseHdbValue(&v);
}
}
/*---------------------------------------------------------------------------------*/
static void doCountCommand(pHdb self, SConnection *pCon, int command)
{
pHdb master = NULL, slaves = NULL, status = NULL;
char *pPtr, name[80];
pICountable pCount = NULL;
void *data;
master = GetHipadabaNode(self,"master");
slaves = GetHipadabaNode(self,"slaves");
status = GetHipadabaNode(self,"status");
assert(master != NULL);
assert(slaves != NULL);
assert(status != NULL);
/*
treat master
*/
data = FindCommandData(pServ->pSics,master->value.v.text,NULL);
if(data != NULL){
pCount = GetCountableInterface(data);
if(pCount != NULL){
switch(command){
case 1001: /* stop */
pCount->Halt(data);
break;
case 1002: /*pause */
if(strcmp(status->value.v.text,"starting") == 0){
SCWrite(pCon,"WARNINg: cannot pause when not yet started", eWarning);
return;
} else {
pCount->Pause(data,pCon);
}
break;
case 1003: /* continue */
pCount->Continue(data,pCon);
break;
case 1011:
break;
default:
assert(0);
}
}
}
/*
treat slaves
*/
pPtr = slaves->value.v.text;
while((pPtr = stptok(pPtr,name,sizeof(name),",")) != NULL){
data = FindCommandData(pServ->pSics,name,NULL);
if(data != NULL){
pCount = GetCountableInterface(data);
if(pCount != NULL){
switch(command){
case 1001: /* stop */
case 1011:
pCount->Halt(data);
break;
case 1002: /*pause */
pCount->Pause(data,pCon);
break;
case 1003: /* continue */
pCount->Continue(data,pCon);
break;
default:
assert(0);
}
}
}
if(data == NULL || pCount == NULL){
SCPrintf(pCon,eLogError,"ERROR: slave counter %s NOT found or unusable", name);
}
}
}
/*---------------------------------------------------------------------------------*/
static void copyExponent(pHdb self, void *data)
{
pHdb source = NULL, target = NULL;
pCounter count = (pCounter)data;
source = GetHipadabaNode(self,"exponent");
target = GetHipadabaNode(count->pDes->parNode,"exponent");
if(source != NULL && target != NULL){
UpdateHipadabaPar(target,MakeHdbInt(source->value.v.intValue), NULL);
}
}
/*---------------------------------------------------------------------------------*/
static void startMultiCounting(pHdb self, SConnection *pCon)
{
pHdb mode = NULL, preset = NULL, slaves = NULL;
pHdb sID, mID;
char *pPtr, name[80];
pICountable pCount = NULL;
void *data;
long slaveID, masterID;
CounterMode eMode;
hdbValue v;
mode = GetHipadabaNode(self,"mode");
preset = GetHipadabaNode(self,"preset");
slaves = GetHipadabaNode(self,"slaves");
sID = GetHipadabaNode(self,"slaveID");
mID = GetHipadabaNode(self,"masterID");
assert(mode != NULL);
assert(preset != NULL);
assert(slaves != NULL);
assert(sID != NULL);
assert(mID != NULL);
strtolower(mode->value.v.text);
if(strcmp(mode->value.v.text,"timer") == 0) {
eMode = eTimer;
} else {
eMode = ePreset;
}
/*
start slaves
*/
slaveID = GetTaskGroupID(pServ->pTasker);
pPtr = slaves->value.v.text;
while((pPtr = stptok(pPtr,name,sizeof(name),",")) != NULL){
data = FindCommandData(pServ->pSics,name,NULL);
if(data != NULL){
pCount = GetCountableInterface(data);
if(pCount != NULL){
copyExponent(self,data);
pCount->SetCountParameters(data,preset->value.v.doubleValue,
eMode);
masterID = StartCountTask(data,pCon,name);
if(masterID < 0) {
SCPrintf(pCon,eLogError,"ERROR: failed to start slave %s, aborting", name);
doCountCommand(self,pCon,1001);
return;
} else {
AddTaskToGroup(pServ->pTasker,masterID, slaveID);
}
}
}
if(data == NULL || pCount == NULL){
SCPrintf(pCon,eLogError,"ERROR: slave counter %s NOT found or unusable", name);
}
}
v = MakeHdbInt(slaveID);
UpdateHipadabaPar(sID,v,pCon);
v = MakeHdbInt(slaveSTARTING);
UpdateHipadabaPar(mID,v,pCon);
}
/*---------------------------------------------------------------------------------*/
static int checkSlavesStarted(pCounter self, SConnection *pCon)
{
char *pPtr, name[80];
pHdb slaves = NULL, status = NULL, preset = NULL, master = NULL, mode = NULL, mID = NULL;
pDummy data = NULL;
pICountable pCount = NULL;
CounterMode eMode;
long masterID;
hdbValue v;
mID = GetHipadabaNode(self->objectNode,"masterID");
assert(mID != NULL);
if(mID->value.v.intValue != slaveSTARTING){
return 1;
}
if(SCGetInterrupt(pCon) != eContinue) {
doCountCommand(self->objectNode,pCon,1001);
mID->value.v.intValue = -1;
return 0;
}
slaves = GetHipadabaNode(self->objectNode,"slaves");
assert(slaves != NULL);
pPtr = slaves->value.v.text;
while((pPtr = stptok(pPtr,name,sizeof(name),",")) != NULL){
data = FindCommandData(pServ->pSics,name,NULL);
if(data != NULL){
status = GetHipadabaNode(data->pDescriptor->parNode,"status");
if(status != NULL){
if(strstr(status->value.v.text,"starting") != NULL){
return 0;
}
}
}
}
/*
when we are here all HM were started and we can start master
*/
mode = GetHipadabaNode(self->objectNode,"mode");
preset = GetHipadabaNode(self->objectNode,"preset");
master = GetHipadabaNode(self->objectNode,"master");
assert(master != NULL);
assert(mode != NULL);
assert(preset != NULL);
strtolower(mode->value.v.text);
if(strcmp(mode->value.v.text,"timer") == 0) {
eMode = eTimer;
} else {
eMode = ePreset;
}
data = FindCommandData(pServ->pSics,master->value.v.text,NULL);
if(data != NULL){
pCount = GetCountableInterface(data);
if(pCount != NULL){
copyExponent(self->objectNode,data);
pCount->SetCountParameters(data,preset->value.v.doubleValue,
eMode);
masterID = StartCountTask(data,pCon,master->value.v.text);
}
}
v = MakeHdbInt(masterID);
UpdateHipadabaPar(mID,v,pCon);
return 0;
}
/*---------------------------------------------------------------------------------*/
static hdbCallbackReturn MultiSecControllCallback(pHdb node,
void *userData,
pHdbMessage message)
{
pHdbDataMessage mm = NULL;
pHdb self = NULL;
int code;
SConnection *pCon = NULL;
mm = GetHdbSetMessage(message);
if(mm == NULL){
return hdbContinue;
}
code = (int)mm->v->v.doubleValue;
pCon = (SConnection *)mm->callData;
self = node->mama;
assert(self != NULL);
switch(code){
case 1000: /* start */
startMultiCounting(self, pCon);
break;
case 1001: /* stop */
doCountCommand(self,pCon,code);
break;
case 1002: /*pause */
doCountCommand(self,pCon,code);
break;
case 1003: /* continue */
doCountCommand(self,pCon,code);
break;
default:
if(pCon != NULL){
SCPrintf(pCon,eLogError,"ERROR: control code %d not recognised", code);
return hdbAbort;
}
}
return hdbContinue;
}
/*-------------------------------------------------------------------------2-*/
static int MultiSecTransfer(void *pData, SConnection * pCon)
{
int i, retVal = OKOK, tclStatus;
char pBueffel[132];
pCounter pCount = NULL;
pHdb transfer;
SConnection *myCon;
pCount = (pCounter) pData;
transfer = GetHipadabaNode(pCount->objectNode,"transfer");
if(transfer != NULL){
myCon = SCCopyConnection(pCon);
SCsetMacro(myCon,1);
MacroPush(myCon);
tclStatus = Tcl_Eval(InterpGetTcl(pServ->pSics), transfer->value.v.text);
if (tclStatus != TCL_OK) {
snprintf(pBueffel, 131, "ERROR: TransferScript returned: %s",
Tcl_GetStringResult(InterpGetTcl(pServ->pSics)));
SCWrite(pCon, pBueffel, eError);
MacroPop();
SCDeleteConnection(myCon);
return HWFault;
}
MacroPop();
SCDeleteConnection(myCon);
}
return retVal;
}
/*-------------------------------------------------------------------------------------*/
static int isMultiMasterRunning(pCounter self, SConnection *pCon, int *status)
{
pHdb mID, master, myStatus, control, ccd, stopTime, timeNode;
hdbValue v;
long mlID;
void *data;
pICountable pCount;
float controlVal, tVal;
MonEvent sMon;
mID = GetHipadabaNode(self->objectNode,"masterID");
master = GetHipadabaNode(self->objectNode,"master");
myStatus = GetHipadabaNode(self->objectNode,"status");
control = GetHipadabaNode(self->objectNode,"control");
ccd = GetHipadabaNode(self->objectNode,"ccd");
stopTime = GetHipadabaNode(self->objectNode,"stopTime");
timeNode = GetHipadabaNode(self->objectNode,"time");
assert(mID != NULL);
assert(master != NULL);
assert(myStatus != NULL);
assert(control != NULL);
assert(ccd != NULL);
assert(stopTime != NULL);
assert(timeNode != NULL);
mlID = mID->value.v.intValue;
if(mlID == 0) {
return 0;
}
data = FindCommandData(pServ->pSics,master->value.v.text,NULL);
assert(data != NULL);
pCount = GetCountableInterface(data);
assert(pCount != NULL);
if(isTaskIDRunning(pServ->pTasker,mlID)) {
*status = pCount->CheckCountStatus(data,pCon);
controlVal = GetControlValue((pCounter)data);
UpdateHipadabaPar(control,MakeHdbFloat(controlVal),pCon);
tVal = GetCountTime((pCounter)data,pCon);
UpdateHipadabaPar(timeNode,MakeHdbFloat(tVal),pCon);
SecCounterSetError(self,"None");
switch(*status){
case HWFault:
UpdateHipadabaPar(myStatus,MakeHdbText("error"),pCon);
UpdateHipadabaPar(stopTime,MakeHdbInt(time(NULL)),pCon);
SecCounterSetError(self,"Master counter errror");
*status = HWBusy;
break;
case HWPause:
UpdateHipadabaPar(myStatus,MakeHdbText("paused"),pCon);
*status = HWPause;
break;
case HWNoBeam:
UpdateHipadabaPar(myStatus,MakeHdbText("nobeam"),pCon);
*status = HWNoBeam;
break;
default:
*status = HWBusy;
UpdateHipadabaPar(myStatus,MakeHdbText("run"),pCon);
if (self->iCallbackCounter > 20) {
MultiSecTransfer(self,pCon);
sMon.fCurrent = controlVal;
sMon.fPreset = GetCounterPreset(self);
sMon.pName = self->name;
InvokeCallBack(self->pCall, MONITOR, &sMon);
self->iCallbackCounter = 0;
} else {
self->iCallbackCounter++;
}
break;
}
} else {
/*
we recently stopped. Mark it so and stop slaves.
*/
mID->value.v.intValue = 0;
*status = HWBusy;
UpdateHipadabaPar(myStatus,MakeHdbText("run"),pCon);
UpdateHipadabaPar(stopTime,MakeHdbInt(time(NULL)),pCon);
tVal = GetCountTime((pCounter)data,pCon);
UpdateHipadabaPar(timeNode,MakeHdbFloat(tVal),pCon);
if(ccd->value.v.intValue != 1) {
doCountCommand(self->objectNode,pCon,1011);
}
}
return 1;
}
/*-------------------------------------------------------------------------------------*/
static int areSlavesRunning(pCounter self, SConnection *pCon, int *status)
{
pHdb slaveID, myStatus, stopTime, ccd;
int i;
slaveID = GetHipadabaNode(self->objectNode,"slaveID");
myStatus = GetHipadabaNode(self->objectNode,"status");
stopTime = GetHipadabaNode(self->objectNode,"stopTime");
ccd = GetHipadabaNode(self->objectNode,"ccd");
assert(slaveID != NULL);
assert(myStatus != NULL);
assert(stopTime != NULL);
assert(ccd != NULL);
if(isTaskGroupRunning(pServ->pTasker,slaveID->value.v.intValue)){
if(ccd->value.v.intValue == 1 && time(NULL) > stopTime->value.v.intValue + 100) {
SCWrite(pCon,"WARNING: CCD overrun, restarting counting...", eLogError);
self->pCountInt->Halt(self);
ReleaseCountLock(self->pCountInt);
for(i = 0; i < 100; i++){
SicsWait(1);
if(!isTaskGroupRunning(pServ->pTasker,slaveID->value.v.intValue)){
self->pCountInt->StartCount(self,pCon);
UpdateHipadabaPar(myStatus,MakeHdbText("run"),pCon);
UpdateHipadabaPar(stopTime,MakeHdbInt(time(NULL)),pCon);
*status = HWBusy;
return 1;
}
}
SCWrite(pCon,"ERROR: failed to stop overrun CCD",eLogError);
*status = HWFault;
} else {
*status = HWBusy;
UpdateHipadabaPar(myStatus,MakeHdbText("run"),pCon);
return 1;
}
} else {
*status = HWIdle;
UpdateHipadabaPar(myStatus,MakeHdbText("idle"),pCon);
return 0;
}
return 1;
}
/*------------------------------------------------------------------------------------*/
static void multiEndCounting(pCounter self, SConnection *pCon)
{
InvokeCallBack(self->pCall, COUNTEND, NULL);
ReleaseCountLock(self->pCountInt);
MultiSecTransfer(self,pCon);
}
/*-------------------------------------------------------------------------------------*/
static int MultiSecStatus(void *pData, SConnection * pCon)
{
int status;
void *data;
pICountable pCount = NULL;
pHdb mID = NULL, sID = NULL, master;
pCounter self = (pCounter)pData;
if(!checkSlavesStarted(self,pCon)) {
return HWBusy;
}
if(isMultiMasterRunning(self,pCon, &status)){
return status;
}
if(areSlavesRunning(self,pCon, &status)){
return status;
}
multiEndCounting(self,pCon);
return HWIdle;
}
/*---------------------------------------------------------------------------
Forward unknown commands to the master counter
-----------------------------------------------------------------------------*/
static int MultiInvokeSICSOBJ(SConnection * pCon, SicsInterp * pSics, void *pData,
int argc, char *argv[])
{
int status;
char buffer[132];
pHdb master = NULL;
pCounter self = (pCounter)pData;
void *data = NULL;
CommandList *pCom;
status = InvokeSICSOBJ(pCon, pSics, pData, argc, argv);
if (status == -1) {
master = GetHipadabaNode(self->objectNode,"master");
assert(master != NULL);
pCom = FindCommand(pSics,master->value.v.text);
if(pCom != NULL){
status = pCom->OFunc(pCon,pSics,pCom->pData,argc,argv);
}
status = 0;
}
return status;
}
/*-------------------------------------------------------------------------------------*/
int MakeMultiSec(SConnection * pCon, SicsInterp * pSics, void *pData,
int argc, char *argv[])
{
pCounter pRes = NULL;
int status, length;
pHdb node, child;
if(argc < 3) {
SCWrite(pCon,"ERROR: need at least a name and length to create a counter",
eError);
return 0;
}
length = atoi(argv[2]);
pRes = CreateSecCounter(pCon,"SingleCounter", argv[1], length);
if(pRes == NULL){
return 0;
}
pRes->pCountInt->CheckCountStatus = MultiSecStatus;
pRes->pCountInt->TransferData = MultiSecTransfer;
node = pRes->objectNode;
child = GetHipadabaNode(node,"control");
AppendHipadabaCallback(child,MakeHipadabaCallback(MultiSecControllCallback,NULL,NULL));
child = MakeSICSHdbPar("master", usMugger, MakeHdbText(""));
if (child == NULL) {
SCWrite(pCon,"ERROR: out of memory in MakeMultiCounterSec",eError);
return 0;
} else {
SetHdbProperty(child, "__save", "true");
AddHipadabaChild(node, child, NULL);
}
child = MakeSICSHdbPar("transfer", usMugger, MakeHdbText(""));
if (child == NULL) {
SCWrite(pCon,"ERROR: out of memory in MakeMultiCounterSec",eError);
return 0;
} else {
SetHdbProperty(child, "__save", "true");
AddHipadabaChild(node, child, NULL);
}
child = MakeSICSHdbPar("error", usMugger, MakeHdbText("None"));
if (child == NULL) {
SCWrite(pCon,"ERROR: out of memory in MakeMultiCounterSec",eError);
return 0;
} else {
AddHipadabaChild(node, child, NULL);
}
child = MakeSICSHdbPar("slaves", usMugger, MakeHdbText(""));
if (child == NULL) {
SCWrite(pCon,"ERROR: out of memory in MakeMultiCounterSec",eError);
return 0;
} else {
SetHdbProperty(child, "__save", "true");
AddHipadabaChild(node, child, NULL);
}
child = MakeSICSHdbPar("ccd", usMugger, MakeHdbInt(0));
if (child == NULL) {
SCWrite(pCon,"ERROR: out of memory in MakeMultiCounterSec",eError);
return 0;
} else {
SetHdbProperty(child, "__save", "true");
AddHipadabaChild(node, child, NULL);
}
child = MakeSICSHdbPar("stopTime", usInternal, MakeHdbInt(0));
if (child == NULL) {
SCWrite(pCon,"ERROR: out of memory in MakeMultiCounterSec",eError);
return 0;
} else {
AddHipadabaChild(node, child, NULL);
}
child = MakeSICSHdbPar("masterID", usInternal, MakeHdbInt(0));
if (child == NULL) {
SCWrite(pCon,"ERROR: out of memory in MakeMultiCounterSec",eError);
return 0;
} else {
AddHipadabaChild(node, child, NULL);
}
child = MakeSICSHdbPar("slaveID", usInternal, MakeHdbInt(0));
if (child == NULL) {
SCWrite(pCon,"ERROR: out of memory in MakeMultiCounterSec",eError);
return 0;
} else {
AddHipadabaChild(node, child, NULL);
}
status =
AddCommand(pSics, argv[1], MultiInvokeSICSOBJ, DeleteCounter,
(void *) pRes);
if (status != 1) {
SCPrintf(pCon,eError, "ERROR: duplicate command %s not created", argv[1]);
return 0;
}
return 1;
}