/** * 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 #include #include #include #include #include #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; }