/*---------------------------------------------------------------------------- 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)) { strlcpy(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); strlcpy(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)) { strlcpy(self->errorText, "Misconfiguration: no monfile", 255); return HWFault; } fd = fopen(pResult, "w"); if (fd == NULL) { strlcpy(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) { strlcpy(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) { strlcpy(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) { strlcpy(self->errorText, pResult, 255); self->lastUpdate = time(NULL) - self->updateIntervall; return HWFault; } } status = invokeScript(self, "mccopydata", pServ->pSics, pResult, 255); if (status == 0) { strlcpy(self->errorText, pResult, 255); self->lastUpdate = time(NULL) - self->updateIntervall; return HWFault; } return OKOK; } /*-------------------------------------------------------------------------*/ int McStasGetError(pMcStasController self, char *error, int errLen) { strlcpy(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; } }