/*---------------------------------------------------------------------------- McStas simulation to SICS controller module implementation file. For more details see mcstas.tex. copyright: see file COPYRIGHT Mark Koennecke, June 2005 -----------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include "stringdict.h" #include "mccontrol.h" /*========================= life and death ==================================*/ static void KillMcStasController(void *pData){ pMcStasController self = (pMcStasController)pData; if(self == NULL){ return; } if(self->pDes != NULL){ DeleteDescriptor(self->pDes); } if(self->scripts != NULL){ DeleteStringDict(self->scripts); } free(self); } /*---------------------------------------------------------------------------*/ int McStasControllerFactory(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]){ pMcStasController pNew = NULL; pNew = (pMcStasController)malloc(sizeof(McStasController)); if(pNew == NULL){ SCWrite(pCon,"ERROR: out of memory creating McStasController", eError); return 0; } memset(pNew,0,sizeof(McStasController)); pNew->pDes = CreateDescriptor("McStasController"); pNew->scripts = CreateStringDict(); if(pNew->pDes == NULL || pNew->scripts == NULL){ SCWrite(pCon,"ERROR: out of memory creating McStasController", eError); free(pNew); return 0; } StringDictAddPair(pNew->scripts,"mcstart","UNDEFINED"); StringDictAddPair(pNew->scripts,"mcisrunning","UNDEFINED"); StringDictAddPair(pNew->scripts,"mcdump","UNDEFINED"); StringDictAddPair(pNew->scripts,"mckill","UNDEFINED"); StringDictAddPair(pNew->scripts,"mccopydata","UNDEFINED"); StringDictAddPair(pNew->scripts,"mcmonfile","UNDEFINED"); pNew->pid = -1; pNew->monitorScale = 1; return AddCommand(pSics,"mccontrol", McStasControllerWrapper, KillMcStasController, pNew); } /*====================== interpreter interface ============================*/ static int configureController(pMcStasController self, SConnection *pCon, int argc, char *argv[]){ char pBueffel[256]; if(argc < 4){ SCWrite(pCon, "ERRROR: insufficient number of arguments to mccontrol configure", eError); return 0; } strtolower(argv[2]); if(strcmp(argv[2],"update") == 0){ self->updateIntervall = atoi(argv[3]); SCSendOK(pCon); return 1; } if(strcmp(argv[2],"monitorscale") == 0){ self->monitorScale = atof(argv[3]); if(self->monitorScale <= 0){ SCWrite(pCon,"ERROR: invalid monitor scale",eError); self->monitorScale = 1; return 0; } SCSendOK(pCon); return 1; } if(!StringDictExists(self->scripts,argv[2])){ snprintf(pBueffel,255,"ERROR: scriptkey %s does not exist",argv[2]); SCWrite(pCon,pBueffel,eError); return 0; } StringDictUpdate(self->scripts,argv[2],argv[3]); SCSendOK(pCon); return 1; } /*------------------------------------------------------------------------*/ static void listConfiguration(pMcStasController self, SConnection *pCon){ Tcl_DString txt; char pLine[256]; char pScript[131]; Tcl_DStringInit(&txt); StringDictGet(self->scripts,"mcstart",pScript,131); snprintf(pLine,255,"mccontrol.mcstart = %s\n",pScript); Tcl_DStringAppend(&txt,pLine,-1); StringDictGet(self->scripts,"mcisrunning",pScript,131); snprintf(pLine,255,"mccontrol.mcisrunning = %s\n",pScript); Tcl_DStringAppend(&txt,pLine,-1); StringDictGet(self->scripts,"mcdump",pScript,131); snprintf(pLine,255,"mccontrol.mcdump = %s\n",pScript); Tcl_DStringAppend(&txt,pLine,-1); StringDictGet(self->scripts,"mckill",pScript,131); snprintf(pLine,255,"mccontrol.mckill = %s\n",pScript); Tcl_DStringAppend(&txt,pLine,-1); StringDictGet(self->scripts,"mccopydata",pScript,131); snprintf(pLine,255,"mccontrol.mccopydata = %s\n",pScript); Tcl_DStringAppend(&txt,pLine,-1); StringDictGet(self->scripts,"mcmonfile",pScript,131); snprintf(pLine,255,"mccontrol.mcmonfile = %s\n",pScript); Tcl_DStringAppend(&txt,pLine,-1); snprintf(pLine,255,"mccontrol.updateintervall = %d\n", self->updateIntervall); Tcl_DStringAppend(&txt,pLine,-1); snprintf(pLine,255,"mccontrol.monitorscale = %f\n", self->monitorScale); Tcl_DStringAppend(&txt,pLine,-1); snprintf(pLine,255,"mccontrol.pid = %d", self->pid); Tcl_DStringAppend(&txt,pLine,-1); SCWrite(pCon,Tcl_DStringValue(&txt),eValue); Tcl_DStringFree(&txt); } /*--------------------------------------------------------------------------*/ static int invokeScript(pMcStasController self, char *name, SicsInterp *pSics, char *result, int resultLen){ char pCommand[256], pScript[132], pMode[10]; Tcl_Interp *pTcl; int status; if(!StringDictGet(self->scripts,name,pScript,131)){ strncpy(result,"ERROR: script not found",resultLen); return 0; } if(strcmp(name,"mccopydata") == 0){ snprintf(pCommand,255,"%s",pScript); }else if(strcmp(name,"mcstart") == 0){ if(self->mode == eTimer){ strcpy(pMode,"timer"); }else { strcpy(pMode,"monitor"); } snprintf(pCommand,255,"%s %s %f", pScript,pMode,self->fPreset); } else { snprintf(pCommand,255,"%s %d",pScript,self->pid); } pTcl = InterpGetTcl(pSics); status = Tcl_Eval(pTcl,pCommand); strncpy(result, pTcl->result, resultLen); if(status == TCL_OK){ return 1; } else { return 0; } } /*------------------------------------------------------------------------*/ static int runScript(pMcStasController self, SConnection *pCon, SicsInterp *pSics, int argc, char *argv[]){ char pResult[256]; int status; if(argc < 3){ SCWrite(pCon, "ERRROR: insufficient number of arguments to mccontrol run", eError); return 0; } status = invokeScript(self,argv[2],pSics,pResult,255); if(status == 1){ SCWrite(pCon,pResult,eValue); return 1; } else { SCWrite(pCon,pResult,eError); return 0; } } /*------------------------------------------------------------------------*/ static int start(pMcStasController self, SConnection *pCon){ int status; status = McStasStart(self,eTimer,1000.); if(status != OKOK){ SCWrite(pCon,self->errorText,eError); return 0; } SCSendOK(pCon); return 1; } /*-----------------------------------------------------------------------*/ static void wait4Finish(pMcStasController self){ int status; if(self->pid > 0){ status = waitpid(self->pid,NULL,WNOHANG); if(status >= 0){ self->pid = -1; } } } /*-------------------------------------------------------------------------*/ int McStasControllerWrapper(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]){ pMcStasController self = NULL; char pBueffel[255], pFile[132]; self = (pMcStasController)pData; assert(self); if(argc < 2) { SCWrite(pCon,"ERROR: insufficient number of arguments to mccontrol", eError); return 0; } strtolower(argv[1]); if(strcmp(argv[1],"configure") == 0){ return configureController(self,pCon,argc,argv); } else if(strcmp(argv[1],"list") == 0){ listConfiguration(self,pCon); return 1; } else if(strcmp(argv[1],"run") == 0){ return runScript(self,pCon,pSics,argc,argv); } else if(strcmp(argv[1],"start") == 0){ return start(self,pCon); } else if(strcmp(argv[1],"finish") == 0){ wait4Finish(self); SCSendOK(pCon); return 1; } else { snprintf(pBueffel,255,"ERROR: subcommand %s to mccontrol unknown", argv[1]); SCWrite(pCon,pBueffel,eError); return 0; } return 0; } /*========================== the actual action functions =================*/ int McStasStart(pMcStasController self, CounterMode mode, float fPreset){ char pResult[256]; int status; FILE *fd = NULL; self->fPreset = fPreset; self->mode = mode; /** * make sure that the monitor file has only a 0 in it... */ if(!StringDictGet(self->scripts,"mcmonfile",pResult,255)){ strncpy(self->errorText,"Misconfiguration: no monfile",255); return HWFault; } fd = fopen(pResult,"w"); if(fd == NULL){ strncpy(self->errorText,"Failed to access monitor file",255); return HWFault; } fprintf(fd,"0\n"); fclose(fd); /* * invoke start script */ status = invokeScript(self,"mcstart",pServ->pSics,pResult,255); if(status == 0){ strncpy(self->errorText,pResult,255); return HWFault; } /* * some general initializations .. */ self->pid = atoi(pResult); self->startTime = time(NULL); self->lastUpdate = self->startTime - self->updateIntervall; self->lastMonitorRead = self->startTime; return OKOK; } /*------------------------------------------------------------------------*/ static long readMonFile(pMcStasController self){ char pResult[256]; FILE *fd = NULL; long monValue = -1; int i; if(!StringDictGet(self->scripts,"mcmonfile",pResult,255)){ return -1; } fd = fopen(pResult,"r"); if(fd != NULL){ fscanf(fd,"%ld", &monValue); fclose(fd); } return monValue; } /*------------------------------------------------------------------------*/ int McStasStatus(pMcStasController self, float *fControl){ char pResult[256]; float monValue; int status, i; /* * check at max any second, else SICS keeps the system busy and * there is no CPU left for McStas */ SicsWait(1); status = invokeScript(self,"mcisrunning",pServ->pSics,pResult, 255); if(status == 0){ strncpy(self->errorText,pResult,255); return HWFault; } status = atoi(pResult); /* * handle timer mode */ if(status == 1 && self->mode == eTimer && time(NULL) >= self->startTime + (int)self->fPreset){ McStasStop(self); return HWBusy; } else { *fControl = time(NULL) - self->startTime; } /* * handle monitor mode */ if(status == 1 && self->mode == ePreset){ /* * check only any three seconds, else SICS uses up all the CPU time * and the simulation has no chance. */ *fControl = self->lastMon; if(time(NULL) < self->lastMonitorRead + 3) { return HWBusy; } monValue = -1; /* * try to read the monfile up to three times. Problems reading it * can be synchronisation problems with McStas */ for(i = 0, monValue = -1; i < 7; i++){ monValue = (float)readMonFile(self); if(monValue >= 0){ break; } } if(monValue < 0){ return HWBusy; } self->lastMonitorRead = time(NULL); monValue *= self->monitorScale; *fControl = monValue; self->lastMon = monValue; if(monValue >= self->fPreset){ McStasStop(self); } } if(status == 1){ return HWBusy; } else { self->stopTime = time(NULL); self->pid = -1; return HWIdle; } } /*-------------------------------------------------------------------------*/ int McStasStop(pMcStasController self){ char pResult[256]; invokeScript(self,"mckill",pServ->pSics,pResult, 255); self->lastUpdate = time(NULL) - 3*self->updateIntervall; return 1; } /*-------------------------------------------------------------------------*/ int McStasTransferData(pMcStasController self){ char pResult[256]; int status; /* * prevent to frequent requests */ if(self->lastUpdate + self->updateIntervall > time(NULL)){ return OKOK; } self->lastUpdate = time(NULL); if(self->pid >= 0){ status = invokeScript(self,"mcdump",pServ->pSics,pResult, 255); if(status == 0){ strncpy(self->errorText,pResult,255); self->lastUpdate = time(NULL) - self->updateIntervall; return HWFault; } } status = invokeScript(self,"mccopydata",pServ->pSics,pResult, 255); if(status == 0){ strncpy(self->errorText,pResult,255); self->lastUpdate = time(NULL) - self->updateIntervall; return HWFault; } return OKOK; } /*-------------------------------------------------------------------------*/ int McStasGetError(pMcStasController self, char *error, int errLen){ strncpy(error,self->errorText,errLen); return 1; } /*-------------------------------------------------------------------------*/ int McStasFix(pMcStasController self){ /* * if the monitor file cannot be read, this may be caused by * a conflict of mCstas writing while we are reading. let us * retry... */ if(strstr(self->errorText,"monitor") != NULL){ return COREDO; } /* * you have to edit the scripts to fix anything which is going wrong here. * But make sure the simulation is stopped. */ McStasStop(self); self->stopTime = time(NULL); self->pid = -1; return COTERM; } /*-------------------------------------------------------------------------*/ float McStasGetTime(pMcStasController self){ if(self->pid < 0){ return (float)(self->stopTime - self->startTime); } else { return (float)time(NULL) - self->startTime; } }