Files
sics/mccontrol.c
2012-11-15 12:39:51 +11:00

483 lines
13 KiB
C

/*----------------------------------------------------------------------------
McStas simulation to SICS controller module implementation file. For more
details see mcstas.tex.
copyright: see file COPYRIGHT
Mark Koennecke, June 2005
-----------------------------------------------------------------------------*/
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <assert.h>
#include <tcl.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#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;
}
}