diff --git a/.cdtproject b/.cdtproject index 784922d7..9a0d3e73 100644 --- a/.cdtproject +++ b/.cdtproject @@ -3,7 +3,13 @@ - + + + + + + + @@ -18,7 +24,7 @@ - + @@ -26,6 +32,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 00000000..ad6d1038 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,2 @@ +emergency.scn +recover.bin diff --git a/.project b/.project index 64b240cd..8fb722c3 100644 --- a/.project +++ b/.project @@ -7,7 +7,12 @@ org.eclipse.cdt.make.core.makeBuilder + clean,full,incremental, + + org.eclipse.cdt.make.core.build.arguments + -f makefile_slinux + org.eclipse.cdt.core.errorOutputParser org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.VCErrorParser; @@ -20,6 +25,10 @@ org.eclipse.cdt.make.core.incrementalBuildTarget all + + org.eclipse.cdt.make.core.environment + + org.eclipse.cdt.make.core.enableAutoBuild false @@ -28,6 +37,10 @@ org.eclipse.cdt.make.core.buildLocation + + org.eclipse.cdt.make.core.build.target.inc + all + org.eclipse.cdt.make.core.enableFullBuild true @@ -36,6 +49,18 @@ org.eclipse.cdt.make.core.enabledIncrementalBuild true + + org.eclipse.cdt.make.core.build.location + + + + org.eclipse.cdt.make.core.build.target.clean + clean + + + org.eclipse.cdt.make.core.build.command + make + org.eclipse.cdt.make.core.enableCleanBuild true @@ -44,6 +69,10 @@ org.eclipse.cdt.make.core.cleanBuildTarget clean + + org.eclipse.cdt.make.core.append_environment + true + org.eclipse.cdt.make.core.useDefaultBuildCmd false @@ -64,6 +93,10 @@ org.eclipse.cdt.make.core.stopOnError false + + org.eclipse.cdt.make.core.build.target.auto + all + diff --git a/.settings/org.eclipse.cdt.core.prefs b/.settings/org.eclipse.cdt.core.prefs new file mode 100644 index 00000000..b0b2face --- /dev/null +++ b/.settings/org.eclipse.cdt.core.prefs @@ -0,0 +1,3 @@ +#Thu Jan 11 14:43:46 CET 2007 +eclipse.preferences.version=1 +indexerId=org.eclipse.cdt.core.fastIndexer diff --git a/HistDriv.i b/HistDriv.i index ae5e646d..b9fad721 100644 --- a/HistDriv.i +++ b/HistDriv.i @@ -1,5 +1,5 @@ -#line 462 "histogram.w" +#line 467 "histogram.w" /*--------------------------------------------------------------------------- H I S T D R I V @@ -58,6 +58,9 @@ SConnection *pCon); float (*GetTime)(pHistDriver self, SConnection *pCon); + HistInt *(*SubSample)(pHistDriver self, + SConnection *pCon,int bank, + char *command); int (*Preset)(pHistDriver self, SConnection *pCon, HistInt iVal); @@ -69,17 +72,19 @@ void *pPriv; } HistDriver; -#line 474 "histogram.w" +#line 479 "histogram.w" -#line 229 "histogram.w" +#line 232 "histogram.w" pHistDriver CreateHistDriver(pStringDict pDict); void DeleteHistDriver(pHistDriver self); int HistDriverConfig(pHistDriver self, pStringDict pOpt, SConnection *pCon); + HistInt *DefaultSubSample(pHistDriver self, SConnection *pCon, + int bank, char *command); -#line 475 "histogram.w" +#line 480 "histogram.w" #endif diff --git a/HistMem.h b/HistMem.h index a05cb32f..6ffc568b 100644 --- a/HistMem.h +++ b/HistMem.h @@ -1,5 +1,5 @@ -#line 435 "histogram.w" +#line 440 "histogram.w" /*-------------------------------------------------------------------------- H I S T M E M @@ -42,22 +42,22 @@ eReflect } OverFlowMode; -#line 455 "histogram.w" +#line 460 "histogram.w" /*--------------------------------------------------------------------------*/ -#line 287 "histogram.w" +#line 292 "histogram.w" pHistMem CreateHistMemory(char *drivername); void DeleteHistMemory(void *self); -#line 303 "histogram.w" +#line 308 "histogram.w" int HistGetOption(pHistMem self, char *name, char *result, int iResultLen); int HistSetOption(pHistMem self, char *name, char *value); int HistConfigure(pHistMem self, SConnection *pCon, SicsInterp *pSics); -#line 331 "histogram.w" +#line 336 "histogram.w" float GetHistPreset(pHistMem self); int SetHistPreset(pHistMem self, float fVal); @@ -73,7 +73,7 @@ void HistDirty(pHistMem self); -#line 361 "histogram.w" +#line 366 "histogram.w" int SetHistogram(pHistMem self, SConnection *pCon, int i,int iStart, int iEnd, HistInt *lData); @@ -85,7 +85,7 @@ HistInt *lData, int iDataLen); int PresetHistogram(pHistMem self, SConnection *pCon, HistInt lVal); -#line 404 "histogram.w" +#line 409 "histogram.w" int MakeHistMemory(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); @@ -94,7 +94,7 @@ int argc, char *argv[]); -#line 457 "histogram.w" +#line 462 "histogram.w" #endif diff --git a/HistMem.i b/HistMem.i index c3bc4a22..5c6b98d0 100644 --- a/HistMem.i +++ b/HistMem.i @@ -1,5 +1,5 @@ -#line 480 "histogram.w" +#line 485 "histogram.w" /*--------------------------------------------------------------------------- H I S T M E M -- Internal @@ -11,7 +11,7 @@ #ifndef SICSHISTMEMINT #define SICSHISTMEMINT -#line 251 "histogram.w" +#line 256 "histogram.w" typedef struct __HistMem { pObjectDescriptor pDes; @@ -23,7 +23,7 @@ pICallBack pCall; } HistMem; -#line 490 "histogram.w" +#line 495 "histogram.w" #endif diff --git a/README b/README new file mode 100644 index 00000000..570f8e6a --- /dev/null +++ b/README @@ -0,0 +1,70 @@ + + SICS README + + Requirements + + - hdf-4, hdf5- libraries: http://hdf.ncsa.uiuc.edu + - szlib : same place as HDF + - Mini XML library : http://www.minixml.org/software.php + - libghttp : + http://ftp.gnome.org/pub/GNOME/source/libghttp/1.0/libghttp-1.0.9.tar.gz + - json-c : http://oss.metaparadigm.com/json-c + - tcl : any version from 8.0, package tcl-devel on most + modern linux distros + + + Building + + Install the libraries stated above, preferably to a common place. + Then edit the suplied makefile, instance makefile_linux, and change: + - uncomment all the NI, NIOBJ, NILIB stuff, except if you want support + for the NI enet100 GPIB/TCP/IP converter. + - Edit linux_def and set HDFROOT to where you installed your libraries + - Review the CFLAGS and LIBS to match your setup. Consider file format + format support: + ** HDF-4 required -DHDF4 in CFLAGS and -lmfdf -ldf in LIBS + ** HDF-5 support requires: -DHDF5 in CFLAGS and -lhdf5 in LIBS + ** XML support requires: -DNXXML -n CFLAGS and -lmxml in LIBS + - build with make -f makefile_linux + - Good Luck!! + Sorry, no configure script here. There are so few people building SICS + that it is not worth the effort. The effort really is to build the + libraries. + + + Running + + In the sim directory there are startup scripts for a number of different + instruments. To run any of them: + - edit the instrument file and change the home or root variable at the + top to match your setup. + - run with: SICServer path-to-instrument-file + For example: SICServer sim/topsi/morpheus.tcl + - Common issues: + ** tmp directory missing: create one + ** SicsDataNumber file missing: create a file with a single 0 in it + + + Trying it out with telnet + + - telnet host-where-sics-runs 2911 + - type username and password: Spy 007 is a good idea for the supplied sims + - type SICS commands + + + Directories + + sics : root directory containing the SICS kernel + sics/psi : psi specific drivers and stuff + sics/site_ansto : ANSTO specific stuff. Currently empty, ANSTO has its + own cvs + sics/dummy : example kit for defining an own site + sics/doc/user : user documentation + sics/doc/manager : manager documentation + sics/doc/programmer : programmer documentation + sics/matrix : matrix library used within SICS + sics/mcstas : code for virtual McStas instruments + sics/sim : control files for various instruments + sics/test : a sort of regression test for the SICS server + + \ No newline at end of file diff --git a/SCinter.c b/SCinter.c index 193b2055..611fca13 100644 --- a/SCinter.c +++ b/SCinter.c @@ -41,7 +41,7 @@ Mark Koennecke, August 2001, modified SicsWriteStatus to write motor positions on demand. - Made ListObjects moe intelligent: list objects according to interface etc. + Made ListObjects more intelligent: list objects according to interface etc. Mark Koennecke, December 2003 Extended 'dir' command (function ListObjects) to list via typename from @@ -50,6 +50,8 @@ Modified printXXX functions to fix duplicate write of last buffer line. Paul Hathaway, May 2004 + + Added FindAlias function, Mark Koennecke, January 2007 ---------------------------------------------------------------------------*/ #include #include @@ -57,6 +59,8 @@ #include #include #include +#include +#include #include "fortify.h" #include "sics.h" #include "splitter.h" @@ -66,6 +70,7 @@ #include "motor.h" #include "obdes.h" #include "lld.h" +#include "dynstring.h" /* M.Z. */ #include "definealias.h" @@ -133,6 +138,7 @@ static void freeList(int listID); SICSLogWrite(pBueffel,eInternal); return 0; } + memset(pNew,0,sizeof(CommandList)); /* if no data given, initialise with Dummy struct */ if(!pData) @@ -151,6 +157,7 @@ static void freeList(int listID); pNew->pData = pData; pNew->pNext = NULL; pNew->startupOnly = startupOnly; + pNew->stat = StatisticsNew(pBueffel); /* find end of list */ tail = NULL; @@ -232,10 +239,14 @@ static void freeList(int listID); { pInterp->pCList = pVictim->pNext; } + if (pVictim->stat) { + StatisticsKill(pVictim->stat); + } free(pVictim); return 1; } + #define MAXLEN 256 #define MAXCOM 50 extern char *stptok(char *s, char *tok, unsigned int toklen, char *brk); @@ -252,6 +263,7 @@ extern char *SkipSpace(char *pPtr); char *pPtr; char **argv = NULL; commandContext comCon; + Statistics *old; assert(self); @@ -307,7 +319,9 @@ extern char *SkipSpace(char *pPtr); Tcl_ResetResult((Tcl_Interp *)self->pTcl); MacroPush(pCon); pCon->conStatus = 0; + old = StatisticsBegin(pCommand->stat); iRet = pCommand->OFunc(pCon, self, pCommand->pData, argc, argv); + StatisticsEnd(old); /* If a task is registered with the dev exec then conStatus is HWBusy*/ if (pCon->conStatus != HWBusy) { comCon = SCGetContext(pCon); @@ -422,7 +436,7 @@ extern char *SkipSpace(char *pPtr); } if(fVal > -990.) { - fprintf(fd,"run %s %f\n",pCurrent->pName, fVal); + fprintf(fd,"drive %s %f\n",pCurrent->pName, fVal); } } } @@ -444,29 +458,47 @@ extern char *SkipSpace(char *pPtr); void DeleteInterp(SicsInterp *self) { CommandList *pCurrent = NULL; - CommandList *pTemp; + CommandList *pTemp, *tail; Tcl_Interp *pTcl = NULL; int i; assert(self); self->iDeleting = 1; - /* delete Commandlist */ + /* find end of list */ + tail = NULL; pCurrent = self->pCList; - while(pCurrent) + while(pCurrent != NULL) { - if(pCurrent->KFunc) + tail = pCurrent; + pCurrent = pCurrent->pNext; + } + + /* delete Commandlist (reversed order) */ + if (tail) { + pCurrent = tail; + while(pCurrent) { - pCurrent->KFunc(pCurrent->pData); + /* the line below fixes problems with kill functions + * traversing the command list + */ + pCurrent->pNext = NULL; + if(pCurrent->KFunc) + { + pCurrent->KFunc(pCurrent->pData); + } + if(pCurrent->pName) + { + /* printf("Deleting %s\n",pCurrent->pName); */ + free(pCurrent->pName); + } + if (pCurrent->stat) { + StatisticsKill(pCurrent->stat); + } + pTemp = pCurrent->pPrevious; + free(pCurrent); + pCurrent = pTemp; } - if(pCurrent->pName) - { - /* printf("Deleting %s\n",pCurrent->pName); */ - free(pCurrent->pName); - } - pTemp = pCurrent->pNext; - free(pCurrent); - pCurrent = pTemp; } FreeAliasList(&self->AList); /* M.Z. */ @@ -949,6 +981,11 @@ static void printType(SicsInterp *pSics, SConnection *pCon, char *typeName) if(!pCom->pData) return NULL; + if (cclass == NULL) + { + return pCom->pData; + } + pDum = (pDummy)pCom->pData; if(strcmp(pDum->pDescriptor->name,cclass) == 0) { @@ -956,6 +993,19 @@ static void printType(SicsInterp *pSics, SConnection *pCon, char *typeName) } return NULL; } +/*---------------------------------------------------------------------------*/ + pObjectDescriptor FindCommandDescriptor(SicsInterp *pSics, char *name) + { + CommandList *pCom; + + pCom = FindCommand(pSics,name); + if(pCom == NULL || pCom->pData == NULL) + { + return NULL; + } + + return ((pDummy)pCom->pData)->pDescriptor; + } /*------------------------------------------------------------------------*/ void *FindDrivable(SicsInterp *pSics, char *name){ pIDrivable pDriv; @@ -1021,3 +1071,80 @@ static void freeList(int listID) pCurrent = pNext; } } +/*---------------------------------------------------------------------*/ +char *FindAliases(SicsInterp *pSics, char *name) +{ + pDynString result = NULL; + CommandList *pOri = NULL, *pCom = NULL; + char *pTrans = NULL, *charResult = NULL; + int first; + + pOri = FindCommand(pSics, name); + if(pOri == NULL) + { + return NULL; + } + if(pOri->pData == NULL) + { + return NULL; + } + + result = CreateDynString(64,64); + if(result == NULL) + { + return NULL; + } + + /* try first to locate Markus style aliases */ + pTrans = TranslateAlias(&pSics->AList,name); + if(strcmp(pTrans,name) != 0) + { + DynStringCopy(result,pTrans); + charResult = strdup(GetCharArray(result)); + DeleteDynString(result); + return charResult; + } + + /* + * locate SicsAlias style aliases by comparing the original + * data pointer with the data pointers of other commands + */ + first = 1; + pCom = pSics->pCList; + while(pCom != NULL) + { + if(pCom != pOri && pCom->pData == pOri->pData) + { + if(first) + { + DynStringCopy(result,pCom->pName); + first = 0; + } + else + { + DynStringConcat(result,","); + DynStringConcat(result,pCom->pName); + } + } + pCom = pCom->pNext; + } + charResult = strdup(GetCharArray(result)); + DeleteDynString(result); + return charResult; +} +/*---------------------------------------------------------------------*/ +void ForEachCommand(int (*scanFunction)(char *name, pDummy object, void *userData) + , void *userData) +{ + CommandList *pCurrent; + + + for(pCurrent = pServ->pSics->pCList; + pCurrent != NULL; + pCurrent = pCurrent->pNext) + { + if(scanFunction(pCurrent->pName, pCurrent->pData, userData) == 0) { + return; + } + } +} diff --git a/SCinter.h b/SCinter.h index dacaf28d..1b24ce24 100644 --- a/SCinter.h +++ b/SCinter.h @@ -9,7 +9,9 @@ ---------------------------------------------------------------------------*/ #ifndef SICSINTERPRETER #define SICSINTERPRETER +#include "obdes.h" #include "Scommon.h" +#include "statistics.h" #include /* M.Z. */ #include "definealias.i" @@ -31,6 +33,7 @@ typedef struct __Clist { struct __Clist *pNext; struct __Clist *pPrevious; int startupOnly; + Statistics *stat; } CommandList; typedef struct __SINTER @@ -132,23 +135,39 @@ typedef struct __SINTER */ char *FindAlias(SicsInterp *pSics, void *pData); - +/*---------------------------------------------------------------------- + FindAliases locates alle aliases related to a gibe name. The result + is returned as a komma separated list. + */ + char *FindAliases(SicsInterp *pSics, char *name); /*------------------------------------------------------------------------- FindCommandData finds a command with the name given. It tests the name in the ObjectDescriptor to be of name class. If all this succeeds a pointer - to the commands data structure is retuned. Else NULL + to the commands data structure is retuned. Else NULL. + Do not test the Object Descriptor name when comclass == NULL. */ void *FindCommandData(SicsInterp *pSics, char *name, char *comclass); +/*------------------------------------------------------------------------- + FindCommandDescriptor finds the descriptor of a command with the name given. +*/ + pObjectDescriptor FindCommandDescriptor(SicsInterp *pSics, char *name); + /*------------------------------------------------------------------------ FindDrivable tries to find Drivable object by the name given. Returns a pointer to the drivable interface in the case of success, NULL in case of failure. In order to save me fixing header files the pointer must be cast to the drivable interface pointer. - ------------------------------------------------------------------------*/ - +*/ void *FindDrivable(SicsInterp *pics, char *name); +/*------------------------------------------------------------------------ + Go through the command list and call scanFunction for every command + until the return value is 0. +*/ +void ForEachCommand(int (*scanFunction)(char *name, pDummy object, void *userData) + , void *userData); + /*----------------------------------------------------------------------- Get a copy of the Tcl interpreter ------------------------------------------------------------------------*/ diff --git a/SICSmain.c b/SICSmain.c index d392f58f..08639957 100644 --- a/SICSmain.c +++ b/SICSmain.c @@ -21,6 +21,7 @@ #include #include #include "nserver.h" +#include "servlog.h" /***************************** Necessary Globals ****************************/ @@ -39,16 +40,18 @@ int main(int argc, char *argv[]) { int iRet; - + char *file=NULL; + int i; + /* initialise, will die on you if problems */ - if(argc >= 2) - { - iRet = InitServer(argv[1],&pServ); - } - else - { - iRet = InitServer(NULL,&pServ); + for (i=1; i +#include +#include static char *aCode[] = { "internal", @@ -13,4 +14,19 @@ "spy", NULL }; static int iCodes = 4; -#endif +/*--------------------------------------------------------------------------*/ +int decodeSICSPriv(char *privText){ + int code = 0; + + strtolower(privText); + while(aCode[code] != NULL){ + if(strcmp(aCode[code], privText) == 0){ + return code; + } + code++; + } + if(code >= iCodes){ + return -1; + } + return -1; +} diff --git a/alias.c b/alias.c index 62bf3840..45155e15 100644 --- a/alias.c +++ b/alias.c @@ -212,3 +212,22 @@ } return 1; } +/*-------------------------------------------------------------------------------*/ +int LocateAliasAction(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]){ + char *aliases = NULL; + + if(argc < 2){ + SCWrite(pCon,"ERROR: missing argument aliasname for locating aliases",eError); + return 0; + } + + strtolower(argv[1]); + aliases = FindAliases(pSics,argv[1]); + if(aliases == NULL){ + SCWrite(pCon,"NONE", eValue); + } else { + SCPrintf(pCon,eValue,"%s = %s",argv[1], aliases); + } + return 1; +} diff --git a/alias.h b/alias.h index 88b327e4..4a70827c 100644 --- a/alias.h +++ b/alias.h @@ -16,5 +16,7 @@ int argc, char *argv[]); int MakeAlias(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); + int LocateAliasAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); #endif diff --git a/anticollider.c b/anticollider.c index f17fc583..6c58dbee 100644 --- a/anticollider.c +++ b/anticollider.c @@ -113,6 +113,7 @@ static long ColliderSetValue(void *pData, SConnection *pCon, float fTarget){ iRet = Tcl_Eval(pServ->pSics->pTcl,Tcl_DStringValue(&command)); if(iRet != TCL_OK){ SCWrite(pCon,"ERROR: Movement not possible or bad collider script",eError); + SCWrite(pCon,Tcl_DStringValue(&command),eError); /* SCWrite(pCon,pServ->pSics->pTcl->result,eError); */ diff --git a/ascon.c b/ascon.c new file mode 100644 index 00000000..3fb2fe23 --- /dev/null +++ b/ascon.c @@ -0,0 +1,537 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sics.h" +#include "splitter.h" +#include "ascon.i" + +/* + CreateSocketAdress stolen from Tcl. Thanks to John Ousterhout +*/ + +static int CreateSocketAdress( + struct sockaddr_in *sockaddrPtr, /* Socket address */ + char *host, /* Host. NULL implies INADDR_ANY */ + int port) /* Port number */ +{ + struct hostent *hostent; /* Host database entry */ + struct in_addr addr; /* For 64/32 bit madness */ + + (void) memset((char *) sockaddrPtr, '\0', sizeof(struct sockaddr_in)); + sockaddrPtr->sin_family = AF_INET; + sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF)); + if (host == NULL) { + addr.s_addr = INADDR_ANY; + } else { + hostent = gethostbyname(host); + if (hostent != NULL) { + memcpy((char *) &addr, + (char *) hostent->h_addr_list[0], (size_t) hostent->h_length); + } else { + addr.s_addr = inet_addr(host); + if (addr.s_addr == (unsigned long)-1) { + return 0; /* error */ + } + } + } + /* + * There is a rumor that this assignment may require care on + * some 64 bit machines. + */ + sockaddrPtr->sin_addr.s_addr = addr.s_addr; + return 1; +} + +double DoubleTime(void) { + struct timeval now; + /* the resolution of this function is usec, if the machine supports this + and the mantissa of a double is 51 bits or more (31 for sec and 20 for micro) + */ + gettimeofday(&now, NULL); + return now.tv_sec + now.tv_usec / 1e6; +} + +void AsconError(Ascon *a, char *msg, int errorno) { + static char *stateText[]={ + "state 0", "kill", "state 2", "notConnected", + "connect", "start connect", "connect finished", "connect failed", + "write", "start write", "write finished", "write failed", + "read", "start read", "read finished", "read failed", + "state 16", "state 17", "state 18", "idle" + }; + char *state; + + if (a->state < 0 || a->state > 19) { + state = "bad state"; + } else { + state = stateText[a->state]; + } + if (errorno != 0) { + a->errList = ErrPutMsg(a->errList, "ASCERR: %s %s (during %s)", msg, strerror(errorno), state); + } else { + a->errList = ErrPutMsg(a->errList, "ASCERR: %s (during %s)", msg, state); + } + a->state |= AsconFailed; +} + +static void AsconConnect(Ascon *a) { + /* input state: AsconConnectStart + output state: AsconFailed or AsconConnecting */ + int ret; + struct sockaddr_in adr; + char *colon; + int port; + int oldopts; + + if (a->fd < 0) { + a->fd = socket(AF_INET,SOCK_STREAM,0); + if (a->fd < 0) { + AsconError(a, "socket failed:", errno); + return; + } + } + colon = strchr(a->hostport, ':'); + if (colon == NULL) return; + port = atoi(colon+1); + if (port <= 0) { + AsconError(a, "bad port number", 0); + return; + } + *colon = '\0'; + ret = CreateSocketAdress(&adr, a->hostport, port); + *colon = ':'; + if (ret == 0) { + AsconError(a, "bad host specification", 0); + return; + } + /* should we insert the workaround for lantronix server ? see network.c */ + oldopts = fcntl(a->fd, F_GETFL, 0); + fcntl(a->fd, F_SETFL, oldopts | O_NONBLOCK); + ret = connect(a->fd, (struct sockaddr *)&adr, sizeof(struct sockaddr_in)); + if (ret < 0) { + switch(errno) { + case EINPROGRESS: + case EALREADY: + case EISCONN: + a->state = AsconConnecting; + break; + default: + AsconError(a, "connect failed:", errno); + return; + } + } + a->state = AsconConnecting; + return; +} + +int AsconStdInit(Ascon *a, SConnection *con, + int argc, char *argv[]) { + a->fd = -1; + a->state = AsconConnectStart; + a->reconnectInterval = 10; + a->hostport = strdup(argv[1]); + if(argc > 2){ + a->sendTerminator = strdup(argv[2]); + } else { + a->sendTerminator = strdup("\n"); + } + if(argc > 3){ + a->timeout = atof(argv[3]); + } else { + a->timeout = 2.0; /* sec */ + } + return 1; +} + +int AsconReadGarbage(int fd) { + fd_set rmask; + struct timeval tmo = {0,0}; + int l, ret, result; + char garbage[100]; + + FD_ZERO(&rmask); + result = 0; + do { + FD_SET(fd, &rmask); + ret = select(fd + 1, &rmask, NULL, NULL, &tmo); + if (ret > 0) { + l = recv(fd, garbage, sizeof garbage, 0); + if (l > 0) { + /* swallow */ + result += l; + } else if (l == 0) { + errno = ECONNRESET; + return -2; + } else if (l < 0) { + return -2; + } + } + } while (ret > 0); + return result; +} + +void PrintChar(char chr) { + if (chr <= 32 || chr >= 127) { + printf("%2.2x ", chr); + } else { + printf(" %c ", chr); + } +} + +int AsconConnectSuccess(int fd) { + fd_set wmask, rmask; + struct timeval tmo = {0,0}; + int oldopts; + int ret; + + oldopts = fcntl(fd, F_GETFL, 0); + assert(oldopts | O_NONBLOCK); /* fd must be in non-blocking mode */ + + FD_ZERO(&wmask); + FD_ZERO(&rmask); + FD_SET(fd, &wmask); + FD_SET(fd, &rmask); + ret = select(fd + 1, &rmask, &wmask, NULL, &tmo); + if (ret > 0) { + assert(FD_ISSET(fd, &wmask)); + if (FD_ISSET(fd, &rmask)) { /* there may already be data for read */ + if (recv(fd, NULL, 0, 0) < 0) { /* zero length, check only return value */ + ret = ASCON_RECV_ERROR; /* first recv failed */ + } + } else { + if (send(fd, NULL, 0, 0) < 0) { /* zero length, check only return value */ + ret = ASCON_SEND_ERROR; /* first send failed */ + } + } + } + fcntl(fd, F_SETFL, oldopts & ~ O_NONBLOCK); /* reset to blocking mode */ + return ret; +} + +int AsconReadChar(int fd, char *chr) { + fd_set rmask; + struct timeval tmo = {0,0}; + int ret; + + FD_ZERO(&rmask); + FD_SET(fd, &rmask); + ret = select(fd + 1, &rmask, NULL, NULL, &tmo); + if (ret <= 0) return ret; + ret = recv(fd, chr, 1, 0); + /* PrintChar(*chr); */ + fflush(stdout); + if (ret > 0) return 1; + if (ret == 0) { + errno = ECONNRESET; + return ASCON_DISCONNECTED; + } + return ASCON_RECV_ERROR; +} + +int AsconWriteChars(int fd, char *data, int length) { + fd_set wmask; + struct timeval tmo = {0,0}; + int ret; + + if (length <= 0) return 0; + /* + { int i; + for (i=0; i 0) return ret; + if (ret == 0) { + errno = ECONNRESET; + return ASCON_DISCONNECTED; + } + return ASCON_SEND_ERROR; +} + +static double lastCall = 0; + +int AsconStdHandler(Ascon *a) { + int ret; + int l; + char chr; + double now = DoubleTime(); + + if (now > lastCall + 0.5) { /* AsconStdHandler was not called since a long time (0.5 sec) */ + if (lastCall != 0) { /* extend timeout time (for debugging purposes) */ + a->start += now - lastCall - 0.5; + } + } + lastCall = now; + switch (a->state) { + case AsconConnectStart: + AsconConnect(a); + break; + case AsconConnecting: + ret = AsconConnectSuccess(a->fd); + if (ret == 0) { + /* in progress */ + } else if (ret > 0) { + a->state = AsconConnectDone; /* success */ + } else if (ret < 0) { + AsconError(a, "AsconConnectSuccess failed:", errno); + } + break; + case AsconWriteStart: + DynStringConcat(a->wrBuffer, a->sendTerminator); + a->wrPos = 0; + a->state = AsconWriting; + if(strstr(GetCharArray(a->wrBuffer),"@@NOSEND@@") != NULL){ + a->state = AsconWriteDone; + } + break; + case AsconWriting: + AsconReadGarbage(a->fd); + l = GetDynStringLength(a->wrBuffer) - a->wrPos; + ret = AsconWriteChars(a->fd, GetCharArray(a->wrBuffer) + a->wrPos, l); + if (ret < 0) { + AsconError(a, "send failed:", errno); + /* + * Ooops: which state shall we go to after a write fail? + * This seems to retry. + */ + } else { + a->wrPos += ret; + if (a->wrPos >= GetDynStringLength(a->wrBuffer)) { + a->state = AsconWriteDone; + } + } + break; + case AsconReadStart: + DynStringClear(a->rdBuffer); + a->start = DoubleTime(); + a->state = AsconReading; + break; + case AsconReading: + ret = AsconReadChar(a->fd, &chr); + while (ret > 0) { + a->start = DoubleTime(); + + if (chr == '\n') { + if (a->readState) { + /* swallow LF after CR */ + DynStringClear(a->rdBuffer); + a->readState = 0; + } else { + DynStringConcatChar(a->rdBuffer, '\0'); + a->state = AsconReadDone; + break; + } + } else if (chr == '\r') { + a->readState = 1; + DynStringConcatChar(a->rdBuffer, '\0'); + a->state = AsconReadDone; + break; + } else { + if (DynStringConcatChar(a->rdBuffer, chr) == 0) { + AsconError(a, "DynStringConcatChar failed:", ENOMEM); + break; + } + a->readState = 0; + } + ret = AsconReadChar(a->fd, &chr); + } + if (ret < 0) { + AsconError(a, "AsconReadChar failed:", errno); + return 1; + } + if (a->state == AsconReadDone) { + DynStringConcatChar(a->rdBuffer, '\0'); + } else { + if (a->timeout > 0) { + if (DoubleTime() - a->start > a->timeout) { + AsconError(a, "read timeout", 0); + a->state = AsconTimeout; + } + } + } + break; + default: + return 1; + } + return 1; +} + +/* define type AsconProtocolList and functions AsconProtocolAdd etc. */ +#define MC_NAME(T) AsconProtocol##T +#include "mclist.c" + +static AsconProtocolList protocols={0}; + +void AsconInsertProtocol(AsconProtocol *protocol) { + AsconProtocolAdd(&protocols, protocol); +} + +AsconHandler AsconSetHandler(Ascon *a, SConnection *con, + int argc, char *argv[]) { + AsconProtocol *p; + + if (argc < 1) return NULL; + if (strcasecmp(argv[0], "std") == 0) { + if (argc < 2) return NULL; + AsconStdInit(a, con, argc, argv); + return AsconStdHandler; + } + for (p = protocols.head; p!= NULL; p=p->next) { + if (strcasecmp(p->name, argv[0]) == 0) { + if(p->init(a, con, argc, argv)){ + return p->handler; + } else { + return NULL; + } + } + } + return NULL; +} + +/* --- implementation of higher level interface ---- */ + +char *ConcatArgs(int argc, char *argv[]) { + return Arg2Tcl(argc, argv, NULL, -1); +} + +Ascon *AsconMake(SConnection *con, int argc, char *argv[]) { + Ascon *a; + char *args; + + a = calloc(1, sizeof(*a)); + if (a == NULL) { + SCWrite(con, "ERROR: no memory", eError); + return NULL; + } + a->handler = AsconSetHandler(a, con, argc, argv); + if (a->handler == NULL) { + args = ConcatArgs(argc, argv); + if (!args) return NULL; + SCPrintf(con, eError, "ERROR: illegal protocol: %s", args); + free(args); + return NULL; + } + a->rdBuffer = CreateDynString(60, 63); + a->wrBuffer = CreateDynString(60, 63); + a->errList = NULL; + a->responseValid = 0; + a->reconnectInterval = 10; + a->lastReconnect = 0; + return a; +} + +void AsconKill(Ascon *a) { + if (a->fd > 0) { + close(a->fd); + } + DeleteDynString(a->rdBuffer); + DeleteDynString(a->wrBuffer); + if (a->hostport) { + free(a->hostport); + } + if(a->sendTerminator){ + free(a->sendTerminator); + } + if(a->private != NULL && a->killPrivate != NULL){ + a->killPrivate(a->private); + } + free(a); +} + +AsconStatus AsconTask(Ascon *a) { + double now; + + while (a->handler(a)) { + switch (a->state) { + case AsconReading: + case AsconWriting: + return AsconPending; + case AsconNotConnected: + return AsconOffline; + break; + case AsconConnectDone: + a->state = AsconIdle; + return AsconReady; + case AsconWriteDone: + if (a->noResponse) { + return AsconReady; + } + a->state = AsconReadStart; + break; + case AsconReadDone: + a->state = AsconIdle; + a->responseValid = 1; + return AsconReady; + case AsconConnecting: + return AsconUnconnected; + default: + switch (a->state % 4) { + case AsconOnTheWay: + case AsconStart: + return AsconPending; + case AsconFailed: + if (a->state != AsconTimeout) { + now = DoubleTime(); + if (now > a->lastReconnect + a->reconnectInterval) { + a->lastReconnect = now; + close(a->fd); + a->fd = -1; + a->state = AsconConnectStart; + } + } + return AsconFailure; + case AsconFinished: + if (a->state < AsconConnectFailed) { + return AsconUnconnected; + } + return AsconReady; + } + } + } + return AsconIdle; +} + +int AsconWrite(Ascon *a, char *command, int noResponse) { + if (a->state <= AsconConnectFailed || a->state % 4 < AsconFinished) return 0; + DynStringCopy(a->wrBuffer, command); + a->noResponse = noResponse; + a->state = AsconWriteStart; + a->responseValid = 0; + AsconTask(a); + return 1; +} + +char *AsconRead(Ascon *a) { + if (a->noResponse) { + a->noResponse=0; + return ""; + } + if (a->state % 4 == AsconFailed) { + a->state = AsconIdle; + return ""; + } + if (a->responseValid) { + a->responseValid = 0; + return GetCharArray(a->rdBuffer); + } + return NULL; +} + +ErrMsg *AsconGetErrList(Ascon *a) { + return a->errList; +} diff --git a/ascon.h b/ascon.h new file mode 100644 index 00000000..fe6f4d32 --- /dev/null +++ b/ascon.h @@ -0,0 +1,82 @@ +#ifndef ASCON_H +#define ASCON_H + +#include "sics.h" +#include "errormsg.h" + +/** \file + * \brief Asynchronous connection handling for devices controlled over tcp-ip + * connections. Interface for higher level modules. + */ + +/** \brief the asynchronous connection + */ +typedef struct Ascon Ascon; + +/** \brief the possible results of AsconTask + */ +typedef enum { + AsconOffline, + AsconUnconnected, + AsconPending, + AsconReady, + AsconFailure +} AsconStatus; + +/** \brief make a new asynchronous connection + * \param con the SICS connection + * \param argc number of arguments + * \param argv the arguments. argv[0] must be the protocol name, the other arguments + * are protocol specific, but argv[1] is usually host::port + * \return the created connection or NULL on failure + */ +Ascon *AsconMake(SConnection *con, int argc, char *argv[]); + +/** \brief kill function + * \param a the connection to be killed + */ +void AsconKill(Ascon *a); + +/** \brief the task handler. To be called repeatedly. + * \param a the connection + * \return the state of the connection + */ +AsconStatus AsconTask(Ascon *a); + +/** \brief write to the connection. allowed only when the state is AsconReady + * \param a the connection + * \param command the command to be sent + * \param noResponse 0 normally, 1 if no reponse is expected + * \return 1 on success, 0 when not ready + */ +int AsconWrite(Ascon *a, char *command, int noResponse); + +/** \brief read from the connection. allowed only when a response is available + * \param a the connection + * \return the response when a response is ready + * NULL when the command has not completed and the response is not yet finished + * "" when the command has completed, but no response was expected. + * The result is only valid until the next call to other AsconXxx functions + * and has to be duplicated if needed later. + */ +char *AsconRead(Ascon *a); + +/** \brief get the connections error list + * \return the error list + */ +ErrMsg *AsconGetErrList(Ascon *a); + +/** \brief a helper function + * \param argc the number of args + * \param argv the args to be concatenated + * \result a allocated string containing the concatenated arguments + * the args are properly quoted to be used as tcl proc arguments + */ +char *ConcatArgs(int argc, char *argv[]); + +/** \brief function for dealing with times with musec resolution + * \return absolute time as double value + */ +double DoubleTime(void); + +#endif diff --git a/ascon.i b/ascon.i new file mode 100644 index 00000000..25f01eaa --- /dev/null +++ b/ascon.i @@ -0,0 +1,156 @@ +#ifndef ASCON_I +#define ASCON_I + +#include +#include "ascon.h" +#include "dynstring.h" + +/** \file + * \brief Asynchronous connection handling for devices controlled over tcp-ip + * connections. Interface for the implementation of custom protocols. + * + * For the implementation of a custom protocol, you have to implement + * the handler function and the init function, declare the protocol + * of type AsconProtocol and call AsconInsertProtocol on startup. + * The handler and init functions are normally be a wrapper around AsconStdHandler + * and AsconStdInit + * + * The functions with fd as the first argument are utility functions with + * may be used in handler wrapper functions. + * On error, the return value may be one of the defined macros ASCON_xxx, + * and errno will give more details about the error. + */ + +/** + * A sub-state of the connection. Only states with sub-state AsconStart may + * be set by the caller, and only when the sub-state is not AsconOnTheWay + */ +typedef enum { AsconOnTheWay=0, AsconStart=1, AsconFinished=2, AsconFailed=3 } AsconMode; + +/** + * The state of the connection. The sub-state is state % 4. + */ +typedef enum { + AsconNotConnected=0+AsconFinished, + AsconConnecting=4+AsconOnTheWay, + AsconConnectStart=AsconConnecting+AsconStart, + AsconConnectDone=AsconConnecting+AsconFinished, + AsconConnectFailed=AsconConnecting+AsconFailed, + AsconWriting=8+AsconOnTheWay, + AsconWriteStart=AsconWriting+AsconStart, + AsconWriteDone=AsconWriting+AsconFinished, + AsconReading=12+AsconOnTheWay, + AsconReadStart=AsconReading+AsconStart, + AsconReadDone=AsconReading+AsconFinished, + AsconIdle=16+AsconFinished, + AsconTimeout=20 + AsconFailed +} AsconState; + +/** \brief the task handler function prototype + * + * custom handlers must have this prototype + */ +typedef int (* AsconHandler)(Ascon *connection); + +/** Ascon struct + * all members are public, allowing access by handler wrappers + */ +struct Ascon { + AsconState state; /**< the current state */ + int fd; /**< socket */ + int readState; /**< default implementation: 'was cr' */ + pDynString rdBuffer;/**< read buffer */ + pDynString wrBuffer;/**< write buffer */ + int wrPos; /**< write buffer position */ + double timeout; /**< read timeout (sec) */ + char *sendTerminator; /**< terminator for sending messages */ + char *hostport; /**< host:port to connect */ + ErrMsg *errList; /**< error message list */ + double start; /**< unix time when read was started */ + void *private; /**< private data of protocol */ + void (*killPrivate)(void *); /** < kill function for private */ + int noResponse; /**< no response expected */ + int responseValid; /**< a valid response is ready */ + AsconHandler handler; /**< handler function */ + double reconnectInterval; /**< reconnect interval */ + double lastReconnect; /**< last connect try */ +}; + +#define ASCON_SELECT_ERROR -1 +#define ASCON_RECV_ERROR -2 +#define ASCON_SEND_ERROR -3 +#define ASCON_DISCONNECTED -4 + +/** \brief the standard handler routine. + * \param a the connection + * \return 0 when task has finished (connection to be closed), 1 when active + * + * In most cases a custom handler may be a wrapper around AsconStdHandler + */ +int AsconStdHandler(Ascon *a); + +/** \brief initialize a standard connection + * \param a the connection + * \param con A connection to print errors too. + * \param hostport the tcp/ip address (syntax: host:port) + * + * In most cases a custom init function may be a wrapper around AsconStdInit + */ +int AsconStdInit(Ascon *a, SConnection *con, int argc, char *argv[]); + +/** The Ascon Protocol + */ +typedef struct AsconProtocol { + struct AsconProtocol *next; + char *name; + AsconHandler handler; + int (*init)(Ascon *s, SConnection *con, int argc, char *argv[]); +} AsconProtocol; + +/** \brief Insert a new protocol into the protocol list + * protocol the protocol (must be allocated by the caller, may be statically) + */ +void AsconInsertProtocol(AsconProtocol *protocol); + +/** \brief close the connection and free internal used memory + * \param a the connection to be closed + * remark: the connection struct itself has to be freed manually + */ +void AsconClose(Ascon *a); + +/** \brief swallow garbage (utility function) + * \param fd the socket + * \return >=0: number of chars swallowed, else error + */ +int AsconReadGarbage(int fd); + +/** \brief check if a connection has succeded (utility function) + * \param fd the socket + * \return 1: connection succesful, 0: connection in progress, <0: error + */ +int AsconConnectSuccess(int fd); + +/** \brief read one character, if available (utility function) + * \param fd the socket + * \param chr the result + * \return 1: succes, 0: no data available, <0: error + */ +int AsconReadChar(int fd, char *chr); + +/** \brief non blocking write (utility function) + * \param fd the socket + * \param data the data (not nul-terminated, may contain nul) + * \param length the length of the data + * \return >0: number of written chars,0: write not yet possible, <0: error + */ +int AsconWriteChars(int fd, char *data, int length); + +/** \brief store an error + * \param a The asynchronous I/O structure to store the + * error with + * \param msg The error message + * \param errorno The error number + */ +void AsconError(Ascon *a, char *msg, int errorno); + +#endif diff --git a/asyncprotocol.c b/asyncprotocol.c new file mode 100644 index 00000000..211dba1c --- /dev/null +++ b/asyncprotocol.c @@ -0,0 +1,341 @@ +#include +#include +#include + +int defaultSendCommand(pAsyncProtocol p, pAsyncTxn txn) { + int i, iRet; + int state; + const char *term = "\r\n"; + if (p->sendTerminator) + term = p->sendTerminator; + state = 0; + for (i = 0; i < txn->out_len; ++i) { + if (txn->out_buf[i] == 0x00) { /* end of transmission */ + break; + } + else if (txn->out_buf[i] == term[state]) { + ++state; + continue; + } + state = 0; + } + txn->txn_state = 0; + iRet = AsyncUnitWrite(txn->unit, txn->out_buf, txn->out_len); + if (iRet <= 0) + return iRet; + if (term[state] != 0) + iRet = AsyncUnitWrite(txn->unit,(void *) term, strlen(term)); + return iRet; +} + +int defaultHandleInput(pAsyncProtocol p, pAsyncTxn txn, int ch) { + const char *term = "\r\n"; + if (p->replyTerminator) + term = p->replyTerminator; + if (ch == term[txn->txn_state]) + ++txn->txn_state; + else + txn->txn_state = 0; + + if (txn->inp_idx < txn->inp_len) + txn->inp_buf[txn->inp_idx++] = ch; + if (term[txn->txn_state] == 0) { + if (txn->inp_idx < txn->inp_len) + txn->inp_buf[txn->inp_idx] = '\0'; + return AQU_POP_CMD; + } + return 1; +} + +int defaultHandleEvent(pAsyncProtocol p, pAsyncTxn txn, int event) { + /* TODO: what could or should we do to handle the event */ + return AQU_POP_CMD; +} + +int defaultPrepareTxn(pAsyncProtocol p, pAsyncTxn txn, const char* cmd, int cmd_len, int rsp_len) { + int i; + int state; + const char *term = "\r\n"; + if (p->sendTerminator) + term = p->sendTerminator; + state = 0; + for (i = 0; i < cmd_len; ++i) { + if (cmd[i] == 0x00) { /* end of transmission */ + cmd_len = i; + break; + } + else if (cmd[i] == term[state]) { + ++state; + continue; + } + state = 0; + } + if (term[state] == 0) { + /* outgoing command is correctly terminated */ + txn->out_buf = malloc(cmd_len + 1); + if (txn->out_buf == NULL) { + SICSLogWrite("Out of memory in AsyncProtocol::defaultPrepareTxn", eError); + return 0; + } + memcpy(txn->out_buf, cmd, cmd_len + 1); + } + else { + /* outgoing command is NOT correctly terminated */ + int tlen = strlen(term); + txn->out_buf = malloc(cmd_len + tlen + 1); + if (txn->out_buf == NULL) { + SICSLogWrite("Out of memory in AsyncProtocol::defaultPrepareTxn", eError); + return 0; + } + memcpy(txn->out_buf, cmd, cmd_len); + memcpy(txn->out_buf + cmd_len, term, tlen + 1); + cmd_len += tlen; + } + txn->out_len = cmd_len; + txn->out_idx = 0; + if(txn->inp_buf != NULL){ + free(txn->inp_buf); + } + txn->inp_buf = malloc(rsp_len); + if (txn->inp_buf == NULL) { + SICSLogWrite("Out of memory in AsyncProtocol::defaultPrepareTxn", eError); + free(txn->out_buf); + txn->out_buf = NULL; + return 0; + } + txn->inp_len = rsp_len; + txn->inp_idx = 0; + txn->txn_state = 0; + txn->txn_status = 0; + return 1; +} + +static const char* hex = "0123456789ABCDEF"; + +/*--------------------------------------------------------------------*/ +static void encodeTerminator(char *result, char *terminator) +{ + if (terminator) + while (*terminator) { + *result++ = '0'; + *result++ = 'x'; + *result++ = hex[(*terminator >> 4) &0xF]; + *result++ = hex[(*terminator) &0xF]; + ++terminator; + } + *result = '\0'; + return; +} + +static int fromHex(const char* code) { + int icode = -1; + int result = -1; + if (code[0] == '0' && (code[1] == 'x' || code[1] == 'X')) { + if (code[2] >= '0' && code[2] <= '9') + icode = (code[2] - '0'); + else if (code[2] >= 'a' && code[2] <= 'f') + icode = 10 + (code[2] - 'a'); + else if (code[2] >= 'A' && code[2] <= 'F') + icode = 10 + (code[2] - 'A'); + if (icode < 0) + return -1; + result = icode << 4; + icode = -1; + if (code[3] >= '0' && code[3] <= '9') + icode = (code[3] - '0'); + else if (code[3] >= 'a' && code[3] <= 'f') + icode = 10 + (code[3] - 'a'); + else if (code[3] >= 'A' && code[3] <= 'F') + icode = 10 + (code[3] - 'A'); + if (icode < 0) + return -1; + result |= icode; + return result; + } + return -1; +} +/*--------------------------------------------------------------------*/ +static char *decodeTerminator(char *code) +{ + int count = 0, icode; + char *pResult; + char* pCh; + char* pQt = NULL; /* pointer to quote character if found */ + + if (code == NULL) + return NULL; + count = strlen(code); + pResult = (char *) malloc(count + 1); + if (!pResult) { + SICSLogWrite("Out of memory in AsyncProtocol::decodeTerminator", eError); + return NULL; + } + memset(pResult, 0, count + 1); + + pCh = pResult; + if (*code == '\'' || *code == '"') /* check for leading quote */ + pQt = code++; + + while (*code) { + if (pQt && *code == *pQt) /* check for trailing quote */ + break; + + if (code[0] == '\\' && code[1] == 'r') { /* CR */ + *pCh++ = '\r'; + code += 2; + } + else if (code[0] == '\\' && code[1] == 'n') { /* LF */ + *pCh++ = '\n'; + code += 2; + } + else if ((icode = fromHex(code)) >= 0) { /* Hex: 0xFF */ + *pCh++ = icode; + code += 4; + } + else /* literal */ + *pCh++ = *code++; + } + *pCh = '\0'; + + return pResult; +} +int AsyncProtocolNoAction(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]) +{ + char line[132]; + pAsyncProtocol self = (pAsyncProtocol) pData; + snprintf(line, 132, "%s does not understand %s", argv[0], argv[1]); + SCWrite(pCon, line, eError); + return 0; +} + +int AsyncProtocolAction(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]) +{ + char line[132]; + pAsyncProtocol self = (pAsyncProtocol) pData; + if (argc > 1) { + /* handle genecic parameters like terminators */ + if (strcasecmp(argv[1], "sendterminator") == 0) { + if (argc > 2) { + char* pPtr = decodeTerminator(argv[2]); + if (pPtr) { + if (self->sendTerminator) + free(self->sendTerminator); + self->sendTerminator = pPtr; + } + SCSendOK(pCon); + } + else + { + char term[132]; + char line[1024]; + encodeTerminator(term, self->sendTerminator); + sprintf(line, "%s.sendTerminator = \"%s\"", argv[0], term); + SCWrite(pCon, line, eValue); + } + return 1; + } + else if (strcasecmp(argv[1], "replyterminator") == 0) { + if (argc > 2) { + char* pPtr = decodeTerminator(argv[2]); + if (pPtr) { + if (self->replyTerminator) + free(self->replyTerminator); + self->replyTerminator = pPtr; + } + SCSendOK(pCon); + } + else + { + char term[132]; + char line[1024]; + encodeTerminator(term, self->replyTerminator); + sprintf(line, "%s.replyTerminator = \"%s\"", argv[0], term); + SCWrite(pCon, line, eValue); + } + return 1; + } + } + else if (strcasecmp(argv[1], "list") == 0) { + int ac = 2; + char* av[3] = { argv[0], 0, 0 }; + av[1] = "sendterminator"; + AsyncProtocolAction(pCon, pSics, pData, ac, av); + av[1] = "replyterminator"; + AsyncProtocolAction(pCon, pSics, pData, ac, av); + return 1; + } + /* handle any other actions here */ + return AsyncProtocolNoAction(pCon, pSics, pData, argc,argv); +} + +void defaultKillPrivate(pAsyncProtocol p) { + if (p->privateData) { + /* TODO: should we do anything? */ + free(p->privateData); + } +} + +void AsyncProtocolKill(void *pData) { + pAsyncProtocol self = (pAsyncProtocol) pData; + if(self->pDes) + DeleteDescriptor(self->pDes); + if(self->sendTerminator != NULL) + free(self->sendTerminator); + if(self->replyTerminator != NULL) + free(self->replyTerminator); + if (self->killPrivate) + self->killPrivate(self); +} + +pAsyncProtocol AsyncProtocolCreate(SicsInterp *pSics, const char* protocolName, + ObjectFunc pFunc, KillFunc pKFunc) { + int iRet; + pAsyncProtocol self = NULL; + + /* try to find an existing queue with this name */ + self = (pAsyncProtocol) FindCommandData(pServ->pSics,(char *)protocolName, "AsyncProtocol"); + if (self != NULL) { + return self; + } + + self = (pAsyncProtocol) malloc(sizeof(AsyncProtocol)); + if (self == NULL) { + SICSLogWrite("Out of memory in AsyncProtocolCreate", eError); + return NULL; + } + memset(self, 0, sizeof(AsyncProtocol)); + self->pDes = CreateDescriptor("AsyncProtocol"); + if (pFunc == NULL) + pFunc = AsyncProtocolNoAction; + if (pKFunc == NULL) + pKFunc = AsyncProtocolKill; + iRet = AddCommand(pSics, (char *)protocolName, pFunc, pKFunc, self); + if (!iRet ) { + SICSLogWrite("AddCommand failed in AsyncProtocolCreate", eError); + AsyncProtocolKill(self); + return NULL; + } + self->sendCommand = defaultSendCommand; + self->handleInput = defaultHandleInput; + self->handleEvent = defaultHandleEvent; + self->prepareTxn = defaultPrepareTxn; + self->killPrivate = defaultKillPrivate; + self->sendTerminator = strdup("\r\n"); + self->replyTerminator = strdup("\r\n"); + return self; +} + +int AsyncProtocolFactory(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]) { + if (argc < 2) { + SCWrite(pCon,"ERROR: insufficient arguments to AsyncProtocolFactory", eError); + return 0; + } + pAsyncProtocol pNew = AsyncProtocolCreate(pSics, argv[1], + AsyncProtocolAction, AsyncProtocolKill); + /* handle any extra arguments here */ + pNew->privateData = NULL; + return 1; +} diff --git a/asyncprotocol.h b/asyncprotocol.h new file mode 100644 index 00000000..528dcb98 --- /dev/null +++ b/asyncprotocol.h @@ -0,0 +1,62 @@ +#ifndef ASYNCPROTOCOL +#define ASYNCPROTOCOL + +typedef struct __AsyncUnit AsyncUnit, *pAsyncUnit; + +typedef struct __async_txn AsyncTxn, *pAsyncTxn; +typedef int (*AsyncTxnHandler)(pAsyncTxn pTxn); + +typedef struct __async_protocol AsyncProtocol, *pAsyncProtocol; + +pAsyncProtocol AsyncProtocolCreate(SicsInterp *pSics, const char* protocolName, + ObjectFunc pFunc, KillFunc pKFunc); + +int AsyncProtocolAction(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]); + +int AsyncProtocolFactory(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]); + +typedef enum { + ATX_NULL=0, + ATX_TIMEOUT=-1, + ATX_ACTIVE=1, + ATX_COMPLETE=2, + ATX_DISCO=3 +} ATX_STATUS; + +struct __async_txn { + pAsyncUnit unit; /**< unit that transaction is associated with */ + int txn_state; /**< protocol handler transaction parse state */ + ATX_STATUS txn_status; /**< status of the transaction OK, Error, ... */ + int txn_timeout; /**< transaction timeout in milliseconds */ + char* out_buf; /**< output buffer for sendCommand */ + int out_len; /**< length of data to be sent */ + int out_idx; /**< index of next character to transmit */ + char* inp_buf; /**< input buffer for transaction response */ + int inp_len; /**< length of input buffer */ + int inp_idx; /**< index of next character (number already received) */ + AsyncTxnHandler handleResponse; /**< Txn response handler of command sender */ + void* cntx; /**< opaque context used by command sender */ + /* The cntx field may be used by protocol handler from sendCommand + * as long as it is restored when response is complete + */ +}; + +/* + * The async protocol interface virtual function table + */ +struct __async_protocol { + pObjectDescriptor pDes; + char* protocolName; + char *sendTerminator; + char *replyTerminator; + void* privateData; + int (* sendCommand)(pAsyncProtocol p, pAsyncTxn txn); + int (* handleInput)(pAsyncProtocol p, pAsyncTxn txn, int ch); + int (* handleEvent)(pAsyncProtocol p, pAsyncTxn txn, int event); + int (* prepareTxn)(pAsyncProtocol p, pAsyncTxn txn, const char* cmd, int cmd_len, int rsp_len); + void (* killPrivate)(pAsyncProtocol p); +}; + +#endif /* ASYNCPROTOCOL */ diff --git a/asyncqueue.c b/asyncqueue.c new file mode 100644 index 00000000..a86f561e --- /dev/null +++ b/asyncqueue.c @@ -0,0 +1,987 @@ +/* + * A S Y N C Q U E U E + * + * This module manages AsyncQueue communications. + * + * The AsyncQueue is an asynchronous queue between drivers and the device. It + * supports multiple logical units on a single device controller that share a + * single command channel. + * + * Douglas Clowes, February 2007 + * + */ + +#include +#include +#include +#include +#include +#include +#include "network.h" +#include "asyncqueue.h" +#include "nwatch.h" + + +typedef struct __async_command AQ_Cmd, *pAQ_Cmd; + +struct __async_command { + pAQ_Cmd next; + pAsyncTxn tran; + pAsyncUnit unit; + int timeout; + int retries; + int active; +}; + +struct __AsyncUnit { + pAsyncUnit next; + pAsyncQueue queue; + AQU_Notify notify_func; + void* notify_cntx; +}; + +struct __AsyncQueue { + pObjectDescriptor pDes; + char* queue_name; + char* pHost; + int iPort; + int iDelay; /* intercommand delay in milliseconds */ + int timeout; + int retries; + struct timeval tvLastCmd; /* time of completion of last command */ + int unit_count; /* number of units connected */ + pAsyncUnit units; /* head of unit chain */ + pAQ_Cmd command_head; /* first/next command in queue */ + pAQ_Cmd command_tail; /* last command in queue */ + pNWContext nw_ctx; /* NetWait context handle */ + pNWTimer nw_tmr; /* NetWait timer handle */ + mkChannel* pSock; /* socket address */ + pAsyncProtocol protocol; +}; + +static pAsyncQueue queue_array[FD_SETSIZE]; +static int queue_index = 0; + +/* ---------------------------- Local ------------------------------------ + CreateSocketAdress stolen from Tcl. Thanks to John Ousterhout +*/ + +static int +CreateSocketAdress( + struct sockaddr_in *sockaddrPtr, /* Socket address */ + char *host, /* Host. NULL implies INADDR_ANY */ + int port) /* Port number */ +{ + struct hostent *hostent; /* Host database entry */ + struct in_addr addr; /* For 64/32 bit madness */ + + (void) memset((char *) sockaddrPtr, '\0', sizeof(struct sockaddr_in)); + sockaddrPtr->sin_family = AF_INET; + sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF)); + if (host == NULL) { + addr.s_addr = INADDR_ANY; + } else { + hostent = gethostbyname(host); + if (hostent != NULL) { + memcpy((char *) &addr, + (char *) hostent->h_addr_list[0], (size_t) hostent->h_length); + } else { + addr.s_addr = inet_addr(host); + if (addr.s_addr == (unsigned long)-1) { + return 0; /* error */ + } + } + } + /* + * There is a rumor that this assignment may require care on + * some 64 bit machines. + */ + + sockaddrPtr->sin_addr.s_addr = addr.s_addr; + return 1; +} + +static void AQ_Notify(pAsyncQueue self, int event) +{ + pAsyncUnit unit; + for (unit = self->units; unit; unit = unit->next) + if (unit->notify_func != NULL) + unit->notify_func(unit->notify_cntx, event); +} + +static int AQ_Reconnect(pAsyncQueue self) +{ + int iRet; + int sock; + int flag = 1; + char line[132]; + + iRet = NETReconnect(self->pSock); + if (iRet <= 0) { + snprintf(line, 132, "Disconnect on AsyncQueue '%s'", self->queue_name); + SICSLogWrite(line, eStatus); + AQ_Notify(self, AQU_DISCONNECT); + return iRet; + } + snprintf(line, 132, "Reconnect on AsyncQueue '%s'", self->queue_name); + SICSLogWrite(line, eStatus); + AQ_Notify(self, AQU_RECONNECT); + return 1; +} + +static int CommandTimeout(void* cntx, int mode); +static int DelayedStart(void* cntx, int mode); +static int PopCommand(pAsyncQueue self); + +static int StartCommand(pAsyncQueue self) +{ + pAQ_Cmd myCmd = self->command_head; + mkChannel* sock = self->pSock; + + if (myCmd == NULL) + return OKOK; + + /* + * Remove any old command timeout timer + */ + if (self->nw_tmr) + NetWatchRemoveTimer(self->nw_tmr); + + /* + * Implement the inter-command delay + */ + if (self->iDelay) { + struct timeval now, when; + gettimeofday(&now, NULL); + if (self->tvLastCmd.tv_sec == 0) + self->tvLastCmd = now; + when.tv_sec = self->tvLastCmd.tv_sec; + when.tv_usec = self->tvLastCmd.tv_usec + 1000 * self->iDelay; + if (when.tv_usec >= 1000000) { + when.tv_sec += when.tv_usec / 1000000; + when.tv_usec %= 1000000; + } + if (when.tv_sec > now.tv_sec || + (when.tv_sec == now.tv_sec && when.tv_usec > now.tv_usec)) { + int delay = when.tv_sec - now.tv_sec; + delay *= 1000; + delay += (when.tv_usec - now.tv_usec + (1000 - 1)) / 1000; + NetWatchRegisterTimer(&self->nw_tmr, delay, + DelayedStart, self); + return OKOK; + } + } + + /* + * Discard any input before sending command + */ + while (NETAvailable(sock, 0)) { + /* TODO: handle unsolicited input */ + char reply[1]; + int iRet; + iRet = NETRead(sock, reply, 1, 0); + if (iRet < 0) { /* EOF */ + iRet = AQ_Reconnect(self); + if (iRet <= 0) { + myCmd->tran->txn_state = ATX_DISCO; + if(myCmd->tran->handleResponse){ + myCmd->tran->handleResponse(myCmd->tran); + } + PopCommand(self); + return 0; + } + } + } + /* + * Add a new command timeout timer + */ + if (myCmd->timeout > 0) + NetWatchRegisterTimer(&self->nw_tmr, myCmd->timeout, + CommandTimeout, self); + else + NetWatchRegisterTimer(&self->nw_tmr, 30000, + CommandTimeout, self); + myCmd->active = 1; + return self->protocol->sendCommand(self->protocol, myCmd->tran); +} + +static int QueCommandHead(pAsyncQueue self, pAQ_Cmd cmd) +{ + cmd->next = NULL; + /* + * If the command queue is empty, start transmission + */ + if (self->command_head == NULL) { + self->command_head = self->command_tail = cmd; + StartCommand(self); + return 1; + } + if (self->command_head->active) { + cmd->next = self->command_head->next; + self->command_head->next = cmd; + } + else { + cmd->next = self->command_head; + self->command_head = cmd; + } + if (cmd->next == NULL) + self->command_tail = cmd; + return 1; +} + +static int QueCommand(pAsyncQueue self, pAQ_Cmd cmd) +{ + cmd->next = NULL; + /* + * If the command queue is empty, start transmission + */ + if (self->command_head == NULL) { + self->command_head = self->command_tail = cmd; + StartCommand(self); + return 1; + } + self->command_tail->next = cmd; + self->command_tail = cmd; + return 1; +} +static int PopCommand(pAsyncQueue self) +{ + pAQ_Cmd myCmd = self->command_head; + if (self->nw_tmr) + NetWatchRemoveTimer(self->nw_tmr); + self->nw_tmr = 0; + gettimeofday(&self->tvLastCmd, NULL); + /* + * If this is not the last in queue, start transmission + */ + if (myCmd->next) { + pAQ_Cmd pNew = myCmd->next; + self->command_head = pNew; + StartCommand(self); + } + else + self->command_head = self->command_tail = NULL; + free(myCmd->tran->out_buf); + free(myCmd->tran->inp_buf); + free(myCmd->tran); + free(myCmd); + return 1; +} + +static int CommandTimeout(void* cntx, int mode) +{ + pAsyncQueue self = (pAsyncQueue) cntx; + pAQ_Cmd myCmd = self->command_head; + self->nw_tmr = 0; + if (myCmd->retries > 0) { + --myCmd->retries; + StartCommand(self); + } + else { + int iRet; + iRet = self->protocol->handleEvent(self->protocol, myCmd->tran, AQU_TIMEOUT); + if (iRet == AQU_POP_CMD) { + if (myCmd->tran->handleResponse) + myCmd->tran->handleResponse(myCmd->tran); + PopCommand(self); /* remove command */ + } + else if (iRet == AQU_RETRY_CMD) + StartCommand(self); /* restart command */ + else if (iRet == AQU_RECONNECT) + AQ_Reconnect(self); + } + return 1; +} + +static int DelayedStart(void* cntx, int mode) +{ + pAsyncQueue self = (pAsyncQueue) cntx; + self->nw_tmr = 0; + StartCommand(self); + return 1; +} + +static int MyCallback(void* context, int mode) +{ + pAsyncQueue self = (pAsyncQueue) context; + + if (mode & nwatch_read) { + int iRet; + char reply[1]; + + iRet = NETRead(self->pSock, reply, 1, 0); + /* printf(" iRet, char = %d, %d\n", iRet, (int)reply[0]); */ + if (iRet < 0) { /* EOF */ + iRet = AQ_Reconnect(self); + if (iRet <= 0){ + /* changed to call handleResponse with a bad status code: MK + */ + pAQ_Cmd myCmd = self->command_head; + if(myCmd){ + myCmd->tran->txn_state = ATX_DISCO; + if(myCmd->tran->handleResponse){ + myCmd->tran->handleResponse(myCmd->tran); + } + PopCommand(self); + } + return iRet; + } + /* restart the command */ + StartCommand(self); + return 1; + } + if (iRet == 0) { /* TODO: timeout or error */ + return 0; + } else { + pAQ_Cmd myCmd = self->command_head; + if (myCmd) { + iRet = self->protocol->handleInput(self->protocol, myCmd->tran, reply[0]); + if (iRet == 0 || iRet == AQU_POP_CMD) { /* end of command */ + if (myCmd->tran->handleResponse) + myCmd->tran->handleResponse(myCmd->tran); + PopCommand(self); + } + else if (iRet < 0) /* TODO: error */ + ; + } + else { + /* TODO: handle unsolicited input */ + } + } + } + return 1; +} + +int AsyncUnitEnqueueHead(pAsyncUnit unit, pAsyncTxn context) +{ + pAQ_Cmd myCmd = NULL; + + assert(unit && unit->queue && unit->queue->protocol); + myCmd = (pAQ_Cmd) malloc(sizeof(AQ_Cmd)); + if (myCmd == NULL) { + SICSLogWrite("ERROR: Out of memory in AsyncUnitEnqueHead", eError); + return 0; + } + memset(myCmd, 0, sizeof(AQ_Cmd)); + myCmd->tran = context; + myCmd->unit = unit; + myCmd->timeout = unit->queue->timeout; + myCmd->retries = unit->queue->retries; + myCmd->active = 0; + return QueCommandHead(unit->queue, myCmd); +} + +int AsyncUnitEnqueueTxn(pAsyncUnit unit, pAsyncTxn pTxn) +{ + pAQ_Cmd myCmd = NULL; + + assert(unit && unit->queue && unit->queue->protocol); + myCmd = (pAQ_Cmd) malloc(sizeof(AQ_Cmd)); + if (myCmd == NULL) { + SICSLogWrite("ERROR: Out of memory in AsyncUnitEnqueueTxn", eError); + return 0; + } + memset(myCmd, 0, sizeof(AQ_Cmd)); + myCmd->tran = pTxn; + myCmd->unit = unit; + myCmd->timeout = unit->queue->timeout; + myCmd->retries = unit->queue->retries; + myCmd->active = 0; + return QueCommand(unit->queue, myCmd); +} + +pAsyncTxn AsyncUnitPrepareTxn(pAsyncUnit unit, + const char* command, int cmd_len, + AsyncTxnHandler callback, void* context, + int rsp_len) +{ + pAsyncTxn myTxn = NULL; + + assert(unit); + myTxn = (pAsyncTxn) malloc(sizeof(AsyncTxn)); + if (myTxn == NULL) { + SICSLogWrite("ERROR: Out of memory in AsyncUnitPrepareTxn", eError); + return 0; + } + memset(myTxn, 0, sizeof(AsyncTxn)); + if (unit->queue->protocol->prepareTxn) { + int iRet; + iRet = unit->queue->protocol->prepareTxn(unit->queue->protocol, myTxn, command, cmd_len, rsp_len); + } + else { + myTxn->out_buf = (char*) malloc(cmd_len + 5); + if (myTxn->out_buf == NULL) { + SICSLogWrite("ERROR: Out of memory in AsyncUnitPrepareTxn", eError); + free(myTxn); + return 0; + } + memcpy(myTxn->out_buf, command, cmd_len); + myTxn->out_len = cmd_len; + if (myTxn->out_len < 2 || + myTxn->out_buf[myTxn->out_len - 1] != 0x0A || + myTxn->out_buf[myTxn->out_len - 2] != 0x0D) { + myTxn->out_buf[myTxn->out_len++] = 0x0D; + myTxn->out_buf[myTxn->out_len++] = 0x0A; + } + myTxn->out_buf[myTxn->out_len] = '\0'; + } + if (rsp_len == 0) + myTxn->inp_buf = NULL; + else { + if(myTxn->inp_buf != NULL){ + free(myTxn->inp_buf); + } + myTxn->inp_buf = malloc(rsp_len + 1); + if (myTxn->inp_buf == NULL) { + SICSLogWrite("ERROR: Out of memory in AsyncUnitPrepareTxn", eError); + free(myTxn->out_buf); + free(myTxn); + return 0; + } + memset(myTxn->inp_buf, 0, rsp_len + 1); + } + myTxn->inp_len = rsp_len; + myTxn->unit = unit; + myTxn->handleResponse = callback; + myTxn->cntx = context; + return myTxn; +} + +int AsyncUnitSendTxn(pAsyncUnit unit, + const char* command, int cmd_len, + AsyncTxnHandler callback, void* context, + int rsp_len) +{ + pAsyncTxn myTxn = NULL; + myTxn = AsyncUnitPrepareTxn(unit, command, cmd_len, + callback, context, rsp_len); + if (myTxn == NULL) + return -1; + return AsyncUnitEnqueueTxn(unit, myTxn); +} + + +typedef struct txn_s { + char* transReply; + int transWait; +} TXN, *pTXN; + +/** + * \brief TransCallback is the callback for the general command transaction. + */ +static int TransCallback(pAsyncTxn pCmd) { + char* resp = pCmd->inp_buf; + int resp_len = pCmd->inp_idx; + pTXN self = (pTXN) pCmd->cntx; + + if (pCmd->txn_status == ATX_TIMEOUT) { + memcpy(self->transReply, resp, resp_len); + self->transReply[resp_len] = '\0'; + self->transReply[0] = '\0'; + self->transWait = -1; + } + else { + memcpy(self->transReply, resp, resp_len); + self->transReply[resp_len] = '\0'; + self->transWait = 0; + } + return 0; +} + +int AsyncUnitTransact(pAsyncUnit unit, + const char* command, int cmd_len, + char* response, int rsp_len) +{ + TXN txn; + assert(unit); + txn.transReply = response; + txn.transWait = 1; + AsyncUnitSendTxn(unit, + command, cmd_len, + TransCallback, &txn, rsp_len); + while (txn.transWait == 1) + TaskYield(pServ->pTasker); + if (txn.transWait < 0) + return txn.transWait; + return 1; +} + +int AsyncUnitWrite(pAsyncUnit unit, void* buffer, int buflen) +{ + int iRet; + mkChannel* sock; + assert(unit); + assert(unit->queue); + if (buflen > 0) { + sock = AsyncUnitGetSocket(unit); + iRet = NETWrite(sock, buffer, buflen); + /* TODO handle errors */ + if (iRet < 0) { /* EOF */ + iRet = AQ_Reconnect(unit->queue); + if (iRet == 0) + return 0; + } + } + return 1; +} + +void AsyncUnitSetNotify(pAsyncUnit unit, void* context, AQU_Notify notify) +{ + assert(unit); + unit->notify_func = notify; + unit->notify_cntx = context; +} + +int AsyncUnitGetDelay(pAsyncUnit unit) +{ + assert(unit); + return unit->queue->iDelay; +} + +void AsyncUnitSetDelay(pAsyncUnit unit, int iDelay) +{ + assert(unit); + unit->queue->iDelay = iDelay; +} + +int AsyncUnitGetTimeout(pAsyncUnit unit) +{ + assert(unit); + return unit->queue->timeout; +} + +void AsyncUnitSetTimeout(pAsyncUnit unit, int timeout) +{ + assert(unit); + unit->queue->timeout = timeout; +} + +int AsyncUnitGetRetries(pAsyncUnit unit) +{ + assert(unit); + return unit->queue->retries; +} + +void AsyncUnitSetRetries(pAsyncUnit unit, int retries) +{ + assert(unit); + unit->queue->retries = retries; +} + +pAsyncProtocol AsyncUnitGetProtocol(pAsyncUnit unit) +{ + return unit->queue->protocol; +} + +void AsyncUnitSetProtocol(pAsyncUnit unit, pAsyncProtocol protocol) +{ + unit->queue->protocol = protocol; +} + +mkChannel* AsyncUnitGetSocket(pAsyncUnit unit) +{ + assert(unit); + assert(unit->queue); + return unit->queue->pSock; +} + +int AsyncUnitReconnect(pAsyncUnit unit) +{ + int iRet; + assert(unit); + assert(unit->queue); + iRet = AQ_Reconnect(unit->queue); + /* TODO: handle in-progress */ + return iRet; +} + +int AsyncQueueAction(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]) +{ + char line[132]; + pAsyncQueue self = (pAsyncQueue) pData; + if (argc > 1) { + if (strcasecmp("send", argv[1]) == 0) { + AsyncUnit myUnit; + char cmd[10240]; + char rsp[10240]; + int idx = 0; + int i, j; + cmd[0] = '\0'; + for (i = 2; i < argc; ++i) { + j = snprintf(&cmd[idx], 10240 - idx, "%s%s", + (i > 2) ? " " : "", + argv[i]); + if (j < 0) + break; + idx += j; + } + memset(&myUnit, 0, sizeof(AsyncUnit)); + myUnit.queue = self; + AsyncUnitTransact(&myUnit, cmd, idx, rsp, 10240); + SCWrite(pCon, rsp, eValue); + return 1; + } + if (strcasecmp(argv[1], "reconnect") == 0) { + AQ_Reconnect(self); + return OKOK; + } + if (strcasecmp(argv[1], "delay") == 0) { + if (argc > 2) { + int delay; + int iRet; + iRet = sscanf(argv[2], "%d", &delay); + if (iRet != 1) { + snprintf(line, 132, "Invalid argument: %s", argv[2]); + SCWrite(pCon, line, eError); + return 0; + } + else { + if (delay < 0 || delay > 30000) { + snprintf(line, 132, "Value out of range: %d", delay); + SCWrite(pCon, line, eError); + return 0; + } + self->iDelay = delay; + return OKOK; + } + } + else { + snprintf(line, 132, "%s.delay = %d", argv[0], self->iDelay); + SCWrite(pCon, line, eStatus); + return OKOK; + } + return OKOK; + } + if (strcasecmp(argv[1], "timeout") == 0) { + if (argc > 2) { + int timeout; + int iRet; + iRet = sscanf(argv[2], "%d", &timeout); + if (iRet != 1) { + snprintf(line, 132, "Invalid argument: %s", argv[2]); + SCWrite(pCon, line, eError); + return 0; + } + else { + if (timeout < 0 || timeout > 30000) { + snprintf(line, 132, "Value out of range: %d", timeout); + SCWrite(pCon, line, eError); + return 0; + } + self->timeout = timeout; + return OKOK; + } + } + else { + snprintf(line, 132, "%s.timeout = %d", argv[0], self->timeout); + SCWrite(pCon, line, eStatus); + return OKOK; + } + return OKOK; + } + if (strcasecmp(argv[1], "retries") == 0) { + if (argc > 2) { + int retries; + int iRet; + iRet = sscanf(argv[2], "%d", &retries); + if (iRet != 1) { + snprintf(line, 132, "Invalid argument: %s", argv[2]); + SCWrite(pCon, line, eError); + return 0; + } + else { + if (retries < 0 || retries > 30000) { + snprintf(line, 132, "Value out of range: %d", retries); + SCWrite(pCon, line, eError); + return 0; + } + self->retries = retries; + return OKOK; + } + } + else { + snprintf(line, 132, "%s.retries = %d", argv[0], self->retries); + SCWrite(pCon, line, eStatus); + return OKOK; + } + return OKOK; + } + } + snprintf(line, 132, "%s does not understand %s", argv[0], argv[1]); + SCWrite(pCon, line, eError); + return 0; +} + +static pAsyncQueue AQ_Create(const char* host, const char* port) +{ + int i; + pAsyncQueue self = NULL; + mkChannel* channel = NULL; + + if (host == NULL) + return NULL; + + /* try the AsyncQueue with this name */ + self = (pAsyncQueue) FindCommandData(pServ->pSics,(char *) host, "AsyncQueue"); + + /* try host and port */ + if (self == NULL && port) { + int port_no = atoi(port); + if (port_no == 0) { + struct servent *sp=NULL; + sp = getservbyname(port, NULL); + if (sp) + port_no = ntohs(sp->s_port); + } + if (port_no > 0) { + struct sockaddr_in sa; + if (CreateSocketAdress(&sa,(char *) host, port_no)) { + /* look for queue with same address */ + for (i = 0; i < queue_index; ++i) + if (queue_array[i]->pSock->adresse.sin_port == sa.sin_port + && queue_array[i]->pSock->adresse.sin_addr.s_addr == sa.sin_addr.s_addr) { + self = queue_array[i]; + break; + } + } + if (self == NULL) { + channel = NETConnectWithFlags((char *)host, port_no, 0); + /* TODO handle asynchronous connection */ + } + } + } + + if (self == NULL) { + if (channel == NULL) + return NULL; + + self = (pAsyncQueue) malloc(sizeof(AsyncQueue)); + if (self == NULL) + return NULL; + memset(self, 0, sizeof(AsyncQueue)); + self->pSock = channel; + self->pDes = CreateDescriptor("AsyncQueue"); + queue_array[queue_index++] = self; + } + + for (i = 0; i < queue_index; ++i) + if (queue_array[i] == self) { + break; + } + if (i == queue_index) + queue_array[queue_index++] = self; + + return self; +} + +static int AQ_Init(pAsyncQueue self) +{ + /* Init the controller */ + if (self->nw_ctx == NULL) + NetWatchRegisterCallback(&self->nw_ctx, + self->pSock->sockid, + MyCallback, + self); + return 1; +} + +static void AQ_Kill(void* pData) +{ + int i; + pAsyncQueue self = (pAsyncQueue) pData; + for (i = 0; i < queue_index; ++i) + if (queue_array[i] == self) { + --queue_index; + if (queue_index > 0) + queue_array[i] = queue_array[queue_index]; + if (self->nw_ctx) + NetWatchRemoveCallback(self->nw_ctx); + if (self->nw_tmr) + NetWatchRemoveTimer(self->nw_tmr); + if (self->queue_name) + free(self->queue_name); + NETClosePort(self->pSock); + free(self->pSock); + DeleteDescriptor(self->pDes); + free(self); + return; + } +} + +/* + * \brief make a AsyncQueue from the command line + * + * MakeAsyncQueue queueName protocolName hostName portname + */ +int AsyncQueueFactory(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]) +{ + pAsyncQueue pNew = NULL; + mkChannel* channel = NULL; + pAsyncProtocol pPro = NULL; + int port_no; + int iRet = 0; + + if (argc < 5) { + SCWrite(pCon,"ERROR: insufficient arguments to AsyncQueueFactory", eError); + return 0; + } + + /* try to find an existing queue with this name */ + pNew = (pAsyncQueue) FindCommandData(pServ->pSics, argv[1], "AsyncQueue"); + if (pNew != NULL) { + char line[132]; + snprintf(line, 132, "WARNING: AsyncQueue '%s' already exists", argv[1]); + SCWrite(pCon, line, eError); + SCSendOK(pCon); + return 1; + } + + /* try to find an existing protocol with this name */ + pPro = (pAsyncProtocol) FindCommandData(pServ->pSics, argv[2], "AsyncProtocol"); + if (pPro == NULL) { + char line[132]; + snprintf(line, 132, "WARNING: AsyncQueue protocol '%s' not found", argv[2]); + SCWrite(pCon, line, eError); + return 0; + } + + port_no = atoi(argv[4]); + if (port_no == 0) { + struct servent *sp=NULL; + sp = getservbyname(argv[4], NULL); + if (sp) + port_no = ntohs(sp->s_port); + } + if (port_no > 0) { + struct sockaddr_in sa; + if (CreateSocketAdress(&sa, argv[3], port_no)) { + int i; + /* look for queue with same address */ + for (i = 0; i < queue_index; ++i) + if (queue_array[i]->pSock->adresse.sin_port == sa.sin_port + && queue_array[i]->pSock->adresse.sin_addr.s_addr == sa.sin_addr.s_addr) { + char line[132]; + snprintf(line, 132, "WARNING: AsyncQueue '%s' has same address as %s", + argv[1], + queue_array[i]->queue_name); + SCWrite(pCon, line, eError); + } + } + /* TODO: implement asynchronous connection */ + channel = NETConnectWithFlags(argv[3], port_no, 0); + } + + if (channel == NULL) { + char line[132]; + snprintf(line, 132, "ERROR: AsyncQueue '%s' cannot connect", argv[1]); + SCWrite(pCon, line, eError); + return 0; + } + + pNew = (pAsyncQueue) malloc(sizeof(AsyncQueue)); + if (pNew == NULL) { + char line[132]; + snprintf(line, 132, "ERROR: AsyncQueue '%s' memory failure", argv[1]); + SCWrite(pCon, line, eError); + return 0; + } + + memset(pNew, 0, sizeof(AsyncQueue)); + pNew->pDes = CreateDescriptor("AsyncQueue"); + pNew->queue_name = strdup(argv[1]); + pNew->protocol = pPro; + pNew->pSock = channel; + queue_array[queue_index++] = pNew; + + AQ_Init(pNew); + + /* + create the command + */ + iRet = AddCommand(pSics, argv[1], AsyncQueueAction, AQ_Kill, pNew); + if(!iRet) + { + char line[132]; + snprintf(line, 123, "ERROR: add command %s failed", argv[1]); + SCWrite(pCon, line, eError); + AQ_Kill(pNew); + return 0; + } + SCSendOK(pCon); + return 1; +} + +/* + * \brief make a AsyncQueue from a named rs232 controller + * + * \param name the name of the SICS "RS232 Controller" object + * \param handle the handle to the AsyncQueue object + * \return 0 for FAILURE, 1 for SUCCESS + */ +int AsyncUnitCreateHost(const char* host, const char* port, pAsyncUnit* handle) +{ + int status; + pAsyncQueue self = NULL; + pAsyncUnit unit = NULL; + + *handle = NULL; + + self = AQ_Create(host, port); + if (self == NULL) + return 0; + status = AQ_Init(self); + + unit = (pAsyncUnit) malloc(sizeof(AsyncUnit)); + if (unit == NULL) { + SICSLogWrite("ERROR: Out of memory in AsyncUnitCreateHost", eError); + *handle = NULL; + return 0; + } + memset(unit, 0, sizeof(AsyncUnit)); + ++self->unit_count; + unit->queue = self; + unit->next = self->units; + self->units = unit; + *handle = unit; + return 1; +} + +int AsyncUnitCreate(const char* host, pAsyncUnit* handle) { + return AsyncUnitCreateHost(host, NULL, handle); +} + +int AsyncUnitDestroy(pAsyncUnit unit) +{ + assert(unit); + assert(unit->queue); + pAsyncQueue self = unit->queue; + pAsyncUnit* pNxt = &self->units; + while (*pNxt) { + if (*pNxt == unit) { + *pNxt = (*pNxt)->next; + break; + } + pNxt = &(*pNxt)->next; + } + --self->unit_count; + if (self->unit_count <= 0) { + AQ_Kill(self); + } + free(unit); + return 1; +} + +pAsyncUnit AsyncUnitFromQueue(pAsyncQueue queue){ + pAsyncUnit result = NULL; + + result = malloc(sizeof(AsyncUnit)); + if(result == NULL){ + return NULL; + } + memset(result,0,sizeof(AsyncUnit)); + result->queue = queue; + return result; +} + diff --git a/asyncqueue.h b/asyncqueue.h new file mode 100644 index 00000000..4f5bfc53 --- /dev/null +++ b/asyncqueue.h @@ -0,0 +1,186 @@ +/* + * A S Y N C Q U E U E + * + * This module manages communications on an asynchronous connection. + * + * Douglas Clowes, May 2007 + * + */ +#ifndef SICSASYNCQUEUE +#define SICSASYNCQUEUE + +#include "asyncprotocol.h" + +#define AQU_TIMEOUT -1 +#define AQU_DISCONNECT -2 +#define AQU_RECONNECT -3 +#define AQU_RETRY_CMD -4 +#define AQU_POP_CMD -5 + +typedef struct __AsyncQueue AsyncQueue, *pAsyncQueue; + + +/** \brief create an AsyncUnit attached to a named AsyncQueue. + * + * \param queueName the name of the AsyncQueue to be used + * \param unit pointer to the AsyncUnit created on positive return + * \return positive if successful + */ +int AsyncUnitCreate(const char* queueName, pAsyncUnit* unit); +/** \brief Get an AsyncUnit from a given AsyncQueue + * \param queue The AsyncQueue fro which this AsyncUnit is valid + * \return a new AsyncUnit or NULL on error + */ +pAsyncUnit AsyncUnitFromQueue(pAsyncQueue queue); + +/** \brief create an AsyncUnit attached to an anonymous AsyncQueue. + * + * \param host name or address of the target host + * \param port number or service name on the target host + * \param unit pointer to the AsyncUnit created on positive return + * \return positive if successful + */ +int AsyncUnitCreateHost(const char* host, + const char* port, + pAsyncUnit* unit); + +/** \brief destroys an AsyncUnit + * + * \param unit pointer to the AsyncUnit to be destroyed + */ +int AsyncUnitDestroy(pAsyncUnit unit); + +/** \brief Queue a transaction at the head of the associated AsyncQueue + * + * \param unit AsyncUnit + * \param pTxn pointer to transaction + */ +int AsyncUnitEnqueueHead(pAsyncUnit unit, pAsyncTxn pTxn); + +/** \brief Queue a transaction at the tail of the associated AsyncQueue + * + * \param unit AsyncUnit + * \param pTxn pointer to transaction + */ +int AsyncUnitEnqueueTxn(pAsyncUnit unit, pAsyncTxn pTxn); + +/** \brief prepare a transaction according to the protocol (default is CRLF) + * + * \param unit AsyncUnit + * \param command text string to be sent + * \param cmd_len length of data in command + * \param responseHandler function to handle the response + * \param context to be used by handler function + * \param resp_len maximum length to be allowed for response + */ +pAsyncTxn AsyncUnitPrepareTxn(pAsyncUnit unit, + const char* command, int cmd_len, + AsyncTxnHandler responseHandler, void* context, + int rsp_len); + +/** \brief prepare and queue a transaction + * + * \param unit AsyncUnit + * \param command text string to be sent + * \param cmd_len length of data in command + * \param responseHandler function to handle the response + * \param context to be used by handler function + * \param resp_len maximum length to be allowed for response + */ +int AsyncUnitSendTxn(pAsyncUnit unit, + const char* command, int cmd_len, + AsyncTxnHandler responseHandler, void* context, + int rsp_len); + +/** \brief send a transaction and wait for the response + * + * \param unit AsyncUnit + * \param command text string to be sent + * \param cmd_len length of data in command + * \param responseHandler function to handle the response + * \param context to be used by handler function + * \param resp_len maximum length to be allowed for response + */ +int AsyncUnitTransact(pAsyncUnit unit, + const char* command, int cmd_len, + char* response, int rsp_len); + +/** \brief write to the AsyncQueue file descriptor + * + * The data is transmitted directly to the descriptor without being queued. + * This may be used by the protocol transmit function or for retrieving error + * text associated with the current transmission. + * + * \param unit AsyncUnit + * \param data to be transmitted + * \param buflen lenght of data + */ +int AsyncUnitWrite(pAsyncUnit unit, void* buffer, int buflen); + +/** \brief registers a notification callback + * + * The notification callback may notify unsolicited or unusual events + * + * \param unit AsyncUnit + * \param context passed in callback + * \param notify function to be called + */ +typedef void (*AQU_Notify)(void* context, int event); +void AsyncUnitSetNotify(pAsyncUnit unit, void* context, AQU_Notify notify); + +/** \brief get the intertransaction delay in milliseconds + */ +int AsyncUnitGetDelay(pAsyncUnit unit); + +/** \brief set the intertransaction delay in milliseconds + */ +void AsyncUnitSetDelay(pAsyncUnit unit, int iDelay); + +/** \brief get the default transaction timeout in milliseconds + */ +int AsyncUnitGetTimeout(pAsyncUnit unit); + +/** \brief set the default transaction timeout in milliseconds + */ +void AsyncUnitSetTimeout(pAsyncUnit unit, int timeout); + +/** \brief get the number of retries + */ +int AsyncUnitGetRetries(pAsyncUnit unit); + +/** \brief set the number of retries + */ +void AsyncUnitSetRetries(pAsyncUnit unit, int retries); + +/** \brief get the associated protocol handler + */ +pAsyncProtocol AsyncUnitGetProtocol(pAsyncUnit unit); + +/** \brief set the associated protocol handler + */ +void AsyncUnitSetProtocol(pAsyncUnit unit, pAsyncProtocol protocol); + +/** \brief retrieves the socket/channel associated with the AsyncQueue + * + * \param unit AsyncUnit + * \return channel or NULL + */ +mkChannel* AsyncUnitGetSocket(pAsyncUnit unit); + +/** \brief attempt to reconnect the socket of the associated AsyncQueue + * + * \param unit pointer to AsyncUnit + */ +int AsyncUnitReconnect(pAsyncUnit handle); + +/** \brief create an AsyncQueue from the SICS command MakeAsyncQueue + */ +int AsyncQueueFactory(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]); + +/** \brief SICS command handler for the AsyncQueue object + */ +int AsyncQueueAction(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]); + +#endif /* SICSASYNCQUEUE */ diff --git a/choco.c b/choco.c index 4df78fc8..65ca8d01 100644 --- a/choco.c +++ b/choco.c @@ -99,7 +99,7 @@ if(argc < 2) { - sprintf(pMessage, "ERROR: Ragument required for %s",argv[0]); + sprintf(pMessage, "ERROR: argument required for %s",argv[0]); SCWrite(pCon,pMessage,eError); return 0; } @@ -149,7 +149,7 @@ return 0; } /*----------------------------------------------------------------------*/ - static void KillChoco(void *pData) + void KillChoco(void *pData) { pChoco self = NULL; diff --git a/choco.h b/choco.h index 08be2342..94bbe7e2 100644 --- a/choco.h +++ b/choco.h @@ -19,6 +19,7 @@ pCodri CHGetDriver(pChoco self); int CHList(pChoco self, SConnection *pCon, char *name); /*------------------------------------------------------------------------*/ + void KillChoco(void *pData); int ChocoAction(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); int ChocoFactory(SConnection *pCon, SicsInterp *pSics, void *pData, diff --git a/choco.tex b/choco.tex index 2dc57d2c..d1e00149 100644 --- a/choco.tex +++ b/choco.tex @@ -85,7 +85,7 @@ controller driver. \item[SetPar] tries to set the parameter parname to the value fValue. The last is floating point which covers the frequent occurence of numeric values. -\item[SetPar2] The same as SetPar but uses test string as input for +\item[SetPar2] The same as SetPar but uses text string as input for parameter setting. \item[GetPar] retrieves the parameter parname formatted as text. The value is put into the buffer pBuffer. iBufLen is the maximum number of @@ -129,6 +129,7 @@ $\langle$chocoint {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ pCodri CHGetDriver(pChoco self);@\\ \mbox{}\verb@ int CHList(pChoco self, SConnection *pCon, char *name);@\\ \mbox{}\verb@/*------------------------------------------------------------------------*/@\\ +\mbox{}\verb@ void KillChoco(void *pData);@\\ \mbox{}\verb@ int ChocoAction(SConnection *pCon, SicsInterp *pSics, void *pData,@\\ \mbox{}\verb@ int argc, char *argv[]);@\\ \mbox{}\verb@ int ChocoFactory(SConnection *pCon, SicsInterp *pSics, void *pData,@\\ @@ -366,8 +367,7 @@ $\langle$evada {\footnotesize ?}$\rangle\equiv$ \subsubsection{To Do} This scheme seems to be quite promising for handling many SICS objects. The following enhancements could be considered. Allow to set -certain primitive parameters without a drivable interface. And add an -adapter for an environment variable in the controller. +certain primitive parameters without a drivable interface. diff --git a/choco.w b/choco.w index 10b6be55..2f79a9ee 100644 --- a/choco.w +++ b/choco.w @@ -72,7 +72,7 @@ controller driver. \item[SetPar] tries to set the parameter parname to the value fValue. The last is floating point which covers the frequent occurence of numeric values. -\item[SetPar2] The same as SetPar but uses test string as input for +\item[SetPar2] The same as SetPar but uses text string as input for parameter setting. \item[GetPar] retrieves the parameter parname formatted as text. The value is put into the buffer pBuffer. iBufLen is the maximum number of @@ -111,6 +111,7 @@ includes: pCodri CHGetDriver(pChoco self); int CHList(pChoco self, SConnection *pCon, char *name); /*------------------------------------------------------------------------*/ + void KillChoco(void *pData); int ChocoAction(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); int ChocoFactory(SConnection *pCon, SicsInterp *pSics, void *pData, @@ -267,8 +268,7 @@ controller driver: \subsubsection{To Do} This scheme seems to be quite promising for handling many SICS objects. The following enhancements could be considered. Allow to set -certain primitive parameters without a drivable interface. And add an -adapter for an environment variable in the controller. +certain primitive parameters without a drivable interface. diff --git a/commandlog.c b/commandlog.c index 41483133..1f315529 100644 --- a/commandlog.c +++ b/commandlog.c @@ -11,6 +11,11 @@ Added a tail facility Mark Koennecke, October 1999 + + Added compact mode: + - timestamps look different and are omitted if no other text is written + - socket number information is written on the timestamp line + --------------------------------------------------------------------------*/ #include #include @@ -34,115 +39,170 @@ /*-------------------- the tail buffer ---------------------------------*/ static pCircular pTail = NULL; #define MAXTAIL 1000 +#define NOID -1964 + static time_t lastStamp = 0; + static time_t iCompact = 0; + static time_t tLastWrite = 0; + char *cmdPrompt=">"; + static int lastId=NOID; + + static time_t tLogfile = 0; + static time_t tStamp = 0; + static int iEnd = 1; + static int iAutoActive = 0; + static int iIntervall = 60; /*----------------------------------------------------------------------*/ - void WriteToCommandLog(char *prompt,char *text) + void WriteToCommandLogId(char *prompt, int id, char *text) { - int iNL = 0, iPos; - char *pPtr = NULL, *pCopy = NULL, *pText = NULL; - char myBuffer[1024]; + int l, iPos; + char *pPtr = NULL, *pCopy = NULL, *strippedText = text; + struct tm *nowTm; + time_t now; + char stamp1[32], stamp2[32], buffer[80]; + int doStamp, doStampId; + + /* suppress status messages */ + if (strstr(text,"status =") != NULL) { + return; + } - /* - we change the text, so we need to make a local copy. A copy - is dynamically allocated only if it does not fit into - myBuffer. - */ - if(strlen(text) > 1023){ - pCopy = (char *)malloc((strlen(text)+2)*sizeof(char)); - if(pCopy == NULL){ - return; - } - memset(pCopy,0,(strlen(text)+2)*sizeof(char)); - strcpy(pCopy,text); - pText = pCopy; - } else { - strcpy(myBuffer,text); - pText = myBuffer; - } + /* suppress TRANSACTIONFINISHED as well in order to make the WWW + commandlog work and TRANSACTIONSTART in order to make the logfiles + shorter + */ + if (strstr(text,"TRANSACTIONSTART") != NULL) { + return; + } + if (strstr(text,"TRANSACTIONFINISHED") != NULL) { + return; + } + + /* we make a local copy, stripping off the newline at the + end. We anyway need a copy later for the circular buffer */ + l = strlen(text); + pPtr = strrchr(text,'\n'); + if (pPtr != NULL && (pPtr[1]=='\0' || pPtr[2] == '\0')) { + l = pPtr - text; + } + pCopy = malloc(l+1); + if (pCopy == NULL) return; + strncpy(pCopy, text, l); + pCopy[l]='\0'; - /* figure out if we have to do a newline with pText as well */ - pPtr = strrchr(pText,'\n'); - if(pPtr != NULL) - { - iPos = pPtr - pText; - if(iPos >= (strlen(pText) - 2) ) - { - iNL = 1; - } - } + if (prompt == cmdPrompt && iCompact) { + pPtr = strstr(pCopy, "fulltransact "); + if (pPtr && pPtr < pCopy+3) { + strippedText = pPtr + 13; + } + pPtr = strstr(pCopy, "transact "); + if (pPtr && pPtr < pCopy+3) { + strippedText = pPtr + 9; + } + } - /* supress status messages */ - if(strstr(pText,"status =") != NULL) - { - if(pCopy != NULL){ - free(pCopy); - } - return; - } - - /* suppress TRANSACTIONFINISHED as well in order to make the WWW - commandlog work and TRANSACTIONSTART in order to make the logfiles - shorter - */ - if(strstr(pText,"TRANSACTIONFINISHED") != NULL || - strstr(pText,"TRANSACTIONSTART") != NULL) - { - if(pCopy != NULL){ - free(pCopy); - } - return; - } + /* create tail buffer as needed */ + if (!pTail) { + pTail = createCircular(MAXTAIL,free); + } - /* create tail buffer as needed */ - if(!pTail) - { - pTail = createCircular(MAXTAIL,free); - } + now = time(NULL); + + doStamp = 0; + doStampId = 0; + + if (id == NOID) { + if (!prompt) { + prompt=""; + } else { + snprintf(buffer, sizeof buffer, "%s ", prompt); + prompt = buffer; + } + } else if (iCompact == 0) { + if (!prompt) { + snprintf(buffer, sizeof buffer, "To sock %d : ", id); + } else { + snprintf(buffer, sizeof buffer, "sock %d>%s ", id, prompt); + } + prompt = buffer; + } else { + if (id != lastId) { + lastId = id; + doStampId = 1; + } + if (!prompt) { + prompt=""; + } else { + snprintf(buffer, sizeof buffer, "%s ", prompt); + prompt = buffer; + } + } + + if (iCompact > 0) { /* write time stamp */ + if (now/iCompact != lastStamp/iCompact) { + doStamp = 1; + doStampId = 1; + } + if (doStampId) { + lastStamp = now; + nowTm = localtime(&now); + strftime(stamp1, sizeof stamp1, "=== %H:%M:%S ===", nowTm); + if (id != NOID) { + snprintf(stamp2, sizeof stamp2, " socket %d ===", id); + } else { + stamp2[0] = '\0'; + } + } + } /* user file */ - if(fd != NULL) - { - if(iNL) - { - fprintf(fd,"%s %s",prompt, pText); - } - else - { - fprintf(fd,"%s %s\n",prompt, pText); - } + if (fd != NULL) { + if (doStampId) { + fprintf(fd,"%s %s\n", stamp1, stamp2); + } + fprintf(fd,"%s%s\n", prompt, pCopy); } + /* automatic file */ - if(fauto != NULL) - { - if(iNL) - { - fprintf(fauto,"%s %s",prompt, pText); - } - else - { - fprintf(fauto,"%s %s\n",prompt, pText); - } + if (fauto != NULL) { + tLastWrite = now; + if (doStampId) { + fprintf(fauto,"%s%s\n", stamp1, stamp2); + } + fprintf(fauto,"%s%s\n", prompt, strippedText); } /* to all listening sockets. The check is necessary to resolve a shutdown problem */ - if(pServ->pTasker != NULL) - { - TaskSignal(pServ->pTasker,COMLOG,pText); + if (pServ->pTasker != NULL) { + if (doStamp) { + TaskSignal(pServ->pTasker,COMLOG,stamp1); + } + TaskSignal(pServ->pTasker,COMLOG,pCopy); } /* tail buffer */ - if(pTail != NULL) - { - if(iNL) - { - pPtr = strrchr(pText,'\n'); - *pPtr = ' '; - } - setCircular(pTail,strdup(pText)); - nextCircular(pTail); + if (pTail != NULL) { + if (doStamp) { + setCircular(pTail,strdup(stamp1)); + nextCircular(pTail); + } + setCircular(pTail,pCopy); + nextCircular(pTail); } - if(pCopy != NULL){ - free(pCopy); - } + lastId = id; + } +/*------------------------------------------------------------------------*/ + void WriteToCommandLog(char *prompt, char *text) + { + WriteToCommandLogId(prompt, NOID, text); + } +/*------------------------------------------------------------------------*/ + void WriteToCommandLogCmd(int id, char *text) + { + WriteToCommandLogId(cmdPrompt, id, text); + } +/*------------------------------------------------------------------------*/ + int CompactCommandLog(void) { + return iCompact > 0; } /*------------------------------------------------------------------------*/ static void PrintTail(int iNum, SConnection *pCon) @@ -230,9 +290,9 @@ pInst = FindVariable(pServ->pSics,"instrument"); if(pInst) { - sprintf(pBueffel,"Logfile started at instument %s at %s", + sprintf(pBueffel,"Logfile started at instrument %s at %s", pInst->text,pTime); - WriteToCommandLog("SYS>> ", pBueffel); + WriteToCommandLog("SYS>>", pBueffel); } /* if a file to execute is configured, execute it */ @@ -256,12 +316,6 @@ AutoTask puts a time stamp into the auto log file any hour and creates a new log file any 24 hours */ - static time_t tLogfile = 0; - static time_t tStamp = 0; - static int iEnd = 1; - static int iAutoActive = 0; - static int iIntervall = 60; - static int AutoTask(void *pData) { time_t tNow; @@ -293,7 +347,7 @@ if(tLogfile < 0) tLogfile = tNow + 60*60*24; } - if(tNow > tStamp) + if(tNow > tStamp && iIntervall > 0) { CLFormatTime(pTime,79); WriteToCommandLog("TIMESTAMP>> ",pTime); @@ -316,6 +370,10 @@ fflush(fauto); } + if (fauto && tLastWrite > 0 && tNow > tLastWrite) { + fflush(fauto); + tLastWrite = 0; + } return iEnd; } /*----------- a command to configure the log --------------------------*/ @@ -442,15 +500,19 @@ return 0; } iIntervall = iVal; - SCSendOK(pCon); - return 1; } - else + SCPrintf(pCon,eValue,"%s.intervall [min] = %d", argv[0], iIntervall); + return 1; + } + else if(strcmp(argv[1],"compact") == 0) + { + if(argc > 2) { - sprintf(pBueffel,"autolog.intervall = %d", iIntervall); - SCWrite(pCon,pBueffel,eValue); - return 1; + iCompact = atoi(argv[2]); + if (iCompact > 0) iIntervall = 0; } + SCPrintf(pCon,eValue,"%s.compact [sec] = %d", argv[0], iCompact); + return 1; } else if(strcmp(argv[1],"close") == 0) /* close command */ { diff --git a/commandlog.h b/commandlog.h index 9e4472d3..704a264a 100644 --- a/commandlog.h +++ b/commandlog.h @@ -10,6 +10,9 @@ #ifndef COMMANDLOG #define COMMANDLOG void WriteToCommandLog(char *prompt,char *pText); + void WriteToCommandLogId(char *prompt, int id, char *text); + void WriteToCommandLogCmd(int id, char *text); + int CompactCommandLog(void); int CommandLog(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); diff --git a/confvirtualmot.c b/confvirtualmot.c index 4a7f5e44..73074fb1 100644 --- a/confvirtualmot.c +++ b/confvirtualmot.c @@ -333,6 +333,7 @@ static float invokeReadScript(pConfigurableVirtualMotor self, if(status != TCL_OK){ snprintf(self->scriptError,510,"ERROR: Tcl subsystem reported %s", Tcl_GetStringResult(pTcl)); + SCWrite(pCon,self->scriptError,eError); } return atof(Tcl_GetStringResult(pTcl)); } diff --git a/conman.c b/conman.c index 26d92edd..ff748652 100644 --- a/conman.c +++ b/conman.c @@ -39,6 +39,8 @@ fields. Mark Koennecke, December 2004 + Aded buffering support, Mark Koennecke, July 2006 + Copyright: see copyright.h -----------------------------------------------------------------------------*/ #include "fortify.h" @@ -65,7 +67,9 @@ #include "uubuffer.h" #include "commandlog.h" #include "stptok.h" - +#include "statusfile.h" +#include "sicshipadaba.h" +#include "protocol.h" /* #define UUDEB 1 define UUDEB , for buffer writing for checking encoding */ @@ -76,7 +80,7 @@ extern pServer pServ; /*------ Max Size of Command Stack */ -#define MAXSTACK 100 +#define MAXSTACK 1024 /*---------- Magic ID Header */ #define CONMAGIC 26051958 /*------------------------------------------------------------------------- @@ -91,6 +95,16 @@ extern pServer pServ; static int iName = 0; static SConnection *freeConnections = NULL; static long lastIdent = 0; +/*------------- sending connection (prevent double write when listening) ----*/ + static SConnection *sendingConnection = NULL; +/*------------- storing connection and context for later use ----*/ + struct SCStore { + SConnection *pCon; + long ident; + int inMacro; + long macroStack; + commandContext cc; + }; /*===========================================================================*/ static char *ConName(long ident) { static char name[32]; @@ -352,6 +366,7 @@ extern pServer pServ; } assert( (iMode == 0) || (iMode == 1)); self->iMacro = iMode; +/* SCPrintf(self,eError, "SCsetMacro = %lx, %d\n", (long int)self, iMode); */ return 1; } /*---------------------------------------------------------------------------*/ @@ -361,6 +376,7 @@ extern pServer pServ; char pBueffel[132]; SConnection *pVictim = NULL; Item sItem; + pHdb root = NULL; pVictim = (SConnection *)pData; if(!VerifyConnection(pVictim)) @@ -377,7 +393,7 @@ extern pServer pServ; free(pVictim->pSock); pVictim->pSock = NULL; } - WriteToCommandLog("SYS> ", + WriteToCommandLog("SYS>", "ERROR: Erraneous deletion of used Connection stopped"); return; } @@ -388,6 +404,15 @@ extern pServer pServ; */ KillCapture(pVictim); + /* + * remove any callbacks which might still be active in the Hipadaba + */ + root = GetHipadabaRoot(); + if(root != NULL) + { + RemoveConnectionCallbacks(root,pVictim); + } + /* If we have a grab, release it ! */ @@ -401,7 +426,8 @@ extern pServer pServ; } /* log the kill */ - if(pVictim->pSock && pVictim->iLogin == 1) + if(pVictim->pSock && pVictim->iLogin == 1 && + (pVictim->iUserRights < 3 || !CompactCommandLog()) ) { sprintf(pBueffel,"Deleting connection %d",pVictim->pSock->sockid); WriteToCommandLog("SYS>",pBueffel); @@ -446,6 +472,11 @@ extern pServer pServ; DeleteCommandStack(pVictim->pStack); } + /* remove possible buffers */ + if(pVictim->data != NULL) + { + DeleteDynString(pVictim->data); + } pVictim->lMagic=0; /* make a write to a freed connection harmless */ /* finally free pVictim*/ @@ -620,8 +651,10 @@ static int doSockWrite(SConnection *self, char *buffer) if(!iRet) { SCnoSock(self); - if(!self->listening && self->iLogin == 1){ - WriteToCommandLog("SYS> ","Connection broken on send"); + if(!self->listening && self->iLogin == 1 && + (self->iUserRights < 3 || !CompactCommandLog()) ) + { + WriteToCommandLog("SYS>","Connection broken on send"); } } } @@ -663,7 +696,7 @@ static void writeToLogFiles(SConnection *self, char *buffer) return 0; } - if (buffer[0] == '\0' && iOut == eFinish) { + if (buffer[0] == '\0' && iOut >= eStart && iOut <= eEvent) { return 1; /* do not write empty line */ } @@ -681,10 +714,23 @@ static void writeToLogFiles(SConnection *self, char *buffer) SICSLogWrite(buffer,iOut); /* write to commandlog if user or manager privilege */ - if(SCGetRights(self) <= usUser && self->iMacro != 1) + if(SCGetRights(self) <= usUser) { - sprintf(pBueffel,"To sock %d :",iRet); - WriteToCommandLog(pBueffel,buffer); + if(self->iMacro != 1) + { + sendingConnection = self; + WriteToCommandLogId(NULL,iRet,buffer); + sendingConnection = NULL; + } + else + { + if(iOut == eError || iOut == eWarning) + { + sendingConnection = self; + WriteToCommandLogId(NULL,iRet,buffer); + sendingConnection = NULL; + } + } } /* put it into the interpreter if present */ @@ -710,6 +756,92 @@ static void writeToLogFiles(SConnection *self, char *buffer) } return 1; } +/*--------------------------------------------------------------------------*/ + int SCACTWrite(SConnection *self, char *buffer, int iOut) + { + int i, iPtr, iRet; + char pBueffel[1024]; + char *pPtr = pBueffel; + commandContext cx; + + if(!VerifyConnection(self)) + { + return 0; + } + + if (buffer[0] == '\0' && iOut >= eStart && iOut <= eEvent) { + return 1; /* do not write empty line */ + } + + /* log it for any case */ + if(self->pSock) + { + iRet = self->pSock->sockid; + } + else + { + iRet = 0; + } + sprintf(pBueffel,"Next line intended for socket: %d",iRet); + SICSLogWrite(pBueffel,eInternal); + SICSLogWrite(buffer,iOut); + + /* write to commandlog if user or manager privilege */ + if(SCGetRights(self) <= usUser) + { + if(self->iMacro != 1) + { + sendingConnection = self; + WriteToCommandLogId(NULL,iRet,buffer); + sendingConnection = NULL; + } + else + { + if(iOut == eError || iOut == eWarning) + { + sendingConnection = self; + WriteToCommandLogId(NULL,iRet,buffer); + sendingConnection = NULL; + } + } + } + + /* + * copy in ACT + */ + if(strlen(buffer) + 30 > 1024){ + pPtr = (char *)malloc((strlen(buffer)+30)*sizeof(char)); + memset(pPtr,0,strlen(buffer)+20); + } + cx = SCGetContext(self); + sprintf(pPtr,"%d::>%s<::", cx.transID, buffer); + + /* put it into the interpreter if present */ + if(SCinMacro(self)) + { + InterpWrite(pServ->pSics,buffer); + /* print it to client if error message */ + if((iOut== eError) || (iOut == eWarning) ) + { + iRet = doSockWrite(self,pPtr); + } + } + else /* not in interpreter, normal logic */ + { + /* is this really to be printed ? */ + if(iOut < self->iOutput) + return 0; + + /* first the socket */ + iRet = doSockWrite(self,pPtr); + + writeToLogFiles(self,buffer); + } + if(pPtr != pBueffel){ + free(pPtr); + } + return 1; + } /*--------------------------------------------------------------------------*/ int SCWriteWithOutcode(SConnection *self, char *buffer, int iOut) { @@ -722,7 +854,7 @@ static void writeToLogFiles(SConnection *self, char *buffer) return 0; } - if (buffer[0] == '\0' && iOut == eFinish) { + if (buffer[0] == '\0' && iOut >= eStart && iOut <= eEvent) { return 1; /* do not write empty line */ } @@ -742,8 +874,9 @@ static void writeToLogFiles(SConnection *self, char *buffer) /* write to commandlog if user or manager privilege */ if(SCGetRights(self) <= usUser && self->iMacro != 1) { - sprintf(pBueffel,"To sock %d :",iRet); - WriteToCommandLog(pBueffel,buffer); + sendingConnection = self; + WriteToCommandLogId(NULL,iRet,buffer); + sendingConnection = NULL; } /* @@ -798,6 +931,52 @@ static void writeToLogFiles(SConnection *self, char *buffer) free(bufPtr); return 1; } +/*-------------------------------------------------------------------------*/ +static int SCBufferWrite(SConnection *self, char *buffer, int iOut) +{ + if(!VerifyConnection(self)) + { + return 0; + } + assert(self->data != NULL); + DynStringConcat(self->data,buffer); + if(strchr(buffer,'\n') == NULL){ + DynStringConcat(self->data,"\n"); + } + return 1; +} +/*-------------------------------------------------------------------------*/ +int SCStartBuffering(SConnection *pCon) +{ + if(!VerifyConnection(pCon)) + { + return 0; + } + if(pCon->data != NULL) + { + DeleteDynString(pCon->data); + } + pCon->data = CreateDynString(128,128); + if(pCon->data == NULL) + { + return 0; + } + pCon->oldWriteFunc = pCon->write; + pCon->write = SCBufferWrite; + return 1; +} +/*-------------------------------------------------------------------------*/ +pDynString SCEndBuffering(SConnection *pCon) +{ + if(!VerifyConnection(pCon)) + { + return 0; + } + assert(pCon->oldWriteFunc != NULL); + pCon->write = pCon->oldWriteFunc; + pCon->oldWriteFunc = NULL; + return pCon->data; +} /*--------------------------------------------------------------------------*/ int SCOnlySockWrite(SConnection *self, char *buffer, int iOut) { @@ -862,6 +1041,11 @@ static void writeToLogFiles(SConnection *self, char *buffer) SICSLogWrite(pBueffel,eInternal); SICSLogWrite(buffer,iOut); + /* put it into the interpreter if present */ + if(SCinMacro(self)) + { + InterpWrite(pServ->pSics,buffer); + } return 1; } /*-------------------------------------------------------------------------- @@ -878,16 +1062,40 @@ static void writeToLogFiles(SConnection *self, char *buffer) return 0; } - /* put into Serverlog */ - sprintf(pBueffel,"Next line intended for socket: %d",-10); + /* log it for any case */ + if(self->pSock) + { + iRet = self->pSock->sockid; + } + else + { + iRet = -10; + } + + /* put into Serverlog */ + sprintf(pBueffel,"Next line intended for socket: %d",iRet); SICSLogWrite(pBueffel,eInternal); + SICSLogWrite(buffer,iOut); /* write to commandlog if user or manager privilege */ - if(SCGetRights(self) <= usUser && self->iMacro != 1) + if(SCGetRights(self) <= usUser) { - sprintf(pBueffel,"To sock %d :",-10); - WriteToCommandLog(pBueffel,buffer); + if(self->iMacro != 1) + { + sendingConnection = self; + WriteToCommandLogId(NULL,iRet,buffer); + sendingConnection = NULL; + } + else + { + if(iOut == eError || iOut == eWarning) + { + sendingConnection = self; + WriteToCommandLogId(NULL,iRet,buffer); + sendingConnection = NULL; + } + } } /* put it into the interpreter if present */ @@ -957,8 +1165,9 @@ static void writeToLogFiles(SConnection *self, char *buffer) int SCWriteZipped(SConnection *self, char *pName, void *pData, int iDataLen) { char outBuf[65546], *pBuf = NULL, noutBuf[ZIPBUF], *pHeader = NULL; - int compressedLength, iRet, iRet2, iCount; + int compressedLength, iRet, iRet2, iCount, protocolID; z_stream compStream; + commandContext cc; /* check for a valid connection */ if(!VerifyConnection(self)) @@ -1033,7 +1242,15 @@ static void writeToLogFiles(SConnection *self, char *buffer) /* write header line */ memset(outBuf,0,65536); - sprintf(outBuf,"SICSBIN ZIP %s %d\r\n",pName,compressedLength); + + protocolID = GetProtocolID(self); + if(protocolID == 5){ + cc = SCGetContext(self); + sprintf(outBuf,"SICSBIN ZIP %s %d %d\r\n",pName, + compressedLength, cc.transID); + } else { + sprintf(outBuf,"SICSBIN ZIP %s %d \r\n",pName,compressedLength); + } pHeader = strdup(outBuf); if(pHeader == NULL) { @@ -1258,8 +1475,7 @@ static void writeToLogFiles(SConnection *self, char *buffer) SetStatus(eOld); CostaLock(pCon->pStack); strncpy(pResult,pPtr,iLen); - sprintf(pFrom,"Prompted from sock %2.2d: ", pCon->pSock->sockid); - WriteToCommandLog(pFrom,pPtr); + WriteToCommandLogId(" prompted>", pCon->pSock->sockid, pPtr); return 1; } } @@ -1359,21 +1575,26 @@ static void writeToLogFiles(SConnection *self, char *buffer) /* print to command log if user or manager */ if(SCGetRights(self) <= usUser) { - if(self->pSock != NULL) - { - sprintf(pBueffel,"sock %d>>",self->pSock->sockid); + /* + * This is a fix to suppress cron messages in the success + * case + */ + if(SCGetWriteFunc(self) != SCNotWrite) + { + sendingConnection = self; + if(self->pSock != NULL) + { + WriteToCommandLogCmd(self->pSock->sockid, pCommand); + } else { + WriteToCommandLog("CRON>>",pCommand); + } + sendingConnection = NULL; } - else - { - strcat(pBueffel,"CONT or CRON>> "); - } - WriteToCommandLog(pBueffel,pCommand); } /* invoke */ self->inUse++; self->eInterrupt = eContinue; - self->parameterChange = 0; /* get first word of command */ @@ -1382,18 +1603,8 @@ static void writeToLogFiles(SConnection *self, char *buffer) SCAdvanceContext(self,pBueffel); iRet = InterpExecute(pInter,self,pCommand); SCPopContext(self); - if(self->parameterChange == 1) - { - /* - automatically save changed parameters - */ - pFile = IFindOption(pSICSOptions,"statusfile"); - if(pFile != NULL) - { - WriteSicsStatus(pInter,pFile,0); - self->parameterChange = 0; - } - } + StatusFileTask(NULL); /* save changed parameters */ + self->inUse--; return iRet; } @@ -1402,7 +1613,7 @@ static void writeToLogFiles(SConnection *self, char *buffer) config OutCode val sets an new output code config Rights User Password sets and verifies new user rights config File Filename Logs to another file - config output normal | withcode Sets output mode + config output normal | withcode | ACT Sets output mode config listen 0 | 1 enables commandlog listen mode ---------------------------------------------------------------------------*/ @@ -1410,6 +1621,7 @@ static void writeToLogFiles(SConnection *self, char *buffer) int argc, char *argv[]) { char pBueffel[512]; + char pHost[132]; int i, iRet; int iNum; @@ -1541,6 +1753,10 @@ static void writeToLogFiles(SConnection *self, char *buffer) { SCSetWriteFunc(pCon,SCWriteWithOutcode); } + else if(strcmp(argv[2],"act") == 0) + { + SCSetWriteFunc(pCon,SCACTWrite); + } else { SCWrite(pCon,"ERROT: output mode not recognised",eError); @@ -1565,10 +1781,19 @@ static void writeToLogFiles(SConnection *self, char *buffer) SCWrite(pCon,pBueffel,eError); return 0; } + if (CompactCommandLog()) { + if (pCon->iUserRights < 3 || i < 3) { + NETInfo(pCon->pSock,pHost,sizeof pHost); + sprintf(pBueffel,"User %s from %s switched to %d privilege", + argv[2],pHost,i); + WriteToCommandLogId("SYS>",pCon->pSock->sockid,pBueffel); + } + } else { + sprintf(pBueffel,"User %s socket %d switched to %d privilege", + argv[2],pCon->pSock->sockid,i); + WriteToCommandLog("SYS>",pBueffel); + } pCon->iUserRights = i; - sprintf(pBueffel,"User %s socket %d switched to %d privilege", - argv[2],pCon->pSock->sockid,i); - WriteToCommandLog("SYS>",pBueffel); SCWrite(pCon,"Change of Authorisation Acknowledged",eWarning); return 1; } @@ -1821,6 +2046,11 @@ static void writeToLogFiles(SConnection *self, char *buffer) { return 0; } + if(self->pSock->iType == 0) + { + NetReadRemove(pServ->pReader,self->pSock); + self->iEnd = 1; + } if(self->iEnd) { @@ -1888,11 +2118,21 @@ static void writeToLogFiles(SConnection *self, char *buffer) self->iLogin = 1; SCSetRights(self,iRet); pHost[0] = '\0'; - NETInfo(self->pSock,pHost,131); - sprintf(pBueffel,"Accepted connection on socket %d from %s", - self->pSock->sockid, pHost); - SICSLogWrite(pBueffel,eInternal); - WriteToCommandLog("SYS >", pBueffel); + if (CompactCommandLog()) { + if (iRet < 3) { + NETInfo(self->pSock,pHost,131); + sprintf(pBueffel,"Accepted connection %s from %s as %s", + ConName(self->ident), pHost, pUser); + SICSLogWrite(pBueffel,eInternal); + WriteToCommandLogId("SYS>", self->pSock->sockid, pBueffel); + } + } else { + NETInfo(self->pSock,pHost,131); + sprintf(pBueffel,"Accepted connection %s on socket %d from %s", + ConName(self->ident), self->pSock->sockid, pHost); + SICSLogWrite(pBueffel,eInternal); + WriteToCommandLog("SYS >", pBueffel); + } free(pPtr); return 1; } @@ -1954,7 +2194,7 @@ static void writeToLogFiles(SConnection *self, char *buffer) else if(iSignal == COMLOG && self->listening == 1) { pPtr = (char *)pSigData; - if(pPtr != NULL) + if(pPtr != NULL && self != sendingConnection) { doSockWrite(self,pPtr); } @@ -1971,11 +2211,7 @@ static void writeToLogFiles(SConnection *self, char *buffer) /*-----------------------------------------------------------------------*/ void SCparChange(SConnection *self) { - if(!VerifyConnection(self)) - { - return; - } - self->parameterChange = 1; + StatusFileDirty(); } /*------------------------------------------------------------------------*/ int SCActive(SConnection *self) @@ -1994,25 +2230,75 @@ int SCActive(SConnection *self) return 0; } /*--------------------------------------------------------------------------*/ -void SCSave(SCStore *con, SConnection *pCon) { - con->pCon = pCon; - if (pCon) { - con->ident = pCon->ident; +SCStore *SCSave(SConnection *pCon, SCStore *oldStore) { + commandContext cc; + + if (oldStore == NULL) { + oldStore = calloc(1,sizeof(*oldStore)); + assert(oldStore); } + oldStore->pCon = pCon; + if (pCon) { + oldStore->ident = pCon->ident; + oldStore->inMacro = pCon->iMacro; + oldStore->cc = SCGetContext(pCon); + oldStore->macroStack = 0; + } + return oldStore; } /*--------------------------------------------------------------------------*/ -SConnection *SCLoad(SCStore *con) { - SConnection *pCon; - - pCon = con->pCon; +SConnection *SCLoad(SCStore *conStore) { + SConnection *pCon = NULL; + commandContext old; + + if (conStore) { + pCon = conStore->pCon; + } if (pCon) { - if (con->ident != pCon->ident) { - con->pCon = NULL; /* connection is dead */ + if (conStore->ident != pCon->ident) { + conStore->pCon = NULL; /* connection is dead */ pCon = NULL; } } + if (pCon) { + return pCon; + } + return pServ->dummyCon; +} +/*--------------------------------------------------------------------------*/ +SConnection *SCStorePush(SCStore *conStore) { + SConnection *pCon; + + pCon = SCLoad(conStore); + /* push macro flag on stack */ + conStore->macroStack <<= 1; + conStore->macroStack |= (pCon->iMacro != 0); + SCsetMacro(pCon, conStore->inMacro); + SCPushContext2(pCon, conStore->cc); return pCon; } +/*--------------------------------------------------------------------------*/ +void SCStorePop(SCStore *conStore) { + SConnection *pCon; + + pCon = SCLoad(conStore); + SCPopContext(pCon); + /* pop macro flag from stack + SCsetMacro(pCon,conStore->macroStack); + */ + SCsetMacro(pCon, (conStore->macroStack & 1)); + conStore->macroStack >>= 1; +} +/*--------------------------------------------------------------------------*/ +int SCStoreConnected(SCStore *conStore) { + return (conStore && + conStore->pCon && + conStore->pCon->ident == conStore->ident); +} +/*--------------------------------------------------------------------------*/ +void SCStoreFree(SCStore *conStore) { + free(conStore); +} /* --------------------------------------------------------------------------*/ long SCTagContext(SConnection *self, char *tagName) { diff --git a/conman.h b/conman.h index 17928a5b..aaf0e006 100644 --- a/conman.h +++ b/conman.h @@ -24,6 +24,7 @@ #include "network.h" #include "obdes.h" #include "commandcontext.h" +#include "dynstring.h" #define MAXLOGFILES 10 @@ -57,8 +58,13 @@ typedef int (*writeFunc)(struct __SConnection *pCon, int iUserRights; int inUse; int iGrab; /* grab flag for token*/ - int parameterChange; int sicsError; + + /* + * for I/O Buffering + */ + pDynString data; + writeFunc oldWriteFunc; /* stuff supporting the sycamore protocol and a @@ -116,6 +122,10 @@ typedef int (*writeFunc)(struct __SConnection *pCon, int SCNotWrite(SConnection *self, char *buffer, int iOut); int SCNormalWrite(SConnection *self, char *buffer, int iOut); int SCWriteWithOutcode(SConnection *self, char *buffer, int iOut); + int SCACTWrite(SConnection *self, char *buffer, int iOut); +/*********************** I/O Buffering ***********************************/ + int SCStartBuffering(SConnection *pCon); + pDynString SCEndBuffering(SConnection *pCon); /************************* CallBack *********************************** */ int SCRegister(SConnection *pCon, SicsInterp *pSics, void *pInter, long lID); @@ -158,16 +168,25 @@ typedef int (*writeFunc)(struct __SConnection *pCon, int ConSicsAction(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); /******************************** Store ************************************/ -typedef struct { - SConnection *pCon; - long ident; -} SCStore; +typedef struct SCStore SCStore; -void SCSave(SCStore *con, SConnection *pCon); -/* save a connection for later use. */ +SCStore *SCSave(SConnection *pCon, SCStore *oldStore); +/* save a connection and its context for later use. */ -SConnection *SCLoad(SCStore *con); -/* check con and return SConnection if still valid or NULL otherwise. */ +SConnection *SCLoad(SCStore *conStore); +/* check con and return SConnection if still valid or a dummy connection otherwise. */ + +SConnection *SCStorePush(SCStore *conStore); +/* load connection and push stored context. Must be paired with an SCStorePop command */ + +void SCStorePop(SCStore *conStore); +/* pop context */ + +int SCStoreConnected(SCStore *conStore); +/* check if a stored connection is not closed */ + +void SCStoreFree(SCStore *conStore); +/* free an SCStore */ void KillFreeConnections(void); diff --git a/countdriv.h b/countdriv.h index 4bb86fed..4f7603aa 100644 --- a/countdriv.h +++ b/countdriv.h @@ -88,4 +88,10 @@ * file: mcstascounter.c */ pCounterDriver NewMcStasCounter(char *name); + + /* + * for regression testing + * file: regresscter.c + */ + pCounterDriver NewRegressCounter(char *name); #endif diff --git a/counter.c b/counter.c index a29a443f..495bee96 100644 --- a/counter.c +++ b/counter.c @@ -103,9 +103,9 @@ if(iRet == OKOK) { self->isUpToDate = 0; - self->badStatusCount = 0; + self->badStatusCount = 0; self->tStart = time(&tX); - InvokeCallBack(self->pCall,COUNTSTART,pCon); + InvokeCallBack(self->pCall,COUNTSTART,pCon); return iRet; } else @@ -314,7 +314,7 @@ for(i = 0; i < 3; i++) { iRet = self->pDriv->ReadValues(self->pDriv); - if(iRet) + if(iRet == OKOK) { self->isUpToDate = 1; return OKOK; @@ -529,6 +529,13 @@ } } + /* + * test for regression testing counter + */ + if(strcmp(argv[2],"regress") == 0){ + pDriv = NewRegressCounter(argv[1]); + } + /* * test for McStas simulation counter driver */ @@ -771,8 +778,9 @@ {"mode",1,{FUPAOPT}}, {"preset",1,{FUPAOPT}}, {"send",0,{0,0}}, - {"setpar",3,{FUPATEXT,FUPAINT,FUPAFLOAT}}, - {"getpar",2,{FUPATEXT,FUPAOPT}} + {"setpar",3,{FUPATEXT,FUPAINT,FUPAFLOAT}}, + {"getpar",2,{FUPATEXT,FUPAOPT}}, + {"getnmon",0,{0,0}} }; char *pMode[] = { "timer", @@ -788,7 +796,7 @@ /* parse function args */ argtolower(argc,argv); argx = &argv[1]; - iRet = EvaluateFuPa((pFuncTemplate)&ActionTemplate,22,argc-1,argx,&PaRes); + iRet = EvaluateFuPa((pFuncTemplate)&ActionTemplate,23,argc-1,argx,&PaRes); if(iRet < 0) { sprintf(pBueffel,"%s",PaRes.pError); @@ -907,6 +915,7 @@ SCSendOK(pCon); return 1; case 11: /* status */ + self->pCountInt->TransferData(self,pCon); if(GetCounterMode(self) == ePreset) { sprintf(pBueffel,"%s.CountStatus = %d %d Beam: %ld E6", @@ -1126,6 +1135,10 @@ return 0; } break; + case 22: /* getnmon */ + snprintf(pBueffel,131,"%s.getnmon = %d", argv[0], GetNMonitor(self)); + SCWrite(pCon,pBueffel,eValue); + break; default: assert(0); /* internal error */ } diff --git a/danu.c b/danu.c index d77f3dea..aac6ee68 100644 --- a/danu.c +++ b/danu.c @@ -363,8 +363,9 @@ int NewThousand(pDataNumber self) if(SCMatchRights(pCon,usMugger)) { - DecrementDataNumber(self); - SCWrite(pCon,"Data file killed",eWarning); + iNum = DecrementDataNumber(self); + snprintf(pBueffel,511,"Data file %d killed", iNum+1); + SCWrite(pCon,pBueffel,eWarning); return 1; } else diff --git a/devexec.c b/devexec.c index a8bf3d9b..5fb75981 100644 --- a/devexec.c +++ b/devexec.c @@ -9,6 +9,9 @@ revised for use with tasker: Mark Koennecke, September 1997 Locking added: Mark Koennecke, August 2002 + Refactored and instrumentation for instrument staticstics added. + Mark Koennecke, July 2006 + Copyright: Labor fuer Neutronenstreuung @@ -39,10 +42,12 @@ NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -----------------------------------------------------------------------------*/ +#include #include #include #include #include +#include #include "fortify.h" #include "sics.h" #include "nserver.h" @@ -52,11 +57,57 @@ #include "devexec.h" #include "status.h" #include "lld.h" +#include "commandlog.h" +#include "ifile.h" /* #define DEBUG 1 */ +/*======================== Logging stuff ==================================*/ +static FILE *devLog = NULL; +/*-------------------------------------------------------------------------*/ +int openDevexecLog(){ + char *fileName = NULL; + char fileBuffer[1024]; + time_t iDate; + struct tm *psTime; + + + if(devLog == NULL){ + fileName = IFindOption(pSICSOptions,"devexeclog"); + if(fileName != NULL){ + strcpy(fileBuffer,fileName); + } else { + iDate = time(NULL); + psTime = localtime(&iDate); + fileBuffer[0] = '\0'; + fileName = getenv("HOME"); + if(fileName != NULL){ + snprintf(fileBuffer,1023,"%s/log/devexec%4.4d.log", + fileName, psTime->tm_year + 1900); + } + } + devLog = fopen(fileBuffer,"a+"); + } + if(devLog == NULL){ + return 0; + } else { + return 1; + } +} +/*-------------------------------------------------------------------------*/ +void DevexecLog(char *operation, char *device) { + struct timeval tv; + struct timezone tm; + if(devLog != NULL){ + gettimeofday(&tv,&tm); + fprintf(devLog, "DEVEXEC:%s:%s:%ld:%ld\n",operation,device, + (long)tv.tv_sec, (long)tv.tv_usec); + fflush(devLog); + } +} +/*======================== internal data structures =======================*/ typedef struct _DevEntry { void *pData; pObjectDescriptor pDescriptor; @@ -64,6 +115,13 @@ char *name; commandContext comCon; } DevEntry, *pDevEntry; +/*------------------------------------------------------------------------*/ +typedef struct { + pExeList self; + pDevEntry pDev; + pICountable pCountInt; + pIDrivable pDrivInt; +}checkContext, *pCheckContext; /*-------------------------------------------------------------------------*/ static pDevEntry CreateDevEntry(pObjectDescriptor pDes, void *pData, float fVal, char *name) @@ -111,9 +169,25 @@ int iLock; pICallBack pCall; time_t lastRun; + int paused; + int taskRunning; } ExeList; static pExeList pExecutor = NULL; +/*--------------------------------------------------------------------------*/ + static void *DevexecInterface(void *pData, int iInter) + { + pExeList self = NULL; + + self = (pExeList)pData; + assert(self); + + if(iInter == CALLBACKINTERFACE) + { + return self->pCall; + } + return NULL; + } /*--------------------------------------------------------------------------*/ pExeList CreateExeList(pTaskMan pTask) { @@ -146,8 +220,11 @@ pRes->lTask = -1; pRes->iLock = 0; pRes->drivePrint = 0; + pRes->paused = 0; + pRes->taskRunning = 0; pRes->pCall = CreateCallBackInterface(); pRes->lastRun = time(NULL); + pRes->pDes->GetInterface = DevexecInterface; return pRes; } /*-------------------------------------------------------------------------*/ @@ -167,6 +244,10 @@ free(self); pServ->pExecutor = NULL; + if(devLog != NULL){ + fclose(devLog); + devLog = NULL; + } } /*--------------------------------------------------------------------------*/ void ExeInterest(pExeList self, pDevEntry pDev, char *text) { @@ -284,6 +365,7 @@ self->iEnd = 0; pCon->conStatus = HWBusy; } + DevexecLog("START",pNew->name); return 1; } else @@ -386,7 +468,7 @@ pCon,pCter->pDriv->fPreset); } /*--------------------------------------------------------------------------*/ - int CheckExeList(pExeList self) + int CheckExeListOld(pExeList self) { int iRet; pDevEntry pDev = NULL; @@ -402,7 +484,7 @@ /* Sometimes this gets called, though nothing is running. There are cases where this is feasible for maintainance, but in some cases it - is pure rubbish, because nothing runs. This will ne checkd here. + is pure rubbish, because nothing runs. This will be checked here. */ if((self->pOwner == NULL) || (LLDcheck(self->iList) == LIST_EMPTY)) { @@ -421,10 +503,7 @@ LLDnodeDataTo(self->iList,&pDev); if(pDev) { - /* - SCSetContext(self->pOwner,pDev->cmdID,pDev->name); - */ - SCPushContext(self->pOwner, pDev->comCon.transID, pDev->comCon.deviceID); + SCPushContext(self->pOwner, pDev->comCon.transID, pDev->comCon.deviceID); pDrivInt = pDev->pDescriptor->GetInterface(pDev->pData,DRIVEID); pCountInt = pDev->pDescriptor->GetInterface(pDev->pData,COUNTID); @@ -560,6 +639,197 @@ return 0; } } +/*-------------------------------------------------------------------------*/ +static int checkInterrupt(pCheckContext pCheck, int targetStatus){ + if(SCGetInterrupt(pCheck->self->pOwner) != eContinue) { + pCheck->self->iStatus = DEVINT; + SCPopContext(pCheck->self->pOwner); + SetStatus(eEager); + return -1; + } else { + return targetStatus; + } +} +/*--------------------------------------------------------------------------*/ +static int initializeCheck(pCheckContext pCheck, pDevEntry pDev){ + int eCode = HWFault; + + SCPushContext(pCheck->self->pOwner, + pDev->comCon.transID, pDev->comCon.deviceID); + pCheck->pDev = pDev; + pCheck->pDrivInt = pDev->pDescriptor->GetInterface(pDev->pData,DRIVEID); + pCheck->pCountInt = pDev->pDescriptor->GetInterface(pDev->pData,COUNTID); + if(pCheck->pDrivInt != NULL){ + eCode = pCheck->pDrivInt->CheckStatus(pDev->pData,pCheck->self->pOwner); + } else if(pCheck->pCountInt != NULL) { + eCode = pCheck->pCountInt->CheckCountStatus(pDev->pData,pCheck->self->pOwner); + } + return eCode; +} +/*--------------------------------------------------------------------------*/ +static int finishDevice(pCheckContext pCheck){ + int status; + + if(pCheck->pCountInt != NULL) { + pCheck->pCountInt->TransferData(pCheck->pDev->pData,pCheck->self->pOwner); + } else if(pCheck->pDrivInt != NULL) { + pCheck->pDrivInt->iErrorCount = 0; + } + ExeInterest(pCheck->self, pCheck->pDev, "finished"); + DevexecLog("STOP",pCheck->pDev->name); + DeleteDevEntry(pCheck->pDev); + LLDnodeDelete(pCheck->self->iList); + status = LLDnodePtr2Prev(pCheck->self->iList); + SCWrite(pCheck->self->pOwner, "", eFinish); + pCheck->self->iStatus = DEVDONE; + return checkInterrupt(pCheck,status); +} +/*-------------------------------------------------------------------------*/ +static int errorDevice(pCheckContext pCheck){ + int status; + + ExeInterest(pCheck->self, pCheck->pDev, "finished with problem"); + DevexecLog("STOP",pCheck->pDev->name); + LLDnodeDelete(pCheck->self->iList); + status = LLDnodePtr2Prev(pCheck->self->iList); + SCWrite(pCheck->self->pOwner, "", eFinish); + pCheck->self->iStatus = DEVERROR; + if(pCheck->pDrivInt != NULL) { + pCheck->pDrivInt->iErrorCount++; + } + status = checkInterrupt(pCheck,status); + DeleteDevEntry(pCheck->pDev); + return status; +} +/*-------------------------------------------------------------------------*/ +static int testFinish(pExeList self){ + if((self->pOwner == NULL) || (LLDcheck(self->iList) == LIST_EMPTY)) { + self->pOwner = NULL; + self->iRun = 0; + self->iEnd = 1; + self->iStop = 0; + self->lTask = -1; + return 1; + } else { + return 0; + } +} +/*--------------------------------------------------------------------------*/ + int CheckExeList(pExeList self) + { + int iRet, status; + checkContext check; + pDevEntry pDev = NULL; + pICountable pCountInt = NULL; + pIDrivable pDrivInt = NULL; + int eCode; + int isCounting=0, isDriving=0; + char pBueffel[512]; + SConnection *pCon; + pCon = self->pOwner; + + assert(self); + + /* Sometimes this gets called, though nothing is running. There are + cases where this is feasible for maintainance, but in some cases it + is pure rubbish, because nothing runs. This will be checked here. + */ + if(testFinish(self) == 1){ + return 1; + } + + /* + check the status of all registered devices. Remove when finished + */ + check.self = self; + status = LLDnodePtr2First(self->iList); + while(status != 0) + { + LLDnodeDataTo(self->iList,&pDev); + if(pDev) + { + eCode = initializeCheck(&check,pDev); + if(eCode != HWNoBeam && eCode != HWPause && self->paused == 1){ + DevexecLog("CONTINUE","ALL"); + self->paused = 0; + } + switch(eCode) + { + case HWIdle: + case OKOK: + status = finishDevice(&check); + if(status < 0){ + return status; + } + break; + case HWFault: /* real HW error: burning, no net etc.. */ + status = errorDevice(&check); + if(status < 0){ + return status; + } + break; + case HWNoBeam: + SetStatus(eOutOfBeam); + if(self->paused == 0){ + self->paused = 1; + DevexecLog("NOBEAM","ALL"); + } + status = checkInterrupt(&check,1); + if(status < 0){ + return status; + } + break; + case HWPause: + SetStatus(ePaused); + if(self->paused == 0){ + self->paused = 1; + DevexecLog("PAUSE","ALL"); + } + status = checkInterrupt(&check,1); + if(status < 0){ + /* + * continue in order to wait for devices to come to a stop + */ + ContinueExecution(self); + return status; + } + break; + case HWBusy: + if(check.pDrivInt != NULL) + { + isDriving = 1; + } + else if(check.pCountInt != NULL) + { + isCounting = 1; + } + self->iStatus = DEVBUSY; + break; + case HWPosFault: /* cannot get somewhere... */ + status = errorDevice(&check); + if(status < 0){ + return status; + } + break; + } + SCPopContext(self->pOwner); + } + status = LLDnodePtr2Next(self->iList); + } + + if (isCounting) { + if (isDriving) { + SetStatus(eCountDrive); + } else { + SetStatus(eCounting); + } + } else if (isDriving) { + SetStatus(eDriving); + } + + iRet = LLDnodePtr2First(self->iList); + return testFinish(self); + } /*---------------------------------------------------------------------------*/ int Wait4Success(pExeList self) { @@ -875,7 +1145,7 @@ { ListPending(self,pCon); return 1; - } + } iRet = StopExe(self,argv[1]); if(!iRet) @@ -897,7 +1167,7 @@ SCPopContext(pCon); return 1; } -/*--------------------------------------------------------------------------*/ + /*--------------------------------------------------------------------------*/ int ListExe(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) { @@ -1096,8 +1366,20 @@ return 0; } - self->lastRun = time(NULL); + if(self->taskRunning == 1){ + printf("DevexecTask reentrant protection triggered\n"); + return 1; + } + /* + * CheckExeList may cause waits and thus reentrant calls to + * this. Which can cause trouble + */ + self->taskRunning = 1; iRet = CheckExeList(self); + self->taskRunning = 0; + + + self->lastRun = time(NULL); switch(iRet) { case -1: /* some problem */ @@ -1106,7 +1388,9 @@ { if(iInterrupt > 1) { + self->taskRunning = 1; StopExe(self,"all"); + self->taskRunning = 0; } #ifdef DEBUG printf("DevExecTask found an error\n"); diff --git a/devexec.h b/devexec.h index 9c43e207..028d5e37 100644 --- a/devexec.h +++ b/devexec.h @@ -118,6 +118,8 @@ #line 259 "devexec.w" /*-------------------------- Commands ------------------------------------*/ + int DevexecAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); int StopCommand(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); /* @@ -152,11 +154,7 @@ /* continues execution */ - int DevexecAction(SConnection *pCon, SicsInterp *pSics, void *pData, - int argc, char *argv[]); - /* - * various commands - */ + /*--------------------------- Locking ---------------------------------*/ #line 183 "devexec.w" @@ -165,11 +163,12 @@ void UnlockDeviceExecutor(pExeList self); -#line 297 "devexec.w" +#line 299 "devexec.w" /* -------------------------- Executor management -------------------------*/ pExeList GetExecutor(void); void SetExecutor(pExeList pExe); - +/*----------------------- Logging -----------------------------------------*/ + void DevexecLog(char *op, char *device); #endif diff --git a/devexec.tex b/devexec.tex index e6515b8f..6ebd2463 100644 --- a/devexec.tex +++ b/devexec.tex @@ -312,6 +312,8 @@ to the global SICS device executor. \mbox{}\verb@/*-------------------------------------------------------------------------*/@\\ \mbox{}\verb@@$\langle$devstop {\footnotesize ?}$\rangle$\verb@@\\ \mbox{}\verb@/*-------------------------- Commands ------------------------------------*/@\\ +\mbox{}\verb@ int DevexecAction(SConnection *pCon, SicsInterp *pSics, void *pData,@\\ +\mbox{}\verb@ int argc, char *argv[]);@\\ \mbox{}\verb@ int StopCommand(SConnection *pCon, SicsInterp *pSics, void *pData,@\\ \mbox{}\verb@ int argc, char *argv[]);@\\ \mbox{}\verb@ /*@\\ @@ -353,7 +355,8 @@ to the global SICS device executor. \mbox{}\verb@ @\\ \mbox{}\verb@ pExeList GetExecutor(void);@\\ \mbox{}\verb@ void SetExecutor(pExeList pExe);@\\ -\mbox{}\verb@ @\\ +\mbox{}\verb@/*----------------------- Logging -----------------------------------------*/@\\ +\mbox{}\verb@ void DevexecLog(char *op, char *device); @\\ \mbox{}\verb@#endif @\\ \mbox{}\verb@@$\diamond$ \end{list} diff --git a/devexec.w b/devexec.w index 45ff782c..12acf662 100644 --- a/devexec.w +++ b/devexec.w @@ -258,6 +258,8 @@ to the global SICS device executor. /*-------------------------------------------------------------------------*/ @ /*-------------------------- Commands ------------------------------------*/ + int DevexecAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); int StopCommand(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); /* @@ -299,7 +301,8 @@ to the global SICS device executor. pExeList GetExecutor(void); void SetExecutor(pExeList pExe); - +/*----------------------- Logging -----------------------------------------*/ + void DevexecLog(char *op, char *device); #endif @} diff --git a/devser.c b/devser.c new file mode 100644 index 00000000..af7da49b --- /dev/null +++ b/devser.c @@ -0,0 +1,361 @@ +#include +#include "ascon.h" +#include "devser.h" + +typedef struct DevAction { + struct DevAction *next; + void *data; + DevActionHandler *hdl; + DevPrio prio; + DevKillActionData *kill; +} DevAction; + +typedef struct SchedHeader { + struct SchedHeader *next; + DevAction *actions; /* list of actions for given interval and prio */ + DevAction *followingAction; + double interval; + double timeDue; + DevPrio prio; +} SchedHeader; + +struct DevSer { + Ascon *asyncConn; /* connection */ + DevAction *current; + int killCurrent; + DevAction *actions; /* the action queue */ + SchedHeader *headers; + ErrMsg *errmsg; + int steps; + int stopTask; +}; + +static char *devPrio[NumberOfPRIO] = { + "null", "slow", "read", "progress", "write", "halt" +}; + +char *DevPrio2Text(DevPrio prio) { + if (prio <= 0 || prio >= NumberOfPRIO) { + prio = NullPRIO; + } + return devPrio[prio]; +} + +DevPrio DevText2Prio(char *text) { + DevPrio prio; + for (prio = 0; prio < NumberOfPRIO; prio++) { + if (strcasecmp(text, devPrio[prio]) == 0) return prio; + } + return NullPRIO; +} + +static void DevFreeActionList(DevAction *actions) { + DevAction *victim; + while (actions != NULL) { + victim = actions; + actions = victim->next; + if (victim->kill != NULL) victim->kill(victim->data); + free(victim); + } +} + +static void DevKillTask(void *ds) { + DevSer *devser = ds; + + if (devser->stopTask) { + free(devser); + } else { + devser->stopTask = 1; + } +} + +DevAction *DevNextAction(DevSer *devser) { + DevPrio prio; + double now; + SchedHeader *header; + + + devser->current = NULL; + if (devser->actions) { + prio = devser->actions->prio; + } else { + prio = NullPRIO; + } + now = DoubleTime(); + for (header = devser->headers; + header != NULL && header->prio > prio; + header = header->next) { + if (header->followingAction == NULL) { + if (now >= header->timeDue) { + header->followingAction = header->actions; + if (header->interval <= 0) { + header->timeDue = now; + } else { + header->timeDue = (floor(now / header->interval) + 1) + * header->interval; + } + } + } + if (header->followingAction != NULL) { + devser->current = header->followingAction; + devser->killCurrent = 0; + header->followingAction = header->followingAction->next; + return devser->current; + } + } + if (devser->actions) { + devser->current = devser->actions; + devser->killCurrent = 1; + devser->actions = devser->actions->next; + } + return devser->current; +} + +int DevQueueTask(void *ds) { + DevSer *devser = ds; + AsconStatus status; + DevAction *action; + char *sendData; + char *replyData; + + if (devser->steps == 0) return 1; + if (devser->stopTask) { + return 0; + } + action = devser->current; + if (action == NULL) { + action = DevNextAction(devser); + } + + while (action != NULL) { + status = AsconTask(devser->asyncConn); + if (status == AsconFailure) { + devser->errmsg = AsconGetErrList(devser->asyncConn); + } else if (status != AsconReady) { + return 1; + } + if (devser->steps > 0) { /* debugging mode */ + devser->steps--; + } + if(status == AsconFailure){ + replyData = devser->errmsg->text; + } else { + replyData = AsconRead(devser->asyncConn); + } + sendData = action->hdl(action->data, replyData); + if (sendData != NULL) { + AsconWrite(devser->asyncConn, sendData, 0); + return 1; + } + if (devser->killCurrent) { + if (action->kill != NULL) action->kill(action->data); + devser->killCurrent = 0; + free(action); + devser->current = NULL; + } + action = DevNextAction(devser); + } + return 1; +} + +DevSer *DevMake(SConnection *con, int argc, char *argv[]) { + DevSer *devser = NULL; + Ascon *asyncConn = NULL; + + asyncConn = AsconMake(con, argc, argv); + if (!asyncConn) { + return NULL; + } + devser = calloc(1, sizeof(*devser)); + assert(devser); + devser->asyncConn = asyncConn; + devser->current = NULL; + devser->killCurrent = 0; + devser->actions = NULL; + devser->headers = NULL; + devser->stopTask = 0; + devser->steps = -1; /* no debugging by default */ + TaskRegister(pServ->pTasker, DevQueueTask, NULL, DevKillTask, devser, 0); + return devser; +} + +void DevDebugMode(DevSer *devser, int steps) { + devser->steps = steps; +} + +DevAction *DevNewAction(void *data, DevActionHandler hdl, + DevKillActionData *killFunc, DevPrio prio) { + DevAction *action; + action = calloc(1, sizeof(*action)); + assert(action); + action->data = data; + action->hdl = hdl; + action->kill = killFunc; + action->prio = prio; + action->next = NULL; + return action; +} + +void DevKill(DevSer *devser) { + SchedHeader *h, *victim; + + if (devser->asyncConn) { + AsconKill(devser->asyncConn); + } + DevFreeActionList(devser->actions); + h = devser->headers; + while (h != NULL) { + victim = h; + h = victim->next; + DevFreeActionList(victim->actions); + free(victim); + } + if (devser->stopTask) { + free(devser); + } else { + devser->stopTask = 1; + } +} + +void DevQueue(DevSer *devser, void *actionData, DevPrio prio, + DevActionHandler hdl, DevActionMatch *matchFunc, + DevKillActionData *killFunc) { + DevAction *action, **ptr2Last; + DevAction *new; + + if (prio <= NullPRIO) prio = NullPRIO + 1; + if (prio >= NumberOfPRIO) prio = NumberOfPRIO - 1; + ptr2Last = &devser->actions; + for (action = devser->actions; action != NULL && action->prio >= prio; action = action->next) { + if (action->hdl == hdl && matchFunc(actionData, action->data)) { + return; /* there is already an identical action */ + } + ptr2Last = &action->next; + } + new = DevNewAction(actionData, hdl, killFunc, prio); + new->next = action; + *ptr2Last = new; +} + +int DevUnschedule(DevSer *devser, void *actionData, + DevActionHandler hdl, DevActionMatch *matchFunc) { + SchedHeader *header = NULL; + DevAction **ptr2Last = NULL; + DevAction *action = NULL; + int cnt=0; + + /* scan through all headers */ + for (header = devser->headers; header != NULL; header = header->next) { + ptr2Last = &header->actions; + for (action = header->actions; action != NULL; action = *ptr2Last) { + if (action->hdl == hdl && matchFunc(actionData, action->data)) { + if (action == header->followingAction) { + /* advance followingAction if equal*/ + header->followingAction = action->next; + } + if (action == devser->current) { + devser->current = NULL; + devser->killCurrent = 0; /* should already be 0 */ + } + cnt++; + /* remove from list */ + *ptr2Last = action->next; + if (action->kill != NULL) action->kill(action->data); + free(action); + } else { + ptr2Last = &action->next; + } + } + } + return cnt; +} + +int DevSchedule(DevSer *devser, void *actionData, + DevPrio prio, double interval, + DevActionHandler hdl, DevActionMatch *matchFunc, + DevKillActionData *killFunc) { + SchedHeader *header = NULL; + SchedHeader **ptr2LastHeader = NULL; + SchedHeader *newHeader; + DevAction *action = NULL; + DevAction **ptr2Last = NULL; + DevAction *newAction; + int ret; + + if (prio <= NullPRIO) prio = NullPRIO + 1; + if (prio >= NumberOfPRIO) prio = NumberOfPRIO - 1; + ret = DevUnschedule(devser, actionData, hdl, matchFunc); + + newAction = DevNewAction(actionData, hdl, killFunc, prio); + /* find matching header */ + ptr2LastHeader = &devser->headers; + for (header = devser->headers; header != NULL; header = *ptr2LastHeader) { + if (header->prio == newAction->prio && header->interval == interval) { + /* append new action at the tail */ + ptr2Last = &header->actions; + for (action = header->actions; action != NULL; action=action->next) { + ptr2Last = &action->next; + } + *ptr2Last = newAction; + assert(newAction->next == NULL); + return ret; + } else if (header->prio < newAction->prio || + (header->prio == newAction->prio + && header->interval > interval)) { + break; + } + if (header->actions == NULL) { + /* remove empty header */ + *ptr2LastHeader = header->next; + free(header); + } else { + ptr2LastHeader = &header->next; + } + } + + /* insert new header */ + newHeader = calloc(1, sizeof(*newHeader)); + assert(newHeader); + newHeader->actions = newAction; + newHeader->followingAction = NULL; + newHeader->prio = newAction->prio; + newHeader->interval = interval; + newHeader->next = header; + newHeader->timeDue = DoubleTime() + interval; + *ptr2LastHeader = newHeader; + return ret; +} + +int DevRemoveAction(DevSer *devser, void *actionData) { + SchedHeader *header = NULL; + DevAction **ptr2Last = NULL; + DevAction *action = NULL; + int cnt=0; + + + /* Remove current action, if matched. If a reply is pending, the next action will + get the reply. But as in the inital state no reply is expected, this should not harm. */ + action = devser->current; + if (action != NULL && actionData == action->data) { + if (devser->killCurrent) { + if (action->kill != NULL) action->kill(action->data); + devser->killCurrent = 0; + free(action); + } + devser->current = NULL; + } + /* remove from queue */ + ptr2Last = &devser->actions; + for (action = devser->actions; action != NULL; action = action->next) { + if (actionData == action->data) { + cnt++; + /* remove from list */ + *ptr2Last = action->next; + if (action->kill != NULL) action->kill(action->data); + free(action); + } else { + ptr2Last = &action->next; + } + } + return cnt++; +} diff --git a/devser.h b/devser.h new file mode 100644 index 00000000..95f29a8a --- /dev/null +++ b/devser.h @@ -0,0 +1,123 @@ +#ifndef DEVSER_H +#define DEVSER_H + +/** \file + * \brief Device Serializer + */ + +typedef struct DevSer DevSer; + +/** \brief The action handler to be called + * \param actionData the data stored with the action + * \param lastReply the last reply or NULL when no command was + * sent in the last action + * \return the command to be sent or NULL if no command has to be sent + */ +typedef char *DevActionHandler(void *actionData, char *lastReply); + +/** \brief Check if an action matches the call data + * \param callData the callers data + * \param actionData the action data + * \return 1 on a match, 0 on no match + */ +typedef int DevActionMatch(void *callData, void *actionData); + +/** \brief Kill ActionData + * \param actionData action data + */ +typedef void DevKillActionData(void *actionData); + +/** \brief possible priorities. + * NullPRIO and NumberOfPRIO must not be used as priority + */ +typedef enum { + NullPRIO, SlowPRIO, ReadPRIO, ProgressPRIO, WritePRIO, HaltPRIO, NumberOfPRIO +} DevPrio; + +/** \brief Make a new device serializer and async connection. + * \param con the SICS connection (for error messages) + * \param argc the number of args for specifying the protocol + * \param argv the args + * \return the created device serializer or NULL on failure + */ +DevSer *DevMake(SConnection *con, int argc, char *argv[]); + +/** \brief put the device serializer into debug mode + * \param devser the device serializer + * \param steps the number of steps to be executed or -1 for disable debugging mode + */ +void DevDebugMode(DevSer *devser, int steps); + +/** \brief Kill the contents of the device serializer and its async connection. + * + * The data structure itself is killed at some time later + * \param devser the device serializer + */ +void DevKill(DevSer *devser); + +/** \brief Queue an action + * + * If a matching action with the same action handler + * exists already, no new action is queued. + * \param devser the device serializer + * \param actionData the action data + * \param prio the priority + * \param hdl the action handler + * \param matchFunc the match function + * \param killFunc the action data kill function (called from DevKill and + * after the action has finished, i.e. when hdl returned NULL) + * or NULL if no kill function is needed. + */ +void DevQueue(DevSer *devser, void *actionData, DevPrio prio, + DevActionHandler hdl, DevActionMatch *matchFunc, + DevKillActionData *killFunc) ; + +/** \brief Schedule a periodic action + * + * If a matching action exists already, + * it is overwritten with a possibly changed interval and priority. + * \param devser the device serializer + * \param actionData the action data + * \param prio the priority + * \param interval the interval in seconds (0 is allowed) + * \param hdl the action handler + * \param matchFunc the match function (callData must be of the same type as actionData) + * \param killFunc the action data kill function (called from DevKill and + * from DevUnschedule) or NULL if no kill function is needed. + * \return 0 when this was a new action, > 0 when an action was overwritten + */ +int DevSchedule(DevSer *devser, void *actionData, + DevPrio prio, double interval, + DevActionHandler hdl, DevActionMatch *matchFunc, + DevKillActionData *killFunc); + +/** \brief Unschedule matching actions + * \param devser the device serializer + * \param callData the callers data to be as first argument of the match function + * \param hdl the action handler + * \param matchFunc the match function (callData does not need to have the same type as actionData) + * \return the number of unscheduled actions + */ +int DevUnschedule(DevSer *devser, void *actionData, + DevActionHandler hdl, DevActionMatch *matchFunc); + +/** \brief remove action from the serializer + * \param devser the device serializer + * \param actionData the action data to be compared for a match + */ +int DevRemoveAction(DevSer *devser, void *actionData); + +/** \brief Convert integer priority to text + * \param prio + * \return text + */ +char *DevPrio2Text(DevPrio prio); + +/** \brief Convert text priority to integer + * \param text + * \return prio + */ +DevPrio DevText2Prio(char *text); + + +#endif diff --git a/diffscan.c b/diffscan.c index 597edb90..08fd95d3 100644 --- a/diffscan.c +++ b/diffscan.c @@ -70,7 +70,7 @@ int MakeDiffScan(SConnection *pCon, SicsInterp *pSics, void *pData, pNew->pDes->SaveStatus = SaveDiffScan; if(argc > 1) { - status = AddCommand(pSics,argv[2], + status = AddCommand(pSics,argv[1], DiffScanWrapper, KillDiffScan, pNew); @@ -336,7 +336,7 @@ static int DiffScanTask(void *pData){ check for interrupt */ if(SCGetInterrupt(self->scanObject->pCon) >= eAbortScan){ - pCount->pDriv->Halt(pCount->pDriv); + pCount->pCountInt->Halt(pCount); pVar->pInter->Halt(pVar->pObject); SicsWait(1); finish = 0; diff --git a/doc/manager/nxscript.htm b/doc/manager/nxscript.htm index 04db765e..803d6fe0 100644 --- a/doc/manager/nxscript.htm +++ b/doc/manager/nxscript.htm @@ -104,7 +104,7 @@ the dictionary file: would denote the normal counting tube at a scanning type of experiment. -
nxscript puthm hmAlias hmName ?start? ?length? +
nxscript puthm hmAlias hmName ?start? ?length? ?bank?
Writes data from the histogram memory hmName to a NeXus file using the alias hmAlias. Nxscript automatically updates the dim0, dim1, ..., timedim dictionary variables. Thus these can be used to define the dimensions in the @@ -116,7 +116,9 @@ subset writing, the dimensions have to be specified in the definition string belonging to the alias. Nxscript sets a variable timedim in the dictionary though which contains the length of a time binning if appropriate. This is a special feauture for writing extra detectors at -SANS and AMOR. +SANS and AMOR. Optionally, together with start and length, a bank number can +be given. This is a feauture to support the rare case of having multiple banks +in one histogram memory. If not give bank defaults to 0.
nxscript puttimebinning aliasName hmName
Writes the time binning at histogram memory hmName to file using the alias aliasName. The length of the time binning data is @@ -125,14 +127,28 @@ automatically appended to the definition string for the alias.
Writes the Tcl array arrayName to file using the aliasName. The definiton string belonging to aliasName does not need to contain a -dim argument as this is set by this routine. The parameter length is -the length of the array. Only rank 1 arrays are supported. +the length of the array. Only rank 1 arrays are supported. The array is +considered a float array. +
nxscript putintarray aliasName arrayName length +
The same as above, but the data is considered integer.
nxsript putglobal attName bla bla bla
This writes an global attribute attName. Everything after attName is concatenated to a string which then respresents the value of the -attribute. +attribute. +
nxscript putsicsdata alias dataname +
Writes the sicsdata array dataname to alias. +
nxscript putattribute alias name value +
Add another text attribute to alias. The name of the attribute is name, the + value value.
nxscript makelink targetAlias victimAlias
This creates a symbolic link for victimAlias in the group designated by targetAlias. +
nxscript putslab alias startlist sizelist obj +
Writes a slab of data. alias is the alis to write too. startslist is + a Tcl list containing the start indexes, sizelist is the size of the slab + to write as a Tcl list and obj is the SICS object to write. Obj can be a + histogram memory; then the histogram memory data is written. Or it can be + a sicsdata object, the value of which will then be written.

Automatic Updating of NeXus Files

diff --git a/doc/manager/nxupdate.htm b/doc/manager/nxupdate.htm index 6b0e1373..0c20845d 100644 --- a/doc/manager/nxupdate.htm +++ b/doc/manager/nxupdate.htm @@ -65,6 +65,9 @@ file.
updateintervall
The time intervall in seconds between updates. The defualt is 1200, eg. 20 minutes. +
onoff +
can be 1 or 0. Switches automatic updates on or off. It might be usefule for + scans to switch this off.

diff --git a/doc/manager/tas.htm b/doc/manager/tas.htm index 4a774030..f7c1b85d 100644 --- a/doc/manager/tas.htm +++ b/doc/manager/tas.htm @@ -21,6 +21,37 @@ The TAS requires the following initializations in its instrument file:
MakeTasUB tasub
Installs the TAS crystallographic calculation module into SICS. It will have the name tasub (recommended). +
MakeTasUB tasub a1 a2 mcv mch a3 a4 sgu sgl a5 a6 acv ach +
Installs the TAS crystallographic calculation module into SICS. It will + have the name tasub (recommended). This versions allows to specifiy motor names for functions. If there is no motor for + a function it can be replaced with a placeholder in the parameter list, like dummy. This is only allowed for the + curvature motors. The motor functions: +
+
a1 +
monochormator rotation +
a2 +
monochromator two theta +
mcv +
monochromator vertical curvature +
mch +
monochromator horizontal curvature +
a3 +
sample rotation +
a4 +
sample tow theta +
sgu +
sample tilt +
sgl +
second sample tilt +
a5 +
analyzer rotation +
a6 +
analyzer two theta +
acv +
analyzer vertical curvature +
ach +
analyzer horizontal curvature +
MakeTasScan iscan tasub
Installs the module with the TAS specific scan functions into SICS. The TAS implements its own data format resembling the ILL TAS data format. diff --git a/doc/user/commandlog.htm b/doc/user/commandlog.htm index 2dcd7216..bff36ae1 100644 --- a/doc/user/commandlog.htm +++ b/doc/user/commandlog.htm @@ -35,6 +35,11 @@ started. In order to make this work a ServerOption with the name logstartfile must exist in the instrument configuration file. The value of this option must be the full path name of the file to execute.

+

+Note: with the command config listen 1 you can have the output +to the command log printed into your client, too. With config listen 0 you can switch this off again. This is useful for listening into a running + instrument. +

diff --git a/doc/user/exeman.html b/doc/user/exeman.html index 963493f3..f0880351 100644 --- a/doc/user/exeman.html +++ b/doc/user/exeman.html @@ -45,6 +45,13 @@ named buffer within the stack of nested buffers.
Clears the queue of batch buffers
exe queue
Prints the content of the batch buffer queue. +
exe fullpath filename +
Prints the full path name for filename if the file can be located + somewhere in exe paths. Else an error is printed. The purpose is to use + exe file management facilties in scripts. +
exe makepath filename +
Prints the full path name for filename in the first direcory of batch path. + This is a tool to have scripts open files in the proper user directory.
exe run
Starts executing the batch buffers in the queue.
exe print buffername @@ -58,7 +65,12 @@ most useful for SICS clients watching the progress of the experiment.
exe append some text
Appends a line with everything after append to the upload buffer
exe save filename -
saves the recently uploaded buffer under filename on the SICS server. +
saves the recently uploaded buffer under filename on the SICS server. Does not overwrite + existing files. +
exe forcesave filename +
saves the recently uploaded buffer under filename on the SICS server. Overwrites existing file. +
exe clearupload +
clears any pending upload operations.

diff --git a/doc/user/macro.htm b/doc/user/macro.htm index 0b07be0a..cba3c764 100644 --- a/doc/user/macro.htm +++ b/doc/user/macro.htm @@ -7,7 +7,7 @@

SICS has a built in macro facility. This macro facility is aimed at instrument managers and users alike. Instrument managers may provide customised measurement procedures in this language, users may write batch files in this language. The macro language is John Ousterhout's Tool Command Language (TCL). Tcl has control constructs, variables of its own, loop constructs, associative arrays and procedures. Tcl is well documented by several books and online tutorials, therefore no details on Tcl will be given here. All SICS commands are available in the macro language. Some potentially harmful Tcl commands have been deleted from the standard Tcl interpreter. These are: exec, source, puts, vwait, exit,gets and socket. A macro or batch file can be executed with the command:

- fileeval name tries to open the file name and + exe name tries to open the file name and executes the script in this file.

diff --git a/doc/user/optimise.htm b/doc/user/optimise.htm index 8d80b76c..4e97d439 100644 --- a/doc/user/optimise.htm +++ b/doc/user/optimise.htm @@ -29,6 +29,18 @@ maximum number of cycles was reached. This routine requires that the instrument is currently placed somewhere on the peak and not miles away.

+The peak optimiser supports another optimisation algorithm which is faster but +may not be as accurate. This is hill climbing: +

+while errors gt precision and cycles lt maxcycles
+  for all variables 
+	  find the direction into which the intensity rises
+	  step into this direction until the intensity drops
+   end for
+end while
+
+

+

The Peak Optimiser is implemented as an object with the name opti. It understand the following commands:

@@ -43,7 +55,10 @@ and number of steps parameters should cover the whole peak. However, the Optimiser will extend the scan is the specified range is not sufficient.
opti run
Starts the optimiser. It will then optimise the peak. This may take some -time. +time as it uses a time consuming scan based algorithm. +
opti climb +
Starts the optimiser in hill climbing mode. Hill climbing is faster but may + not be as accurate as a scan based optimization.
The behaviour of the optimiser can be configured by modifying some parameters. The synatx is easy: opti parameter prints the value of the @@ -72,5 +87,12 @@ status of the countmode parameter this is either a preset time or a preset monitor.

+

+It is the users reponsability to provide meaningful step widths. Usually this is +dependent on the instrument resolution and thus fairly constant. Also these +optimisation algorithms will fail if the instrument is not positioned at the +flank of a peak. Probaly the best will be to do several cycles of hill +climbing first, followed by one cycle of scan optimisation for extra accuracy. +

diff --git a/doc/user/sansdoc.tgz b/doc/user/sansdoc.tgz new file mode 100644 index 00000000..ebc3db41 Binary files /dev/null and b/doc/user/sansdoc.tgz differ diff --git a/doc/user/sansdocbook.xml b/doc/user/sansdocbook.xml new file mode 100644 index 00000000..9f02cdc6 --- /dev/null +++ b/doc/user/sansdocbook.xml @@ -0,0 +1,5284 @@ + + + + + SICS manual for the SANS I instrument + + + + Dr + Joachim + Kohlbrecher + +
+ + Paul Scherrer Institute PSI + LNS, Laboratory for Neutron Scattering + + Villigen + 5232 + Switzerland +
+
+ + + + Dr + Mark + Könnecke + +
+ + Paul Scherrer Institute PSI + LDM, Laboratory for Developments and Methods + + Villigen + 5232 + Switzerland +
+
+
+ + + Abstract + This manual describes how to set up the small angle neutron scattering instrument at + the SINQ spallation source at PSI and gives a short remainder of important command. + This document can be downloaded + http://sans.web.psi.ch/SANSDoc/SANSDoc.ps.gz + as a gzipped postscript file. + + + +
+ + + Quick reference Guide +
+ Instrument control programs + + + + + + startsics & + + + + starts the SICS server. + + + + + + + killsics & + + + + kills the SICS server. + + + + + + + sics & + + + + starts the SICS command line client. + + + + + + + sansstatus & + + + + starts the SANS status display. + + + + + + + varwatch & + + + + starts the variable watcher. + + + + + + +
+
+ The <computeroutput>token</computeroutput> command + + + + + + token grab + + + + reserves control over the instrument to the client issuing this command. + + + + + + + token release + + + + releases the token. + + + + + + + token force <passwd> + + + + This command forces an exiting grab on a token to be released (manager privileges required). + + + + + + + +
+
+ Executing a macro + + + + + + FileEval <file> + + + + executes the script in the file <file> (needs absolute path). + + + + + + + BatchRoot [<path>] + + + + defines directory for script files executed by BatchRun. + + + + + + + Batchrun <filename> + + + + executes script file <filename> + located in directory defined by BatchRoot. + + + + + + + exe BatchPath [<path>] + + + + defines directory for script files executed by exe. + + + + + + + exe <filename> + + + + executes script file <filename> + located in directory defined by exe BatchPath. Compared to + BatchRun, this command allows enhanced batch control through + the SICSBatchEditor. + + + + + + + Clientput <text> + + + + writes <text> + to client. + + + + + + + TCL command language interface + + + + is implemented in the SICS server. + + + + + + +
+
+ Logging executed commands + + + + + + commandlog new <filename> + + + + starts a new commandlog writing to file <filename> + (file stored in /home/SANS/log). + + + + + + + commandlog + + + + displays status of commandlog. + + + + + + + commandlog close + + + + closes the command log. + + + + + + + commandlog auto + + + + switches the automatic log on. + + + + + + commandlog tail <n> + + + + prints the last <n> lines of the log file. + + + + + + +
+
+ Driving a motor + + + + + + run <mot1> <val1> [<mot2> <val2> ...] + + + + starts motion of the motors to the new values without waiting for the requested operation to finish. + + + + + + + drive <mot1> <val1> [<mot2> <val2> ...] + + + + drives the motors and waits until movement is finished. + + + + + + + success + + + + waits and blocks the command connection until all pending operations have finished. + + + + + + + <mot> list + + + + lists all the motor <mot> parameters. + + + + + + +
+
+ Motor handling + + + + + + <motor> list + + + + lists all motor parameters. + + + + + + + <motor> reset + + + + resets motor parameters to default values. + + + + + + + <motor> position + + + + prints actual position. + + + + + + + <motor> interest + + + + prints position changes. + + + + + + + <motor> hardlowerlim + + + + prints hardware lower limit. + + + + + + + <motor> hardupperlim + + + + prints hardware upper limit + + + + + + + <motor> softlowerlim [<val>] + + + + prints/sets software lower limit. + + + + + + + <motor> softupperlim [<val>] + + + + prints/sets software upper limit. + + + + + + + <motor> softzero [<val>] + + + + prints/sets software zero. + + + + + + + <motor> fixed [<val>] + + + + fixes motor for <val> ≥ 0 or releases it for + <val> < 0. + + + + + + + <motor> interruptmode [<val>] + + + + defines interrupt issued if motor movement fails. + + + + + + + <motor> precision [<val>] + + + + denotes the positioning precision. + + + + + + + <motor> accesscode [<val>] + + + + specify user privileges of <motor>. + + + + + + + <motor> speed + + + + defunct. + + + + + + + <motor> sign [<val>] + + + + reverses operation sense. + + + + + + + +
+
+ Special commands (e.g. <computeroutput>st</computeroutput>, + <computeroutput>dt</computeroutput> ...) + + + + + + <cop> + + + + lists current position. + + + + + + + <cop> back + + + + drives component to last position. + + + + + + + <cop> pos <name> + + + + defines a name for actual position. + + + + + + + <cop> <name> + + + + drives component to named position. + + + + + + + <cop> drop <name> + + + + deletes named position. + + + + + + + <cop> drop all + + + + deletes all named positions. + + + + + + + <cop> list + + + + lists all named position. + + + + + + + <cop> find + + + + returns name of actual position. + + + + + + + <cop> <axis> [=] <val> ... + + + + drive component to new position. + + + + + + +
+
+ Sample environment devices + + + + + + EVFactory new <ED> <type> <par> ... + + + + creates a new sample environment device called <ED>. + + + + + + + EVFactory del <ED> + + + + deletes sample environment device <ED>. + + + + + + + emon list + + + + lists all registered environment devices. + + + + + + + emon register <ED> + + + + registers environment device <ED>. + + + + + + + emon unregister <ED> + + + + unregisters environment device <ED>. + + + + + + + <ED> Tolerance [<val>] + + + + allowed deviation from preset. + + + + + + + <ED> Access [<val>] + + + + changes access to device. + + + + + + + <ED> Lowerlimit [<val>] + + + + lower limit for controller. + + + + + + + <ED> Upperlimit [<val>] + + + + upper limit for controller. + + + + + + + <ED> ErrHandler [<val>] + + + + error handler to use for this controller. + + + + + + + <ED> Interrupt [<val>] + + + + interrupt to issue when an error is detected. + + + + + + + <ED> SafeValue [<val>] + + + + The value to drive the controller to when an error is detected and safe error handling is set. + + + + + + + <ED> send <par> [<par> ...] + + + + sends everything after send directly to the controller and returns its response. + + + + + + + <ED> list + + + + lists all parameters. + + + + + + + <ED> [<val>] + + + + returns current value or will drive device to new value <val>. + + + + + + + <ED> log on/off + + + + switches logging on/off. + + + + + + + <ED> log clear + + + + clears all recorded time stamps and values. + + + + + + + <ED> log gettime + + + + retrieves list of all recorded time stamps. + + + + + + + <ED> log getval + + + + retrieves list of all recorded values. + + + + + + + <ED> log getmean + + + + calculates mean value and standard deviation of logged values. + + + + + + + <ED> log frequency [<val>] + + + + specifies the time intervall in seconds between log records. + + + + + + +
+
+ Beam shutter + + + + + + shutter + + + + returns status of instrument shutter. + + + + + + + shutter open + + + + opens beam shutter. + + + + + + + shutter close + + + + closes beam shutter. + + + + + + +
+
+ Neutron Velocity Selector + + + + + + lambda + + + + prints the current wavelength in nm. + + + + + + + drive lambda <val> + + + + changes wavelength to value <val> in nm. + + + + + + + lambda rot <val> + + + + calculates the rotation speed for the wavelength given by <val>. + + + + + + + lambda wl <val> + + + + calculates the wavelength for the rotation speed given by <val>. + + + + + + + nvs status + + + + prints status summary. + + + + + + + nvs list + + + + displays rotation speed and tilt angle. + + + + + + + nvs [rot=<val1>] [tilt=<val2>] + + + + sets a new tilt angle and/or rotation speed. + + + + + + + nvs rotinteres + + + + enables printing of status message about the current state of the selector when it is driven. + + + + + + + nvs loss + + + + start a loss current measurement. + + + + + + + nvswatch [<par>] ... + + + + is the object for monitoring the velocity selector and understands the commands for sample environment devices. + + + + + + +
+
+ Positioning an attenuator + + + + + + att + + + + prints the current positioned attenuator. + + + + + + + att <val> + + + + positions attenuator <val>. + Allowed attenuator numbers are: 0, 1, 2, 3, 4 and 5. + + 0 : square 50 mm x 50 mm slit, attenuation = 1 + + 1 : circular 41 x diameter 0.4 mm slit, attenuation = 1/485 + + 2 : circular 9 x diameter 2 mm slit, attenuation = 1/88 + + 3 : circular 20 mm diameter slit, attenuation = 1/8 + + 4 : circular 30 mm diameter slit, attenuation = 1/3.5 + + 5 : circular 15 mm diameter slit, attenuation = 1/?? + + + + + + + + +
+
+ Changing collimation + + + + + + coll + + + + prints the current collimatin length. + + + + + + + coll <val> + + + + positions the collimation <val>. + Allowed collimation lengths are: 1, 1.4, 2, 3, 4.5, 6, 8, 11, 15 and 18. + + + + + + +
+
+ Positioning the detector + + + + + + detectorx + + + + motor to change sample detector distance (x). + + + + + + + detectory + + + + motor for lateral displacement (y). + + + + + + + detectorrotation + + + + motor for detector rotation (phi). + + + + + + + dt [x=<val1>] [y=<val2>] [phi=<val3>] + + + + special SANS command for controlling all three detector axes together. + + + + + + +
+
+ Positioning the beam stop + + + + + + beamstopx + + + + motor for horizontal movement. + + + + + + + beamstopy + + + + motor for vertical movement. + + + + + + + bs [x=<val1>] [y=<val2>] + + + + special SANS command for controlling both beam stop axes together. + + + + + + + bsout + + + + moves beam stop out of detecion area. + + + + + + + bsin + + + + moves beam stop back into position. + + + + + + + bscfree + + + + releases the beam stop motors. + + + + + + + bschange [<val>] + + + + changes beam stop size, without parameter it returns the beam stop type. + + 1 for beam stop size of 40 mm x 40 mm + + 2 for beam stop size of 70 mm x 70 mm + + 3 for beam stop size of 85 mm x 85 mm + + 4 for beam stop size of 100 mm x 100 mm + + + + + + + + + bscslot + + + + returns value of the beam stop type. + + + + + + +
+ +
+ Sample table + + + + + + saz + + + + motor for vertical translation (z). + + + + + + + say + + + + motor for horizontal translation parallel to neutron beam (y). + + + + + + + sax + + + + motor for horizontal translation perpendicular to neutron beam (x). + + + + + + + som + + + + motor for rotation around vertical axis (phi). + + + + + + + gtheta + + + + motor for rotation around horizontal axis (theta). + + + + + + + sposi + + + + horizontal translation for linear translator (posi). + + + + + + + st [<axis n>=<val n>] ... + + + + is a special SANS command for controlling all seven sample axes together. + <axis n> can be x, y, z, omega, phi, theta, posi. + + + + + + +
+
+ Sample holder for electro magnet + + + + + + mz + + + + motor for vertical movement (z). + + + + + + + mom + + + + motor for rotation around vertical axis (omega). + + + + + + + msh [z=<val1>] [omega=<val2>] + + + + is a special SANS command for controlling both axes of the magnet sample holder together. + + + + + + +
+
+ Haake temperature controller + + + + + + inihaakearray eimer lnsa10 4000 1 + + + + + evfactory new temperature tcl eimer + + + + initialisation sequence. + + + + + + + temperature + + + + is a valid sample environment device if initialised as above. + + + + + + + temperature list + + + + overview of the temperature controller parameters. Use 'emon unregister temperature' + to avoid out of range error messages. + + + + + + + temperature sensor <val> + + + + selects the controlling sensor. <val> can be + intern or extern. + + + + + + +
+
+ Bruker electro magnet + + + + + + evfactory new magnet bruker + + + + + lnsa10.psi.ch 4000 9 + + + + initialisation sequence. + + + + + + + magnet + + + + is a valid sample environment device if initialised as above. + + + + + + + magnet polarity + + + + prints polarity of magnet. + + + + + + + magnet polarity <val> + + + + sets polarity, <val> can be + plus or minus. + + + + + + + magnet mode + + + + prints the mode of the controller. + + + + + + + magnet mode <val> + + + + sets mode, <val> can be + field or current. + + + + + + + magnet field + + + + prints the magnetic field. + + + + + + + magnet current + + + + prints the current. + + + + + + +
+
+ Eurotherm controller + + + + + + evfactory new temperature euro + + + + + lnsa10.psi.ch 4000 <port> + + + + initialisation sequence. <port> is the serial port number at sans.psi.ch where + the controller is connected (usually port 13). + + + + + + + temperature + + + + is a valid sample environment device if initialised as above. + + + + + + + temperature list + + + + overview of the temperature controller parameters. Use 'emon unregister temperature' + to avoid out of range error messages. + + + + + + +
+
+ ITC-4 and ITC-503 temperature controllers + + *outdated controllers, will be replaced* + +
+
+ Analogue and digital input and output + + + + + + init_adios.tcl + + + + + initialisation sequence. + + + + + + + AO <channel> <value> + + + + sends an analogue signal (-10V -> 10V) to channel 0 or 1. + + + + + + AI <channel> + + + + reads the tension at channels 0 to 7. Channels 0 and 1 are reserved for the CJC and + the thermocouple readout. + + + + + + temp <type> + + + + returns the temperature as measured by a thermocouple (tc) or a resistor (pt). + + + + + + log <time interval> <channel1> + <channel2> ... + + + + stores the values measured at the specified channels into a file in the + speficied time interval. Up to 4 channels can be logged simultanuously. + The temperature can be logged using channel 1. + + + + + + log stop + + + + stops logging the data. + + + + + + +
+
+ Counter handling + + + + + + counter SetPreset <val> + + + + sets the preset to <val>. + + + + + + + counter GetPreset + + + + prints the current preset value. + + + + + + + counter SetExponent <val> + + + + sets exponent for Monitor mode. + + + + + + + counter GetExponent + + + + prints current exponent used in Monitor mode. + + + + + + + counter SetMode <val> + + + + sets counting mode. Allowed modes are Timer + and Monitor. + + + + + + + counter GetMode + + + + prints the current mode. + + + + + + + counter GetCounts + + + + prints counts gathered in the last run. + + + + + + + counter GetMonitor <n> + + + + prints counts gathered by monitor <n> in the last run. + + + + + + + counter Count <preset> + + + + starts counting with preset <preset>. + + + + + + + counter Status + + + + prints the counter status. + + + + + + + counter GetTime + + + + retrieves the actual time the counter counted for. + + + + + + + counter GetThreshold <n> + + + + retrieves the threshold of monitor <n>. + + + + + + + counter SetThreshold <n> <val> + + + + sets the threshold for monitor <n> to + <val> and also sets the active monitor for the threshold + to <n>. + + + + + + +
+
+ Histogram Memory + + + + + + banana configure HistMode <val> + + + + sets the mode of operation (Transparent, Normal, TOF, Stroposcopic). + + + + + + + banana configure OverFlowMode <val> + + + + determines how bin overflow is handled (Ignore, Ceil, Count). + + + + + + + banana configure Rank <val> + + + + defines number of histograms in memory. + + + + + + + banana configure Length <val> + + + + gives length of individual histogram. + + + + + + + banana configure BinWidth <val> + + + + determines size of single bin in histogram memory in bytes. + + + + + + + banana timebin + + + + prints actual time binning aray. + + + + + + + banana genbin <start> <step> <n> + + + + determines size of single bin in histogram memory in bytes. + + + + + + + banana setbin <inum> <val> + + + + configuring unequally spaced time binnings. + + + + + + + banana clearbin + + + + deletes the binning informations. + + + + + + + banana preset [<val>] + + + + prints/sets preset Timer or Monitor. + + + + + + + banana exponent [<val>] + + + + prints/sets exponent for Monitor preset. + + + + + + + banana CountMode [<mode>] + + + + prints/sets count mode (Monitor, Timer). + + + + + + + banana init + + + + transfers configuration from host to the actual histogram memory. + + + + + + + banana count + + + + starts counting. + + + + + + + banana initval <val> + + + + initialises histogram memory to value <val>. + + + + + + + banana get <i> <istart> <iend> + + + + retrieves a part of histogram memory. + + + + + + + banana sum <d0min> <d0max> <d1min> <d1max> + + + + returns the sum of counts on an area of the detector. + + + + + + +
+
+ Data acquisition + + + + + + StoreData + + + + writes current instrument status to a NeXus file. + + + + + + + count [<mode> <preset>] + + + + start count operation in mode <mode> + with preset <preset>. + + + + + + + repeat <num> [<mode> <preset>] + + + + calls <num> times count. + + + + + + +
+
+ XY table + + + + + + xydata clear + + + + clears all entries in the x-y table. + + + + + + + xydata list + + + + lists the entries in the x-y table on the screen. + + + + + + + xydata write <filename> + + + + writes the x-y list to the disk file <filename>. + This file resides on the machine running the SICS server. + + + + + + + xydata uuget + + + + sends the x-y list in an uuen-coded format. This is the command to give to the + VarWatch SICS client in order to make it display the x-y list. + + + + + + + xydata add <xval> <yval> + + + + creates a new x-y list entry with the values <xval> and <yval>. + + + + + + +
+ +
+ + + How to start +
+ Log in on the SANS computer + + After starting up an X-Terminal click on the XDMCP button and choose + the computer lnsa10.psi.ch. Login with your + <username> + and <password> + (case sensitive, this is a Unix machine). Wait for the Common Desktop + Environment to start up. Open a terminal window (dtterm). + You will be asked for your last name and if this is your first login a + subdirectory /data/lnsg/<lastname> + will be created. + +
+
+ Start the SINQ instrument control software SICS + + SICS is a client server system. This means there are at least two programs + necessary to run the experiment. The first is the server program, which + runs for the SANS instrument on the lnsa10.psi.ch + workstation. A user rarely needs to bother about this program as it meant to run + all the time. Secondly a client program is needed, through which the user can interact + with the instrument control server. Its main purpose is to forward commands + to the server and to display the answers. For the SANS instrument three + SICS clients are available: + + + command line control client (sics &) + + + + status display (sansstatus &) + + + + variable watcher (varwatch) + + + + + Any of the SICS clients first have to be connected to a SICS server before + it becomes active.Therefor you first have to establish a connection through + the [Connect] menu of the client. + + +
+ Command control line client + + The command line control client allows you after connecting to the + instrument server to read out instrument parameters like + motor positions, temperatures, magnetic field, etc. In order to move a motor + or to start a data aquisition you need access privileges. The SICS + server supports autorisation on different levels to protect the + instrument against unauthorized hackers or accidental change of + the instrument adjustment of less knowledgable user. Four different + levels of access to the instrument are supported. + + + Spy: you may read out any + instrument value, but may not change anything + + + + + User: you are privileged + to perform a certain amount of operations necessary + to run the instrument + + + + + Manager: you have the + permission to do almost everything + + + + + Internal: is not accessible + to the outside world and is used to circumvent + protection for internal uses + + + + + To change the privileges select [Set Rights] + from the [User Parameter] menu. + With an valid username and the corresponding password you will get + the privileges to change the instrument set-up. Please do not use this privileges + on any other terminal than the one in the SANS cabine. For more information click + on [Help] button on the top right corner of the + command line client. A list of the most needed SICS commands is given in + . If you start the sics client the first time you + should fill out this items in the menu + [New Experiment] for documentation + of your data. + +
+
+ SANS status display + + The SANS status display is an application for displaying the status of a SANS + measurement at SINQ. The application can be stated by the command + sansstatus &. First a connection to the + SICS server has to be established. You can choose between two data display areas. + The first form shows a large colour mapped image of the detector. + The second form shows a couple of selected instrument parameters. + Switching between the two displays is achieved through the buttons + [Detector] and [Parameter] + of the tool bar. + + + The displayed data window can be updated either manually or automatic. By + default manual update is enabled. Manual update happens when hitting the + red button at the bottom. Automatic update can be enabled by checking the + CheckBox labelled [Automatic Update] in + the [Update] menu. The update intervall + can be configured with a dialog which shows up when the + [Set Update Intervall] menu selection is + clicked. Automatic updates impose a high network load. Use with care and + only when necessary. For this reason, automatic updates are disabled in + the applet version of this program. An updates take approximatly 5 seconds. + Consequently update intervalls shorter than that are nonsense. Speeding + this up an [Turbo Update] button has been + introduced. In this modus the client doesn't communicate with the SICS + server to recieve the detector information, but it communicates directly + with the histograming memory. This speeds the update frequency up to a few + frames per second. [Turbo Update] will + continuously poll the histogram memory for new histogram data. This is only + limited by CPU, network and histogram memory performance. Thus + [Turbo Update] tends to overload your + system. It should only be used while adjusting the beamstop, otherwise its + use is a waste of system resources which will eventually slow down your + data reduction chores. Do NOT forget to switch this off when you are done. + The system will automatically switch to [Auto Update] + mode after 20 minutes. + + + The detector display has a lot of options. Below the colour picture there + is a line which displays the current detector coordinate and the data + value at the current mouse position. By choosing the + [Open Old File] item in the [File] + menu you can load an old data set, i.e. you + can use the SANS status display as a viewer for old SANS data files. A + double clicking on the colour display brings up a dialog which allows to + configure the colour mapping of the detector display. By + holding down the mouse button and dragging the mouse a rectangular + area of the detector display can be selected. When releasing the mouse + button, the selected region is summed and displayed as a X-Y graph + in a separate window. In the title of this plot the total + counts in the selected rectangular area on the detector is printed. + Dragging the mouse downwards in this new window allows to zoom in into + details of the histogram. Dragging the mouse upwards zooms out again. + At the bottom of the window is a dismiss button which removes the + histogram window. The histogram in the histogram window will NOT be + automatically updated when new data arrives. + +
+
+ Variable watcher <computeroutput>varwatch</computeroutput> + + + This little SICS client allows to plot the value of a variable in a + SICS server against time. This will probably be mostly used for + watching the temperature stability of a temperature controller. + But it is not restricted to temperature variables, all numeric SICS + variables can be monitored that way. + + + The configuration of the variable watcher involves two steps: + + + Select your favourite + SICS server from the choices in the + [Connect] menu. + [Custom] allows to + specify a SICS server directly by host name and port number. + + + + Configure the variable to watch using the + [Configure] item in the + [Plot] menu. Three + parameters are relevant: + + + The watch frequency, which is the + time in minutes between plot updates. + + + + The backlog, which is the number + of values which will be plotted. + + + + + The SICS variable to watch. This text + must be a valid SICS command which returns + a reply in the form + blabla = <number> + . + + + + + + + + + Further user interface elements are two buttons below the plot. + These allow to start and stop the recording of the variables value. + The yellow text field besides these buttons displays the current + operation of the variable watcher which can be one of Inactive or + Recording. Below is a line showing the current status of the SICS + server. Please note, that the most recent value is always at the + right hand side of the plot. + + + The plot can be zoomed into by dragging downwards with the left + mouse button. Zooming out is achieved by dragging outwards with + the mouse or with the [Reset] + or [Rescale Y] options in the + [Plot] menu. The plot can be + printed using the [Print] option + in the [Plot] menu. Next to + plotting a variable in a SICS server against time the variable + watcher can be used to display a XYTable + which is a class maintaining a list of x-y values. If a + XYTable object is available in + the system under the name <xydata> + the command <xydata> uuget has + to be given to the varwatch SICS + client in order to make it display the x-y list. More information + about handling a XYTable can be + found in . + +
+
+
+ Instrument preparation before the first measurement + + Before you can start with your first data aquisition be aware of a few things. + For running a measurement you need at least to run two programs: the SANS + status display (sansstatus &) and the + command line client (sics &). After + connecting both clients to the SANS server and authorizing the command line + client at the server with user privileges you are ready to change the instrument + set-up. The initial step at the beginning of your measurement are the following: + + Check if beam is closed, if not than do it now. + + + + Set the experiment informations via the dialog box + [New Experiment]. + + + + Define the directory, where you intend to store your batch + files with batchroot /data/lnsg/<username>[/<batchdir>] + resp. with + exe batchpath /data/lnsg/<username>[/<batchdir>]. + + + + Choose the needed instrument settings like sample detector + distance, collimation, wavelength, etc. + + + + Adjust the beam stop by performing the following steps: + + + Move in an attenuator with an attenuation factor + larger than 100. + + + + Put a strong forward scatterer into the beam, + e.g. teflon + + + + Center the beam stop roughly by the SICS + command bs x 0 y 0. + + + + Open the beam. + + + + Use the command banana preset 1000 + and banana count + to start an aquisition without storing + the result. + + + + Adjust the beam stop with the + bs-command. + + + + After each beam stop movement you can reset the + histograming memory of the detector by + banana initval 0. + + + + Remove the attenuator. + + + + Perform a fine adjustment of the beam stop with an + unattenuated beam if neccessary. + + + + Stop the data aquisition by pressing the + [Interrupt] button in the + lower left corner of the command line client. + + + + + + + +
+
+ + + Instrument control commands +
+ Some basic SICS commands and concepts +
+ The <computeroutput>token</computeroutput> command + In SICS any client can issue commands to the SICS server. + This is a potential cause for trouble with users issuing conflicting + commands without knowing. In order to deal with this problem a token + mechanism has been developed. A connection can grab a token and then + has full control over the SICS server. Any other connection will not + be privileged to do anything useful, except for looking at things. + A token can be released manually with a special command or is automatically + released when the connection dies. Another command exists which allows + a SICS manager to force his way into the SICS server. The commands in + more detail: + + + + token grab + + + + Reserves control over the instrument to the client issuing this + command. Any other client cannot control the instrument now. + However, other clients are still able to inspect variables. + + + + + + token release + + + + Releases the control token. Now any other client can control + the instrument again. Or grab the control token. + + + + + + token force <password> + + + + This command forces an existing grab on a token to be released. + This command requires manager privilege. Furthermore a special + password must be specified as third parameter in order to do + this. This command does not grab control though. + + + + + +
+
+ How to execute Macros + + SICS has a built in macro facility. This macro facility is aimed at instrument + managers and users alike. Instrument managers may provide customised measurement + procedures in this language, users may write batch files in this language. + The macro language is John Ousterhout's + http://cseng.awl.com/authordetail.qry?AuthorID=69 + + Tool Command Language (TCL) http://www.tcltk.com + . + A set of important Tcl commands are described in section . + To execute batch files three commands are available: + + + + FileEval <batchfile> + + + + This command tries to open the file + batchfile and executes the + script in this file. If not an absolute path name is defined + the SICS server will search in the directory + /home/SANS/bin. However, you + don't have privileges to save files in this directory. For + executing batch files located in a directory of your choice + the commands BatchRoot and + BatchRun resp. + exe BatchPath and + exe are available. + + + + + + BatchRoot [<pathname>] + + + + By this command the directory name, in which SICS is searching + for a batch file, is stored in the SICS variable + BatchRoot. Calling + BatchRoot without parameters + will return the actual contents of the variable. + + + + + + BatchRun <filename> + + + + This command tries to open the file filename + located in the directory defined by + BatchRoot and executes the + script in this file. + + + + + + exe BatchPath [<pathname>] + + + + By this command the directory name, in which SICS is searching + for a batch file, is stored in the SICS variable + exe BatchPath. Calling + exe BatchPath without parameters + will return the actual contents of the variable. + + + + + + exe <filename> + + + + This command tries to open the file filename + located in the directory defined by + exe BatchPath and executes the + script in this file. Compared to BatchRun, + this command allows enhanced batch control through the + SICSBatchEditor. + + + + + If you want to print information from a macro script to a client (about the progress of + the batch job for example), a special command is available: + + + + ClientPut some text ... + + + + This command writes everything after ClientPut + to the client which started the script. + + + + + +
+
+ Logging the executed commands + + Some users wish to have all communication of the SICS server with all the clients + having user or manager privileges being collected in a file for further review. This + is implemented via the commandlog command. This log + allows to retrace each step of an experiment. It is usually switched off and must be + configured by the instrument manager. commandlog + understands the following syntax: + + + commandlog new <filename> + + + + starts a new commandlog writing to + <filename>. The log file can be found for the SANS server in + the directory /home/SANS/log. + + + + + commandlog + + + + without further parameters displays the status of the commandlog. + + + + + + + commandlog close + + + + closes the command log file. + + + + If the user wishes a transscript of the SICS session of the command line client he + is just working with, he can use the [Open Logfile] + option in the [File] menu. It will allow + you to specify a file on your local disk area, where all input/output is logged to. + You will get everything which appears in the client's I/O window. The input is + prepended with ???. In contrast to the commandlog, + which log communication of all clients connected to the server, the + [Open Logfile] option of the client only allows you + to log the communication of its own to a local file. + +
+
+ Some variables for documentation + SICS supports a couple of variables for documentation of a measurement. + Currently available are: + + user, adress, phone, fax, email, title, subtitle, environment, + sample, comment + + + Each variable can be inquired by just typing its name. It can be set by typing + the name followed by the new name, e.g. + title nanocrystalline Fe. + These variables can also be set by the [Set Experiment] + dialog box, which can be started via the [New Parameter] + option of the [User Parameter] menu item in the command + line client. The user is required + to fill this variables with utmost care, because if you want us to retrieve your data in + ten years time you will be happy that the information will be there. + +
+
+ Drive commands + Many objects in SICS are drivable. This means they can run to a new value. + Obvious examples are motors. Less obvious examples include composite adjustments + such as setting a wavelength or a temperature. This class of ob jects can be operated + by the drive, run + , success family of commands. These commands cater + for blocking and non-blocking modes of operation. + + + run <var> <newval> [<var> + <newval> ...] + + + + Can be called with one to n pairs of object new value pairs. This command will + set the variables in motion and return to the command prompt without waiting + for the requested operations to finish. This feature allows to do things to + the instrument while for example a slow device is running into position. + + + + + + + + success + + + + Waits and blocks the command connection until all pending operations have + finished (or an 'interrupt' occured). + + + + + + + + drive <var> <newval> [<var> + <newval> ...] + + + + can be called with one to n pairs of ob ject new value pairs. This command + will set the variables in motion and wait until the driving has finished. + A drive can be seen as a sequence of a run command as stated above immediatetly + followed by a Success command. + + + + + +
+
+ SICS motor handling + + In SICS each motor is an object with a name. Motors may take commands which basically + come in the form <motorname> <command>. + Most of these commands deal with the plethora of parameters which are associated with + each motor. The syntax for manipulating variables is, again, simple. + <motorname> <parametername> will print + the current value of the variable. <motorname> <parametername> + <newval> will set the parameter to the new value specified. A + list of all parameters and their meanings is given below. The general principle behind + this is that the actual (hardware) motor is kept as stupid as possible and all the + intracacies of motor control are dealt with in software. Besides the parameter commands + any motor understands these basic commands: + + + <motorname> list + + + + + gives a listing of all motor parameters + + + + + <motorname> reset + + + + + resets the motor parameters to default values. + + + + + <motorname> position + + + + + prints the current position of the motor. + + + + + <motorname> interest + + + + + initiates automatic printing of any position change of the motor. This + command is mainly interesting for implementors of status display clients. + + + + Please note that the actual driving of the motor is done via the + drive or run command + described in the last + + + + The motor parameters: + + + HardLowerLim + + + + + is the hardware lower limit. This is read from the motor controller and is + identical to the limit switch welded to the instrument. Can usually not be + changed. + + + + + HardUpperLim + + + + + is the hardware upper limit. This is read from the motor controller and is + identical to the limit switch welded to the instrument. Can usually not be + changed. + + + + + SoftLowerLim + + + + + is the software lower limit. This can be defined by the user in order to + restrict instrument movement in special cases. + + + + + SoftUpperLim + + + + + is the software upper limit. This can be defined by the user in order to + restrict instrument movement in special cases. + + + + + SoftZero + + + + + defines a software zero point for the motor. All further movements will + be in respect to this zeropoint. + + + + + Fixed + + + + + can be greater 0 for the motor being fixed and less than zero for the motor + being movable. + + + + + InterruptMode + + + + + defines the interrupt to issue when the motor fails. Some motors are so + critical for the operation of the instrument that all operations shall be + stopped when there is a problem. Others are less critical. This criticallity + is expressed in terms of interrupts, denoted by integers in the range 0 - 4 + translating into the interrupts: continue, AbortOperation, AbortScan, + AbortBatch and Halt. This parameter can usually only be set by managers. + + + + + Precision + + + + + denotes the precision to expect from the motor in positioning. Can usually + only be set by managers. + + + + + AccessCode + + + + + specifies the level of user privilege necessary to operate the motor. Some + motors are for adjustment only and can be harmful to move once the adjustment + has been done. Others must be moved for the experiment. Values are 0 - 3 for + internal, manager + , user, and spy. + This parameter can only be changed by managers. + + + + + Speed + + + + + defunct. + + + + + Sign + + + + + allows to reverse the operating sense of the motor. For cases where electricians + and not physicists have defined the operating sense of the motor. Usually a + parameter not to be changed by ordinary users. + + + + +
+
+ Special SANS commands + + This section describes some commands special to SANS. One feature of SANS is that + components are used as a whole rather than refering to single motors. For SANS the + beamstop, the detector and the sample table is managed like this. Within a component + each axis can be adressed specifically by using an axis + = value pair. Axis defined for each component will + be listed below. Additionally these components support common commands as well. These + mainly deal with named positions. A named position is a set of values which can be + driven to by just specifying its name. For instance: bs PositionA + drives the BeamStop bs to the + position defined as PositionA. All component drive + commands do not block, i.e. you can type further commands. If it is needed to wait + for a component to arrive, use the Success command + right after your command. + + + Commands supported by all components (in the following, the name of the component + will be represented by <COP>): + + + <COP> + + + The name of the component alone will yield a listing of the + current position of the component. This is also a way how to find out which + axis the component supports. + + + + + + <COP> back + + + drives the component back to the position before the last + command. Please note, that back does not operate on itself, i.e. two times + <COP> back will not do a trick. + + + + + + <COP> pos <name> + + + This command promotes the current position of the component + to a named position <name>. Afterwards + this position can be reached by typing: <COP> <name> + . + + + + + + <COP> drop <name> + + + deletes the named position specified as second parameter. + + + + + + <COP> find + + + returns the name <name> + of the actual position of the component if the position was named before by + <COP> pos <name>. + + + + + + <COP> <axis 1> [=] <val 1> + [<axis 2> [=] <val 2> ...] + + + drives the component to the new position specified by 1-n + sets of <axis n> = <val n> + pairs. The equal sign is not mandatory and can be left out. The axis refers + to the internal axis of the component which is seen in a listing as created + by typing <COP>. A relative movement + of an axis can be performed if an ++ + or -- precedes the value for + <val n>. A value + ++10, for example would mean an increase of + the actual axis position by 10. + + + + + The components which follow the component syntax described above are bs + (beam stop), dt (detector), + st (sample table), and msh + (magnet sample holder). They are described in more detail in later sections. + +
+
+ Sample Environment Devices +
+ SICS Concepts for Sample Environment Devices + SICS can support any type of sample environment control device if there is a + driver for it. This includes temperature controllers, magnetic field controllers + etc. The SICS server is meant to be left running continuously. Therefore there exists + a facility for dynamically configuring and deconfiguring environment devices into + the system. This is done via the EVFactory command. + It is expected that instrument scientists will provide command procedures for + configuring environment devices and setting reasonable default parameters. + + In the SICS model a sample environment device has in principle two modes + of operation. The first is the drive modus. The device is monitored in this modus when a + new value for it has been requested. The second modus is the monitor modus. This modus + is entered when the device has reached its target value. After that, the device must be + continously monitored throughout any measurement. This is done through the environment + monitor or emon. The emon + command understands a few commands of its own. + + Within SICS all sample environment devices share some common behaviour concerning + parameters and abilities. Thus any given environment device accepts all of a set of + general commands plus some additional commands special to the device. + + In the next paragraphs the EVFactory, + emon and the general commands understood by any sample + environment device will be discussed. Reading this is mandatory for understanding SICS + environment device handling. Then there will be another section later on in this + manual discussing the special devices known to the system. + +
+
+ Sample Environment Error Handling + A sample environment device may fail to stay at its preset value during a + measurement. This condition will usually be detected by the + emon. The question is how to deal with this problem. + The requirements + for this kind of error handling are quite differentiated. The SICS model therefore + implements several strategies for handling sample environment device failure handling. + The strategy to use is selected via a variable which can be set by the user for any + sample environment device separatetly. Additional error handling strategies can be + added with a modest amount of programming. The error handling strategies currently + implemented are: + + + Lazy + + + Just print a warning and continue. + + + + + + Pause + + + Pauses the measurement until the problem has been resolved. + + + + + + Interrupt + + + Issues a SICS interrupt to the system. + + + + + + Safe + + + Tries to run the environment device to a value considered + safe by the user. + + + + + +
+
+ General Sample Environment Commands + + + EVFactory command: + + + EVFactory + is responsible for configuring and deconfiguring sample + environment devices into SICS. The syntax is simple: + + + EVFactory new <name> <type> <par> + [<par> ...] + + + Creates a new sample environment device. It will be known to + SICS by the name specified as second parameter + <name>. The + <type> + parameter decides which driver to use for this device. The type will be + followed by additional parameters which will be evaluated by the driver + requested. + + + + + + + EVFactory del <name> + + + Deletes the environment device + <name> from the + system. + + + + + + + emon command: + + + The environment monitor emon takes for the monitoring + of an environment device during measurements. It also initiates error handling when + appropriate. The emon understands a couple of commands. + + + emon list + + + This command lists all environment devices currently + registered in the system. + + + + + + + emon register <name> + + + This is a specialist command which registers the + environment device <name> with the + environment monitor. Usually this will automatically be taken care of + by EVFactory. + + + + + + + emon unregister <name> + + + This is a specialist command which unregisters the + environment device <name> with the + environment monitor. Following this call the device will no longer be + monitored and out of tolerance errors on that device no longer be + handled. + + + + +
+
+
+ General Commands UnderStood by All Sample Environment Devices + Please note that each command discussed below MUST be prepended with the + <name> of the environment device as configured + in EVFactory! The general commands understood by any + environment controller can be subdivided further into parameter commands and real + commands. The parameter commands just print the name of the parameter if given without + an extra parameter or set if a parameter is specified. For example: + + + Temperature Tolerance prints the value of the variable + Tolerance for the environment controller + Temperature. + + + Temperature Tolerance 2.0 sets the parameter + Tolerance for Temperature + to 2.0. + + + + + Parameters known to ANY envrironment controller are: + + + Tolerance + + + Is the deviation from the preset value which can be + tolerated before an error is issued. + + + + + Access + + + Determines who may change parameters for this + controller. Possible values are: + + 0 only internal + 1 only Managers + 2 Managers and Users + 3 Everybody, including Spy + + + + + + + + LowerLimit + + + The lower limit for the controller. + + + + + UpperLimit + + + The upper limit for the controller. + + + + + Errhandler + + + The error handler to use for this controller. + Possible values: + + 0 is Lazy + 1 for Pause + 2 for Interrupt + 3 for Safe + For an explanantion of these values see the section + about error handling above. + + + + + + + + Interrupt + + + The interrupt to issue when an error is + detected and interrupt error handling is set. + Valid values are: + + 0 for continue + 1 for abort operation + 2 for for abort scan + 3 for abort batch processing + 4 halt system + 5 exit server + For an explanantion of these values see the section + about error handling above. + + + + + + + + SafeValue + + + The value to drive the controller to when an error + has been detected and safe error handling is set. + + + + + + Additionally the following commands are understood: + + + <name> send <par> + [<par> ...] + + + Sends everything after send directly to the + controller and return its response. This is a general + purpose command which allows to manipulate controllers + and controller parameters directly. The protocoll for + these commands is documented in the documentation for + each controller. Ordinary users should not tamper with + this. This facility is meant for setting up the device + with calibration tables etc. + + + + + <name> list + + + lists all the parameters for this controller. + + + + + + <name> + + + When only the name of the device is typed it + will return its current value. + + + + + <name> <val> + + + will drive the device to the new value + <val>. Please note + that the same can be achieved by using the drive command. + + + + + <name> log on + + + Switches logging on. If logging is on, at each cycle + in the <emon> the current + value of the environment variable will be recorded together with + a time stamp. Be careful about this, for each log point a bit of + memory is allocated. At some time the memory is exhausted! + <name> log clear + frees it again and log frequency + (both below) allows to set the logging time intervall. + + + + + + <name> log of + + + Switches logging off. + + + + + <name> log clear + + + Clears all recorded time stamps and values. + + + + + <name> log gettime + + + This command retrieves a list of all recorded time + stamps. + + + + + + <name> log getval + + + This command retrieves all recorded values. + + + + + <name> log getmean + + + Calculates the mean value and the standard deviation + for all logged values and prints it. + + + + + <name> log frequency [<val>] + + + With a parameter <val> sets, + without a parameter requests the logging intervall for the log created. + This parameter specifies the time intervall in seconds between log + records. The default is 5 minutes. A value of 0 means a record for each + cycle of the SICServer. + + + + + +
+
+ +
+ TCL command language interface + The macro language implemented in the SICS server is John Ousterhout's + http://cseng.awl.com/authordetail.qry?AuthorID=69 + Tool Command Language (TCL) + http://www.tcltk.com + . Tcl has control constructs, variables of its own, loop constructs, + associative arrays and procedures. Tcl is well documented by several books + http://www.cica.indiana.edu/cica/faq/tcl/tcl.html + , online tutorials and manuals + http://www.scriptics.com/man/tcl8.0/contents.htm + . For getting further + informations on Tcl have a look on the TCL WWW Infohttp://www.sco.com/Technology/tcl/Tcl.html + + of Tcl Web serverhttp://www.tcltk.com + . All SICS commands are + available in the macro language. Some potentially harmful Tcl commands have been deleted from + the standard Tcl interpreter. These are: exec, + source, puts, + vwait, exit, + gets + and socket. Below only a small subset of the most important Tcl + commands like assigning variables, evaluating expressions, control and loop constructs are + described. For complete description of Tcl command have a look on the manual + http://www.scriptics.com/man/tcl8.0/contents.htm + pages or on one of the many books about Tcl/Tk. + +
+ + <computeroutput>set</computeroutput> - Read and Write variables + + Synopsis + + set varName ?value? + + + + Description + Returns the value of variable varName. If + value is specified, then set the value of + varName to value, + creating a new variable if one doesn't already exist, and return its value. If + varName contains an open parenthesis and ends with + a close parenthesis, then it refers to an array element: the characters before the + first open parenthesis are the name of the array, and the characters between the + parentheses are the index within the array. Otherwise + varName refers to a scalar variable. + +
+
+ + <computeroutput>expr</computeroutput> - Evaluate an expression + + Synopsis + + expr arg ?arg arg ...? + + + + Description + Concatenates arg's (adding separator spaces + between them), evaluates the result as a Tcl expression, and returns the value. The + operators permitted in Tcl expressions are a subset of the operators permitted in C + expressions, and they have the same meaning and precedence as the corresponding C + operators. Expressions almost always yield numeric results (integer or floating-point + values). For example, the expression + + + + + expr 8.2 + 6 + + evaluates to 14.2. Tcl expressions differ from C expressions in the way that + operands are specified. Also, Tcl expressions support non-numeric operands and string + comparisons. For some examples of simple expressions, suppose the variable + a has the value 3 and the variable + b has the value 6. Then the command on the left side of each of the + lines below will produce the value on the right side of the line: + + + + + expr 3.1 + $a + + + 6.1 + + + + + expr 2 + "$a.$b" + + + 5.6 + + + + + expr 4*[llength "6 2"] + + + 8 + + + + + expr {{word one} < "word $a"} + + + 0 + + + + + + + + + Math functions + Tcl supports the following mathematical functions in expressions: + + + + + + + acos + + + cos + + + hypot + + + sinh + + + + + asin + + + cosh + + + log + + + sqrt + + + + + atan + + + exp + + + log10 + + + tan + + + + + atan2 + + + floor + + + pow + + + tanh + + + + + ceil + + + fmod + + + sin + + + + + + + + + + Each of these functions invokes the math library function of the same name; see the + manual entries for the library functions for details on what they do. Tcl also + implements the following functions for conversion between integers and floating-point + numbers and the generation of random numbers: + + + abs(arg), double(arg), int(arg), rand(), round(arg), srand(arg). + + +
+
+ + <computeroutput>if</computeroutput> - Execute scripts conditionally + + Synopsis + + if expr1 ?then? body1 elseif expr2 ?then? body2 elseif ... + ?else? ?bodyN? + + + + Description + The if command evaluates + expr1 + as an expression (in the same way that expr + evaluates its argument). The value of the expression must be a boolean (a numeric value, + where 0 is false and anything is true, or a string value such as "true" or "yes" for true + and "false" or "no" for false); if it is true then body1 + is executed by passing it to the Tcl interpreter. Otherwise expr2 + is evaluated as an expression and if it is true then body2 + is executed, and so on. If none of the expressions evaluates to true then + bodyN is executed. The then + and else arguments are optional "noise words" to make the + command easier to read. There may be any number of elseif + clauses, including zero. BodyN may also be omitted as long + as else is omitted too. The return value from the command + is the result of the body script that was executed, or an empty string if none of the + expressions was non-zero and there was no bodyN. + + +
+
+ + <computeroutput>for</computeroutput> - "For" loop + + Synopsis + + for start test next body + + + + Description + + For is a looping command, similar in structure to + the C for statement. The start, next, + and body arguments must be Tcl command strings, and + test is an expression string. If a + continue command is invoked within + body then any + remaining commands in the current execution of body are + skipped; processing continues by invoking the Tcl interpreter on next, + then evaluating test, and so on. If a + break command is invoked within + body or next, then the + for command will return immediately. The operation of + break and continue are + similar to the corresponding statements in C. For + returns an empty string. + + + + + +for {set x 0} {$x<10} {incr x} { + puts "x is $x" +} + + + + +
+
+ + <computeroutput>while</computeroutput> - Execute script repeatedly + as long as a condition is met + + Synopsis + + while test body + + + + Description + The while command evaluates + test as an expression (in the same way that + expr + evaluates its argument). The value of the expression must be a proper boolean value; if + it is a true value then body is executed by passing it + to the Tcl interpreter. Once body has been executed then + test is evaluated again, and the process repeats until + eventually test evaluates to a false boolean value. + Continue commands may be executed inside + body to terminate the current iteration of the loop, and + break commands may be executed inside body + to cause immediate termination of the while command. The + while command always returns an empty string. + + + + + +set x 0 +while {$x<10} { + puts "x is $x" + incr x +} + + + + +
+
+ +
+ Instrument settings +
+ Beam shutter + Every instrument area in the SINQ neutron guide hall is controlled by the Local Beam + Controlsystem (LBC). It uses fixed installed barriers to prevent entry to the area around + the active beam during experimental work. If the user carries out his work in accordance + with the operating instructions, he will be protected from direct beam radiation. A danger + zone which is subject to the LBC interlocking system has two beam shutters + + the neutron guide main shutter which only can be opended by the radiation safety + officer + + + and a secondary shutter to close the beam at the instrument separately from the + others if more then one instrument is build up at the same neutron guide. + + + The secondary shutter for the instrument is the one the user can handle. Normally the + secondary shutter can only be operated if the experimental area (yellow fence) of the + instrument is locked. The shutter can then be opened and closed by a button on the key + box at the entrance door of the instrument area and also by the SICS command + shutter which has the following syntax: + + + shutter + + + without parameter yields the actual status of the shutter which can be + shutter is open, shutter is closed, or + Enclosure is broken. The last status message is returned + if the LBC system wouldn't allow to open the beam. If this message is returned the shutter + is closed. + + + + + + shutter close + + + closes the secondary beam shutter. + + + + + + shutter open + + + opens the secondary beam shutter. + + + + + +
+
+ Neutron velocity selector + + The neutron velocity selector is a high-speed rotor. Blades inserted in the rotor + are only transparent for neutrons which manage pass the rotor in a time intervall + defined by the rotation speed of the selector. Thus neutrons in a certain speed + range (wavelength range) are selected. The wavelength distribution of neutrons is + also dependent of the tilt angle between the rotation axis and the neutron beam. + Extensive time-of-flight measurements have been done to determine the wavelength + λ and resolution Δλ/λ as a function of selector speed and + tilting angle. The dependency of the wavelength λ [nm] on the rotation + speed ν [RPM] can well be described by + + + λ(ν,ξ)= A(ξ)/ν+B(ξ) + + + where A(ξ) ans B(ξ) are parameters depending on the tilting angle ξ. The + experimentally determined relationships A(ξ) and B(ξ) are + + + B(ξ)= 0.0122+3.61x10-4ξ+3.14 + x10-4ξ2 + +3.05x10-5ξ3 + +9.32x10-7ξ4 + + + The wavelength resolution Δλ/λ of the selector should be independent + of the rotation speed and only be dependent of the tilting angle ξ. However, this is + only true for long collimations. For short collimation lengths a slight dependency of + the resolution on the rotaion speed could be measured. Also the shape of the resolution + function is than not necessarily triangular. If the wavelength resolution of the selector + is not so important for the refinement of your data analysis you can use the values given + in the following table for the dependency of the wavelength resolution Δλ/ + λ on the tilting angle ξ. + + + + + ξ + Δλ/λ + A(ξ) + B(ξ) + + + + + -15 + 0.12 + 19812 + 0.0218 + + + -10 + 0.115 + 17774 + 0.0182 + + + -5 + 0.095 + 15493 + 0.0163 + + + 0 + 0.1 + 12716 + 0.01097 + + + 5 + 0.155 + 9342 + 0.0269 + + + 10 + 0.3 + 5293 + 0.0869 + + + + + A high speed-device like a velocity selector has to account for gyroscopic forces when + moving the device. In praxis this means that the selector must be stopped before the tilt + angle can be changed. Furthermore there are forbidden areas of rotation speeds. In these + areas the velocity selector is in destructive resonance with itself. For controlling the + neutron velocity selector three command are available: nvs + , nvswatch, and lambda + which are described below. + + + lambda + + + The name of the variable alone prints the current wavelength in nm. + + + + + lambda rot <value> + + + calculates the rotation speed for the wavelength given by + <value>. + + + + + lambda wl <value> + + + calculates the wavelength for the rotation speed given as parameter + <value>. + + + + + drive lambda <newval>, run lambda <newval> + + + The lambda variable can be driven + using the normal drive and + run commands. + + + + + nvs status + + + Prints a status summary of the velocity selector. + + + + + nvs list + + + Displays rotation speed and tilt angle of the velocity selctor. + + + + + nvs [rot=<newval>] [tilt=<newval>] + + + This command sets a new tilt angle and/or rotation speed for + the velocity selector. Either one or both of the keywords + tilt or rot + may be given, followed by a number. + + + + + nvs rotinterest + + + Enables printing of status messages about the current state of the + selector when it is driven. + + + + + nvs loss + + + Starts a loss current measurement on the velocity selector and + prints the result. + + + + + + + The commands described so far cover the actual handling of the velocity selector. During a + measurement users might want to use further functions such as: + + Monitor the rotation speed of the velocity selector. + + Log the rotation speeds of the velocity selector. + + Initiate error handling when the velocity selector + fails to stay within a predefined tolerance of rotation speeds. + + + Now, these are tasks usually connected with sample environment devices. Now, the + SICS programmers have been lazy. Moreover they wanted to avoid duplicating code + (and bugs). Consequently, they tricked the velocity selector to be a sample + environment device as well. + + This means besides the actual velocity selector object (in this case + called nvs) there exists another object for + monitoring the velocity selector. The name of this device is the name of the + velocity selector object with the string watch appended. For example if the + velocity selector has the SICS name nvs, the + monitor object will be nvswatch. The commands + understood by the watch object are fully decribed in the section about sample + environment devices. Please note, that all driving commands for the + watch object have been disabled. Driving + can only be achieved through the velocity selector object or the + lambda command. + +
+
+ Positioning an attenuator + + + + att + + + prints the current positioned attenuator. + + + + + att <val> + + + positions attenuator <val>. + Allowed attenuator numbers are 0, 1, 2, 3, 4 and 5. + + 0 : square 50 mm x 50 mm slit, attenuation = 1 + + 1 : circular 41 x diameter 0.4 mm slit, attenuation = 1/485 + + 2 : circular 9 x diameter 2 mm slit, attenuation = 1/88 + + 3 : circular 20 mm diameter slit, attenuation = 1/8 + + 4 : circular 30 mm diameter slit, attenuation = 1/3.5 + + 5 : circular 15 mm diameter slit, attenuation = 1/?? + + + + + + + +
+
+ Change the collimation + + + + coll + + + prints the current collimation length. + + + + + coll <val> + + + sets the collimation <val>. + Allowed collimation lengths are 1, 1.4, 2, 3, 4.5, 6, 8, + 11, 15 and 18. + + + + + +
+
+ Positioning the detector + + The detector can be moved via three motors named detectorx + , detectory, and detectorrotation. + These motors can be driven by the run or + drive commands described in . + The commands how to change motor parameters like Precision, + SoftZero etc. are described in . + The axes of the detector motors are defined as: + + + detectorx + + + An increasing value moves the detector away and a decreasing + value towards the sample position. + + + + + detectory + + + moves the detector laterally by a maximum of 480 mm in order to increase + the accessible q-range at any detector position. + + + + detectorrotation + + + rotates the detector around its vertical axis to reduce + parallaxes effects. + WarningThe BerSANS software package for the primary data + reduction does not handle the detector rotation. + + + + Instead of driving the motors individually one can refer to all motors as a whole by the + dt command. The command dt + without other parameters will yield a listing of the current position of the detector: + + +dt +Status listing for dt +dt.x = 18800.048828 +dt.y = 0.004000 +dt.phi = 0.414000 + + + To move the detector you can call dt with parameters + defining the new position of the motors, e.g. + + +dt x = 800 y = ++100 phi 0 + + + The = sign is not mandatory and can be left out. The + command above drives the motor detectorx to the + position 800, the motor + detectory to a position 100 mm further into + y-direction, the motor + detectorrotation to position + 0. All the three movements are done parallel. + Relative movements can be performed by preceding an + -- or ++ to the + motor position. The whole set of parameters valid for the + dt command is described in the + about special SANS commands. + +
+
+ Positioning the beam stop + The beam stop can be adjusted by two motors named + beamstopx and beamstopy. + They can be driven by the run or + drive commands which are described in + . The commands how to change motor parameters + like Precision, SoftZero + etc. are described in . The axes of the beam stop + motors are defined as: + + + beamstopx + + + moves the beam stop horizontally. + + + + + beamstopy + + + moves the beam stop vertically. + + + + + Instead of driving the motors individually one can refer to both motors as a + whole by the bs command. The command + bs without other parameters will yield a + listing of the current position of the beam stop: + + +bs +Status listing for bs +bs.x = 0.300000 +bs.y = 2.500000 + + + To move the beam stop you can call bs with + parameters defining the new position of the motors, e.g. + + +bs x = 2 y ++10 + + + The = sign is not mandatory and can be left + out. The command above drives the motor beamstopx + to the position 2 and the motor + beamstopy to a position 10 mm further into + y-direction. Both movements are done parallely. Relative movements can be + performed by preceding an -- or + ++ to the motor position. The whole + set of parameters valid for the bs command + is described in the about special + SANS commands. + + + Additionally to the commands for the movement of the two beam stop axes a few + other commands have been established: + + + bsout + + + moves the beam stop out of the detection area, so that + there is nowhere a shadow of the beam stop on the detector. This + position is outside the software limits of the motors in the area + of the beam stop magazines. In this area an uncontrolled movement + could lead to a collisioni with the magazines. Therefore one can + not move the beam stop anymore with the + bs command after calling + bsout, because the motors are + fixed automatically. + + + + + bsin + + + releases the beam stop motors and moves them back to the + previous position. After calling + bsout you have to call first + bsin to continue with the + movement of the beam stop with the + bs command. + + + + + bsfree + + + is a manager command, which releases the beam stop motors, + if they are still in the + bsout-position. This command should + only be used when something went wrong with the SICS server during + the time the beam stop was in + bsout-position. + + + + + bschange [<val>] + + + allows the user to change the size of the beam stop. Four + different sizes are available and can be selected by the parameter + <val>. Valid values for + <val> are: + + 1 for beam stop size of 40 mm x 40 mm + + 2 for beam stop size of 70 mm x 70 mm + + 3 for beam stop size of 85 mm x 85 mm + + 4 for beam stop size of 100 mm x 100 mm + + + + + The bschange command automatically recognizes the + actually used beam stop size, puts it into the empty magazine and picks up the new beam + stop. bschange automatically closes the instrument + beam shutter, if it was open, but it doesn't reopen it again afterwards. Calling + bschange without a parameter returns the number + of the actually used beam stop size. + + + + + +
+
+ +
+ Sample environments +
+ Sample table + + One standard sample set-up is the sample table with a vertical translator, a xy-table and a + rotation table. Optionally, another linear translator stage or a double goniometer can be + mounted on the rotation table. The available motor axes for the sample table are defined + as follows: + + + saz + + + vertical translation of the sample table + + + + + say + + + horizontal translation parallel to the neutron beam direction + + + + + sax + + + horizontal translation perpendicular to the neutron beam + + + + + som + + + rotation around the vertical axis ω + + + + + gphi + + + rotation around the horizontal axis Φ + + + + + gtheta + + + rotation around the horizontal axis Θ, + Θ ⊥ Φ ⊥ ω + + + + + sposi + + + horizontal translation. Linear translation stage can be + mounted on the rotation table and is used for the movement of + the temperature controlled (Haake temperature controler, + ) + sample holder. + + + + + The motors can be driven by the drive or + run commands described in + . Instead of driving the motors individually + one can refer to all motors as a whole by the + st command. The command + st without parameters will yield a listing + of the current positions of the sample table motors: + + +st +Status listing for st +st.omega = 0.504000 +st.x = 12.965000 +st.y = -18.992001 +st.z = 106.121002 +st.posi = 173.875000 + + + The axes are named x, + y, z, + posi, omega, + phi and theta + which move the motors sax, + say, saz, + spos, som, + gphi and gtheta, + respectively. In the above example the optional linear translator stage was mounted + on the rotation table so that the position of the motor + spos is listed but not those of the motors + gphi and gtheta. + The whole set of parameters valid for the st + command are described in about special SANS + commands. A frequently used parameter for st is + the pos parameter. The command + + +st pos P1 + + + reads out the actual positions of all the motors of the sample table and defines + for it the name P1. Afterwards this position can + be reached by typing simply st P1. Instead of + remembering the positions of all the motors one only has to remember the name + P1 to bring the sample in position. + +
+
+ Haake temperature controller + + This is sort of a buck full of water equipped with a temperature control system. + The RS-232 interface of this device can only be operated at 4800 baud max. This is + why it has to be connected to the serial printer port of the Macintosh serial port + server computer. This makes the channel number to use for initialisation a 1 always. + The driver for this device has been realised in the Tcl extension language of the + SICS server. A prerequisite for the usage of this device is that the file + hakle.tcl is sourced in the SICS initialisation + file and the command inihaakearray has been + published. Installing the Haake into SICS requires two steps: first create an + array with initialisation parameters, second install the device with + evfactory. A command procedure is supplied for + the first step. Thus the initialisation sequence becomes: + + +inihaakearray <name-of-array> <macintosh-computer> <name> <port> <channel> +evfactory new temperature tcl <name-of-array> + + + An example for the SANS: + + +inihaakearray eimer lnsa10.psi.ch 4000 1 +evfactory new temperature tcl eimer + + + Following this, the thermostat can be controlled with the other environment + control commands. + + + The Haake Thermostat understands a single special subcommand: + sensor. The thermostat may be equipped with an + external sensor for controlling and reading. The subcommand + sensor allows to switch between the two. The + exact syntax is: + + +temperature sensor <val> + + + <val> can be either + intern or + extern. + +
+
+ Bruker electromagnet + + This is the controller for the large magnet at SANS. The controller is a box the + size of a chest of drawers. This controller can be operated in one out of two modes: + in field mode the current for the magnet is controlled via an external hall sensor + at the magnet. In current mode, the output current of the device is controlled. + This magnet can be configured into SICS with a command syntax like this: + + +evfactory new <name> bruker <Mac-PC> <Mac-port> <Mac-channel> + + + <name> is a placeholder for the name of the + device within SICS. A good suggestion (which will be used throughout the rest of the + text) is magnet. bruker + is the keyword for selecting the bruker driver. <Mac-PC> + is the name of the Macintosh PC to which the controller has been connected, + <Mac-Port> is the port number at which the + Macintosh-PC's serial port server listens. <Mac-channel> + is the RS-232 channel to which the controller has been connected. For example (at SANS): + + +evfactory new magnet bruker lnsa10.psi.ch 4000 9 + + + creates a new command magnet for a Bruker magnet Controller connected to serial port + 9 at lnsa10. + + + In addition to the standard environment controller commands this magnet controller + understands the following special commands: + + + magnet polarity + + + Prints the current polarity setting of the controller. Possible answers + are plus, minus + and busy. The latter indicates that the controller + is in the process of switching polarity after a command had been given to switch it. + + + + + magnet polarity <val> + + + sets a new polarity for the controller. Possible values for + <val> are + minus or plus. + The meaning is self explaining. + + + + + magnet mode + + + Prints the current control mode of the controller. Possible answers are + field for control via hall sensor or + current for current control. + + + + + + magnet mode <val> + + + sets a new control mode for the controller. Possible values for + <val> are + field or + current. The meaning is explained above. + + + + + magnet field + + + + reads the magnets hall sensor independent of the control mode. + + + + + magnet current + + + reads the magnets output current independent of the control mode. + Warning + There is a gotcha with this. If you type only + magnet a value will be returned. The meaning of + this value is dependent on the selected control mode. In + current mode it is a current, in + field mode it is a magnetic field. This is so + in order to support SICS control logic. You can read values at all times explicitly + using magnet current or + magnet field. + + + + +
+
+ Sample holder for electro magnet + + Another standard sample seup is a vacuum chamber, which is directly connected to the + collimator and detector tubes, so that the SANS can be operated at about + 10-2 mbar in a single vacuum without windows or with thin + aluminium or sapphire windows to work at ambient pressure or at vacuum conditions + down to 10-6 mbar. The chamber is large enough to carry + an electromagnet. For this setup a sample changer with an optional heated sample + position is available. This sample changer can be moved vertically by 245 mm and can + also be rotated by ±10 degree. The two available motors are defined as follows: + + + mz + + + moves the electromagnet sample holder in vertical direction. + + + + + mom + + + rotates the sample around the vertical axis + ω by ±10 degree. + + + + + The motors can be driven by the run or + drive command described in + . Instead of driving the motors individually one can refer + to both motors as a whole by the msh command. The + command msh without parameters will yield a listing of + the current position of the electromagnet sample holder: + + +msh +Status listing for msh +msh.z = 0.000000 +msh.omega = 0.000000 + + + The axes of the sample holder mz and + mom are named in the + msh command + z and omega. + The whole set of parameters valid for the + msh command are described in + about special SANS commands. + +
+
+ Eurotherm temperature controller + + At SANS there is a Eurotherm temperature controller for the sample heater available. + This and probably other Eurotherm controllers can be configured into SICS with the + following command. The Eurotherm needs to be connected with a nullmodem adapter. + + +EVFactory new <name> euro <computer> <port> <channel> + + + <name> is a placeholder for the name of the + device within SICS. A good suggestion is temperature. + euro is the keyword for selecting the Eurotherm driver. + computer is the name of the Macintosh PC to which + the controller has been connected, <port> is the + port number at which the Macintosh-PC's serial port server listens. + <channel> is the RS-232 channel to which the + controller has been connected. + WarningThe Eurotherm needs a RS-232 port with an unusual + configuration: 7bits, even parity, 1 stop bit. Currently only the SANS Macintosh port 13 + (the last in the upper serial port connection box) is configured like this! Thus, an + example for SANS and the name temperature looks like: + + +EVFactory new temperature euro lnsa10.psi.ch 4000 13 + + + + There are two further gotchas with this thing: + + The Eurotherm needs to operate in the EI-bisynch protocoll mode. This + has to be configured manually. For details see the manual coming with the controller. + + The weird protocol spoken by the Eurotherm requires very special control + characters. Therefore the send functionality usually supported by a SICS environment + controller could not be implemented. + + + +
+
+ ITC-4 and ITC-503 temperature controller + *outdated controllers, will be replaced* + +
+
+ +
+ Data handling and acquisition +
+ File naming conventions and storing data + + Data files are stored by the SICS server on the lnsa10.psi.ch + workstation in the directory defined by the SICS variable + SICSDataPath. By default this directory is + /home/SANS/data/. The file name of a data file is + composed of four parts: + + + + the prefix stored in the variable SICSDataPrefix, + by default sans + + + + + the run number stored in the variable SICSDataNumber, + which is incremented before each storing process and has 5 digits (leading 0) + + + + + the actual year (4 digits) + + + + + and the post-fix stored in the variable SICSDataPostfix, + by default .hdf + + + + A typical data file name would be + /home/SANS/data/sans123452006.hdf. + All data files are written in NeXus format. + +
+
+ Data acquisition + + SICS counter handling + + The SICS counter concept may include several monitors per counter. At the SANS + instrument two monitors are installed: one between beam shutter and neutron + velocity selector, which is used for normalizing the SANS measurement on the + incident neutron flux, and a second one after the selector just in front of the + attenuator. For the SANS instrument only one counter is handled which is named + counter. A few words have to be lost about the + SICS handling of preset values for counters. Two modes of operation have to be + distinguished: counting until a timer has passed, for example counting for 20 + seconds. This mode is called Timer mode. In + the other mode, counting is continued until a control monitor has reached a + certain preset value. This mode is called Monitor + mode. At the SANS instrument the first monitor between beam shutter and neutron + selector is used in this mode. The preset values in + Monitor mode are usually very large. Therefore + the counter has an exponent data variable. Values given as preset are + effectively 10 to the power of this exponent. For instance if the preset is 25 + and the exponent is 6, then counting will be continued until the monitor has + reached 25 million. Note, that this scheme with the exponent is only in operation + in Monitor mode. The commands understood are: + + + counter SetPreset <val> + + + sets the counting preset to <val>. + + + + + counter GetPreset + + + prints the current preset value. + + + + + counter SetExponent <val> + + + sets the exponent for the counting preset in monitor mode to + <val>. + + + + + counter GetExponent + + + prints the current exponent used in monitor mode. + + + + + counter SetMode <val> + + + sets the counting mode to <val>. + Possible values are Timer for timer mode + operation and Monitor for waiting for a + monitor to reach a certain value. + + + + + counter GetMode + + + prints the current mode. + + + + + counter GetCounts + + + prints the counts gathered in the last run. + + + + + counter GetMonitor <n> + + + prints the counts gathered in the monitor number + <n> in the last run. + + + + + counter Count <preset> + + + starts counting in the current mode and the preset + <preset>. + + + + + counter status + + + prints a message containing the preset and the current monitor + or time value. Can be used to monitor the progress of the counting + operation.. + + + + + counter GetTime + + + retrieves the actual time the counter counted for. This + excludes time where there was no beam or counting was paused. + + + + + + + + Histogram memory + + The histogram memory is used in order to control the large area sensitive detector. It + takes care of putting counts detected in the detector into the proper bin in memory. + Next to a conventional mode of a SANS measurement where all detected neutrons are + accumulated for a given time or monitor count, also a time of flight mode and a + stroboscopic mode are available, where there is for each detector pixel a row of + memory locations mapping the time bins. As usual in SICS the syntax is the name of the + histogram memory followed by qualifiers and parameters. For the SANS the name of + the histogram memory is banana. + + + + The histogram memory has a plethora of configuration options coming with it which + define memory layout, modes of operation, handling of bin overflow and the like. + Additionally there are histogram memory model specific parameters which are needed + internally in order to communicate with the histogram memory. In most cases the + histogram memory will already have been configured at SICS server startup time. + However, there are occasion where these configuration options need to be enquired + or modified at run time. The command to enquire the current value of a configuration + option is: banana configure <option>, the + command to set it is: + banana configure <option> <newvalue>. + A list of common configuration options and their meaning is given below: + + + + HistMode + + + describes the modes of operation of the histogram memory. + Possible values are: + + + Transparent + + + counter data will be written as is to memory. For + debugging purposes only. + + + + + Normal + + + neutrons detected at a given detector will be added + to the apropriate memory bin. + + + + + TOF + + + time of flight mode, neutrons found in a given detector + will be put added to a memory location determined by the detector + and the time stamp. + + + + + Stroboscopic + + + This mode serves to analyse changes in a sample due to an + varying external force, such as a magnetic field, mechanical stress + or the like. Neutrons will be stored in memory according to detector + position and phase of the external force. + + + + + + + + + OverFlowMode + + + This parameter determines how bin overflow is handled. This happens when + more neutrons get detected for a particular memory location then are allowed + for the number type of the histogram memory bin. Possible values are: + + + Ignore + + + overflow will be ignored, the memory location will wrap + around and start at 0 again. + + + + + Ceil + + + the memory location will be kept at the highest posssible + value for its number type. + + + + + Count + + + as Ceil, but a list of + overflowed bins will be maintained. + + + + + + + + + Rank + + + defines the number of histograms in memory. + + + + + Length + + + gives the length of an individual histogram. + + + + + BinWidth + + + determines the size of a single bin in histogram memory in bytes. + + + + + + + For time of flight mode the time binnings can be retrieved and modified with the following + commands. Note that these commands do not follow the configure syntax given above. Please + note, that the usage of the commands for modifying time bins is restricted to instrument + managers. + + + banana timebin + + + prints the currently active time binning array. + + + + + banana genbin <start> <step> <n> + + + generates a new equally spaced time binning array. + Number <n> time bins will be generated + starting from <start> with a stepwidth of + <step>. + + + + + banana setbin <inum> <value> + + + Sometimes unequally spaced time binnings are needed. These can be configured + with this command. The time bin <iNum> is + set to the value <value>. + + + + + banana clearbin + + + Deletes the currently active time binning information. + + + + + banana preset [<val>] + + + with a new value <val> sets the preset + time or monitor for counting. Without <val> + prints the current value. + + + + + banana exponent [<val>] + + + with a new value <val> sets the exponent + to use for the preset time in Monitor mode. Without <val> + prints the current value. + + + + + banana CountMode [<mode>] + + + with a new value <mode> sets the + count mode. Possible values are Timer for a fixed + counting time and Monitor for a fixed monitor count + which has to be reached before counting finishes. Without a value for + <mode> the command prints the currently + active value. + + + + + banana init + + + after giving the configuration commands this needs to be called in order to + transfer the configuration from the host computer to the actual histogram memory. + + + + + banana count + + + starts counting using the currently active values for + CountMode and preset. + This command does not block, i.e. in order to inhibit further commands from the console, + you have to give Success afterwards. + + + + + banana Initval <val> + + + initialises the whole histogram memory to the value + <val>. Usually 0 in order to clear the histogram + memory. + + + + + banana get <i> <iStart> <iEnd> + + + retrieves the histogram number + <i>. A value of -1 for + <i> denotes retrieval of the whole histogram memory. + <iStart> and <iEnd> + are optional and allow to retrieve a subset of a histogram between + <iStart> and <iEnd>. + + + + + banana sum <d0min> <d0max> <d1min> <d1max> ...<dnmin> <dnmax> + + + calculates the sum of an area on the detector. For each dimension a minimum and + maximum boundary for summing must be given. + + + + + + + Storing Data and starting a SANS measurement + + Instead of initializing and starting a measurement by the banana + command a few other commands have been introduced to take care for those things: + + + StoreData + + + This command does what it says. It writes the current state of the instrument + including counts to a NeXus data file. + + + + + count [<mode> <preset>] + + + starts a count operation in mode <mode> + with a preset <preset>. + <mode> can have the values + Timer or Monitor. + The parameters are optional. If they are not given, the count operation will be started + with the current setting in the histogram memory object + banana. Before the count operation is started, the + count command waits until all other commands executed + earlier are finished. During the count operation no other commands can be executed. + After the count, StoreData will be automatically called. + + + + + repeat <num> [<mode> <preset>] + + + calls num times count. + num is a required parameter. The other two are optional and + are handled as described above for count. + + + + + + +
+
+ XY table + XYTable is a class which maintains a list of X-Y values. These can be plotted in the + VarWatch SICS client by configuring it with a special command. Before you may use an XYTable + object it had to be installed into the system by the SICS administrator. This can be done by + the command MakeXYTable <xydata> in the SANS initialization + file. For this documentation it is assumed that this has happened already and a XYTable object is + available in the system under the name <xydata>. + + + Interaction with the XYTable object happens through the following commands: + + + <xydata> clear + + + clears all entries in the x-y table. + + + + + <xydata> list + + + lists the entries in the x-y table on the screen. + + + + + <xydata> write <filename> + + + writes the x-y list to the disk file filename. This file resides on + the machine running the SICS server. + + + + + <xydata> uuget + + + sends the x-y list in an uuencoded format. This is the command to + give to the VarWatch SICS client in order to make it display the x-y list. + + + + + <xydata> add <xval> <yval> + + + creates a new x-y list entry with the values + <xval> and + <yval>. + + + + + +
+
+ Status of the actual acquisition process + empty +
+
+
+ + + Other programs +
+ PSI2HMI + empty +
+
+ BerSANS software package + The Manual of the BerSANS Software Package is available in pdf format as well as a zipped pdf file. +
+
+ sasfit program + empty +
+
+ +
diff --git a/doc/user/system.htm b/doc/user/system.htm index b260aed6..7212dea5 100644 --- a/doc/user/system.htm +++ b/doc/user/system.htm @@ -66,6 +66,10 @@ above and restores SICS to the state it was in when the status was saved with read.

+restore listerr prints the list of lines which caused errors during the + last restore. +

+

killfile decrements the data number used for SICS file writing and thus consequently overwrites the last datafile. This is useful when useless data files have been created during tests. As this is diff --git a/doc/user/tasmad.html b/doc/user/tasmad.html index eadeb988..0b1f5f96 100644 --- a/doc/user/tasmad.html +++ b/doc/user/tasmad.html @@ -40,7 +40,7 @@

Examples
Whenever examples are shown, anything which is actually typed by the user is shown like this. It will generally be shown in lower case. E.g. -
	scan a1=0 da1=1 np=6
+
	sc a1=0 da1=1 np=6
indicates that everything between the s and 6, inclusive, is to be typed by the user.
Optional Arguments
Square brackets, [ ], indicate optional @@ -69,11 +69,6 @@ (A) a sequence of variable names e.g. DM,DA,SS (carry out command given on variables DM, DA, SS) - e.g. ALF1-ALF4 (carry out command given on variables between ALF1 and - ALF4 in storage order; see section V) - e.g. DM,ALF1-ALF4,SS,DA (a combination of the above) Variables separated - by commas need not be typed in their order of storage in the program. - Note : that for this type of syntax (type A) the only acceptable variable separators are ' ' (i.e. a space), ',' and '-' (' ' and ',' are equivalent). @@ -115,13 +110,11 @@ value. LI LIst : Listing of variables and parameters. LE ListEnergies Energies, k and Q values. - LL ListLimits Limits and zeros. - LZ ListZero Limits and zeros. + LL ListLimits Limits and zeros. Same as lz. + LZ ListZero Limits and zeros. Same as ll. LM ListMach Machine parameters. LS ListSample Sample parameters. LT ListTargets Targets and positions. - LD ListDiaphragms Diaphragms. - LP ListPower Power supply values. LO LOg : Controls terminal logging. OF OFf : Turns flipper off. ON ON : Turns flipper on. @@ -129,17 +122,13 @@ PA Pol.An. : Defines a polarization analysis file (default file ext'n is .PAL). PR PRint : Prints one ore more variables or parameters. -RU RUn : Runs a jobfile. SC SCan : Scans a variable with given or previously defined increment, number of points and time interval or monitor count. SE SEt : Sets a parameter value. -SF ScanFast : Scans a variable quickly. +FS ScanFast : Scans a variable quickly. SW SWitch : Sets some switches. -SZ SetZero : Set zero in such a way that value as given - is defined as actual position of variable - (works only for simple variables, i.e. - variables that have a zero). +SZ SetZero : Sets the zero point offset of a variable.

CLEAR

@@ -150,8 +139,7 @@
 	the motors or supplies which have been cleared are listed by THE 
 	Program.
 
-	e.g. 	 CL A1-A3<CR>
-		 CL I3,RA,I4<CR>
+	e.g. 	 CL I3,RA,I4<CR>
 		 CL<CR>
 
 
@@ -488,33 +476,6 @@ e.g. PR A1,A5<CR> PR QH-EN,GM<CR> - -

RUN

-
-
-	RU(N) : Runs a jobfile. All commands which may be issued at the terminal
-	may also be included in a job-file which essentially replaces the user 
-	at the terminal.
-	The commands in the job file are executed by running the job file. 
-	Before running the job file a syntax check is done. The file is listed 
-	on the terminal and all scans are checked for limit violations. When 
-	checking is complete, execution is started even if errors have been 
-	reported during the check. To interrupt the sequence type CTRL-C twice 
-	(see interruption section II above). If the RUN command is issued alone,
-	MAD Program asks for a job file name. The default file extension for a 
-	job file is .JOB. The job-file name may also be given on the same line 
-	as the RUN command. Job files can be created as normal OpenVMS files 
-	using one edtir (EDT/TPU/NEDIT).
-	Nesting : Job files may be nested. That is, a job file may contain any 
-	number of RUN commands. The nesting depth should not exceed 3 however.
-	 See also DO.
-
-	e.g. 	 RU MYJOB.HET<CR>	commands from file MYJOB.HET
- 		 RU MYJOB<CR>	commands from file MYJOB.JOB 
-		 RUN<CR> 	gives prompt for job file name
-		JOB-FILE NAME:
-
-
 

SCAN

@@ -619,7 +580,7 @@
 			sets dM and dA to 3.355 
 
 
-

SF

+

FS

ScanFast Scans a simple variable quickly. The variable is driven @@ -636,7 +597,7 @@ to be specified directly.
Example:
-    SF A1=6,DA1=1,NP=13,TI=2   --> A1 = 0 to +12 with
+    FS A1=6,DA1=1,NP=13,TI=2   --> A1 = 0 to +12 with
 				    readings every 2 secs.
All of the data is output to a disk file as with the @@ -650,7 +611,7 @@
 
 	SZ : (SetZero.) This command sets the zero for a variable such that 
-	its current value is change into a specified value.
+	its current value of the zer point offset is change into the specified value.
 	Obviously this command works only for variables that have a zero.
 	e.g.	 PR A3
 		A3  -45.42
@@ -719,9 +680,9 @@ Example of phonon scan:
    
Sets the title string (up to 72 characters) to be written to the data file header.
set user ... -
Sets the experiment user's name (6 characters). +
Sets the experiment user's name.
set local ... -
Sets the local contact's name (6 characters). +
Sets the local contact's name. @@ -768,15 +729,14 @@ however, corresponds to a transmission minimum for Ki neutrons. l : Two-axis mode : If you want to work in TWO-AXIS mode, just SEt SA to 0 ! This will change the zero of A5 by 90¡ and any following drive of Ki or Kf will drive the detector to zero and the analyser - perpendicular to the beam (just check that there is no absorbing cache - [Cd, B4C,...] behind the analyser !). Due to the change of A5 zero the + perpendicular to the beam. Due to the change of A5 zero the value of A5 will be ZERO (0!) with a analyser orthogonal to the scatterred beam. - l : Constant QM Mode: If you have a powder sample and want to work in - -1 at a given QM ( modulus of Q that you cannot drive), just SEt the - sample lattice parameters (AS, BS, CS ) to 2.p and lattice angles - (AA, BB, CC ) to 90¡. Any subsequent drive of QH will drive the + l : Constant QM Mode: If you have a powder sample and want to work at + a given QM ( modulus of Q that you cannot drive), just SEt the + sample lattice parameters (AS, BS, CS ) to 2 PI (6.2832) and lattice angles + (AA, BB, CC ) to 90. Any subsequent drive of QH will drive the machine to the correct QM value. Use the powder switch to inhibit the A3 (q) movement. @@ -864,7 +824,6 @@ FX =1 for constant Ki; =2 for constant Kf NP Number of points in a scan TI Preset time [seconds] for a COunt or SCan MN Preset monitor for a COunt or SCan -TO Time-out in for WAit command [minutes] DTL lower temperature error allowed [Kelvin] DTU upper temperature error allowed [Kelvin] @@ -935,14 +894,10 @@ STU Sample upper translation ATL Anal lower translation ATU Anal upper translation MGL Mono lower goniometer (Reserved) -MGU Mono upper goniometer SGL Sample lower goniometer SGU Sample upper goniometer AGL Anal lower goniometer (Reserved) -AGU Anal upper goniometer -MSC Mono "sample" changer (TASP only) -ASC Anal "sample" changer (TASP only) -CSC Collimator "sample" changer (TASP only) +SRO Sample sample table ring rotation. D1T D1B D1R D1L Diaphragm 1 (top/bottom/right/left) D2T D2B D2R D2L Diaphragm 2 (top/bottom/right/left) @@ -950,7 +905,6 @@ D3T D3B D3R D3L Diaphragm 3 (top/bottom/right/left) ILL Instruments: CH Monochromator changer position [degrees or mm] - TM (LM) Monochromator translation [(IN20 : 5mm)] GM Monochromator goniometer angle [1 unit = 4¡] RM Monochromator curvature GL Sample goniometer angle; lower arc [1 unit = 4¡] @@ -973,11 +927,14 @@ QM Length of Q [ TT (T) Temperature of sample thermometer [K] TRT(RT) Temperature of regulation thermometer [K] (can only be printed out) +
+

Polarisation Analysis Variables

+
 *I1   -\
 *I2     \
 *I3   	 +--  power supply current values [A]
  .      /
-*I11  -/
+*I6  -/
 
 *HX   -\     Components of Helmholtz fields at sample in Oersteds.
 *HY   	+--  HX is parallel to Q and HY is perpendicular to Q in
@@ -986,6 +943,7 @@ TRT(RT)	Temperature of regulation thermometer 	[K]
 *F1   -\     Status of flippers one and two; these variables take the 
 *F2   -/     values ON or OFF.
 
+

Increments Variables

 	For all variables A1 through T in the  list of type (iv) variables 
diff --git a/doc/user/tasstore.htm b/doc/user/tasstore.htm
index f35c1e9f..f80278ae 100644
--- a/doc/user/tasstore.htm
+++ b/doc/user/tasstore.htm
@@ -10,24 +10,27 @@ files are formatted in a format compatible to the ILL's triple axis
 data file format. Data files can be found in directories:
 
 
-    /home/INST/data/YYYY
+    /home/INST/data/YYYY/HHH
 
 
on the instrument computer or in
 
-    /data/lnslib/data/INST/data/YYYY
+    /afs/psi.ch/project/sinqdata/YYYY/INST/HHH
 
 
on any other LNS unix system. INST is a placeholder for the instrument -name in capitals, YYYY for the year of data collection. Data files are +name in capitals, YYYY for the year of data collection. HHH is the thousands +directoy. In order to facilitate file handling, data files are grouped into directories +containing thousand data files each. Thus files 0-999 will be in the 000 directory, files + 1000-1999 in the 001 directory etc. Data files are named according to the SINQ naming convention:
-     instRRRRRYYYY.dat
+     instYYYYnTRRRRR.dat
 
with inst being the placeholder for the instrument name in lowercase, -RRRRR the run number as a five digit number and YYYY again the year of -data collection. Example: tasp003302002.dat is data collected in run +RRRRRR the run number as a six digit number and YYYY again the year of +data collection. Example: tasp2002n000330.dat is data collected in run number 330 in 2002.

diff --git a/doc/user/tasub.htm b/doc/user/tasub.htm index 0e2f618e..130cb29e 100644 --- a/doc/user/tasub.htm +++ b/doc/user/tasub.htm @@ -81,6 +81,12 @@ In order to calculate a UB matrix a list of reflections must be maintained. This

Add a new reflection to the list. Besides the indices all angles are given: a3, the sample rotation, a4, sample two theta, sgu, upper tilt cradle, sgl, lower tilt cradle and incoming energey ei and outgoing energy ef. +
tasub addauxref qh qk ql +
Adds an auxiliary reflection with indices qh, qk, ql to the list. A4 is + calculated from cell constants. A3 is either left alone or is calculated to + have the correct angular difference to a previous reflection. This is a help + for setting up the instrument or running powder mode. When a UB has been + generated from auxiliary reflections, a3, sgu and sgl angles will be incorrect.

Calculations

@@ -88,15 +94,23 @@ In order to calculate a UB matrix a list of reflections must be maintained. This This section covers the parameters and commands to use to make the module do calculations for you.
-
tasbub const ki | kf +
tasbub const ki | kf | elastic
Sets a parameter to determine if KI or KF is fixed when the energy transfer en is - being driven. Allowed values: ki, kf + being driven. Allowed values: ki, kf, elastic. In elastic mode the analyzer is + disregarded. This is useful for two circle diffractometers.
tasub const
Prints if ki or kf is fixed.
tasub ss
Prints the sample scattering sense.
tasub ss 1 | -1
Sets the sample scattering sense. Allowed values are either 1 or -1. +
tasub silent 0 | 1 +
Prints or sets the silent flag. If this is 0, the messages Driving motor .. + from .. to .. are suppressed.
+
tasub outofplane 0 | 1 +
Prints or sets the outofplane flag. If this flag is 0, the instrument will stay + in the scattering plane and not move out of it. This is here in order to protect those + bloody magnets which cannot be tilted.
tasub makeub r1 r2
Calculate a new UB matrix from the current cell constants and the entries r1 and r2 in the reflection list. r1 and r2 are integer numbers. This command will not only print the @@ -137,7 +151,7 @@ The virtual motor qm implements powder mode. In this mode, only the sampl respective positions. THis is commonly used to analyze the energy transfer of powder samples.

-There is another important command: +There are other important command:

tasub update
This command will force a recalculation of the current Q-E position for the virtual @@ -145,6 +159,9 @@ motors from angles. Normally tasub will take care of this. However, if any of th motors are moved directly or manualy, this command might be required. The SICS dr wrapper command, however, even takes care of this.
+
tasub updatetargets +
This command makes the QE targets macth the current position. This is +useful after initialization in the instrument.tcl file.

Internal Commands

diff --git a/drive.c b/drive.c index 1ad01395..8e85a51c 100644 --- a/drive.c +++ b/drive.c @@ -387,7 +387,7 @@ } else if(iRet == DEVINT) { - sprintf(pBueffel,"Driving Interrupted!"); + sprintf(pBueffel,"ERROR: Driving Interrupted!"); SCWrite(pCon,pBueffel,eError); ClearExecutor(GetExecutor()); SetStatus(eOld); diff --git a/errormsg.c b/errormsg.c new file mode 100644 index 00000000..d50cb3d6 --- /dev/null +++ b/errormsg.c @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include "errormsg.h" + +/* compare two strings for euqality, ignoring text within square brackets */ +int ErrEqual(char *str1, char *str2) { + char *p; + + while (*str1 != '\0' || *str2 != '\0') { + if (*str1 != *str2) { + return 0; + } + if (*str1 == '[') { + str1 = strchr(str1, ']'); + str2 = strchr(str2, ']'); + if (str1 == NULL || str2 == NULL) { + return str1 == str2; + } + } + str1++; + str2++; + } + return 1; +} + +ErrMsg *ErrPutMsg(ErrMsg *dump, char *fmt, ...) { + ErrMsg *m = NULL; + ErrMsg **last = NULL; + va_list ap; + char buf[256]; + char *text = NULL; + int l; + + va_start(ap, fmt); + l = vsnprintf(buf, sizeof buf, fmt, ap); + va_end(ap); + if (l < sizeof buf) { + text = buf; + } else { + /* assuming we have a C99 conforming snprintf and need a larger buffer */ + text = calloc(l, 1); + va_start(ap, fmt); + vsnprintf(text, l, fmt, ap); + va_end(ap); + } + last = &dump; + for (m = dump; m != NULL; m = m->next) { + if (ErrEqual(text, m->text)) { + *last = m->next; /* remove found item from list */ + break; + } + last = &m->next; + } + if (m == NULL) { /* make a new item */ + if (text == buf) text = strdup(buf); + m = calloc(1, sizeof(*m)); + m->text = text; + m->cnt = 1; + } else { + if (text != buf) free(text); + m->cnt++; + } + m->next = dump; + time(&m->last); + return m; +} diff --git a/errormsg.h b/errormsg.h new file mode 100644 index 00000000..470ebd5e --- /dev/null +++ b/errormsg.h @@ -0,0 +1,32 @@ +#ifndef ERRORMSG_H +#define ERRORMSG_H + +#include + +/** \file + * \brief Error message collection + */ +/** \brief Error message item + */ +typedef struct ErrMsg { + struct ErrMsg *next; + char *text; /**< the message text */ + int cnt; /**< count */ + time_t last; /**< time of last message */ +} ErrMsg; + +/** \brief Put a formatted message to the error message list + * + * The error message list contains only one entry for all messages + * with the same text, storing only the count and the last used time. + * Characters within square brackets are not taken into account + * when comparing messages. + * The new message is always at the head of the list. + * + * \param dump the error message list + * \param fmt the format for the message + * \return the new error message list head + */ +ErrMsg *ErrPutMsg(ErrMsg *dump, char *fmt, ...); + +#endif diff --git a/evcontroller.c b/evcontroller.c index 991dcde4..3a2e5a7e 100644 --- a/evcontroller.c +++ b/evcontroller.c @@ -71,20 +71,36 @@ static long EVIDrive(void *pData, SConnection *pCon, float fVal) { pEVControl self = NULL; - int iRet, iCode, i, iFix; + int iRet, iCode, i, iFix, savedStatus; char pError[132], pBueffel[256]; - + Tcl_Interp *pTcl = NULL; + self = (pEVControl)pData; assert(self); assert(pCon); + if (self->runScript != NULL) { + savedStatus = GetStatus(); + SetStatus(eBatch); + pTcl = InterpGetTcl(pServ->pSics); + snprintf(pBueffel, sizeof(pBueffel), "%s %f", self->runScript, fVal); + iRet = Tcl_Eval(pTcl,pBueffel); + SetStatus(savedStatus); + if(iRet != TCL_OK) + { + SCPrintf(pCon, eError, + "ERROR: %s while processing runscript for %s", + pTcl->result,self->pName); + } + } + self->fTarget = fVal; self->eMode = EVDrive; self->iStop = 0; self->start = time(NULL); self->lastt = 0; self->iWarned = 0; - SCSave(&self->conn, pCon); + self->conn = SCSave(pCon, self->conn); /* try at least three times to do it */ for(i = 0; i < 3; i++) @@ -276,9 +292,9 @@ if (self->lastt > 0) { /* increase tol for hysteresis */ tol=tol*1.1001; } + tmo = (int)(ObVal(self->pParam, SETTLE)); if(fDelta <= tol) /* inside tolerance */ { - tmo = (int)(ObVal(self->pParam, SETTLE)); if (self->lastt <= 0) /* lastt negative: -seconds already waited */ { self->lastt += now; @@ -303,9 +319,11 @@ else { if (self->lastt > 0) { /* save time already waited */ - sprintf(pBueffel,"%s outside tolerance, settling time suspended", - self->pName); - SCWrite(pCon,pBueffel,eWarning); + if (tmo > 0) { + sprintf(pBueffel,"%s outside tolerance, settling time suspended", + self->pName); + SCWrite(pCon,pBueffel,eWarning); + } self->lastt -= now; } notifyStatus(self, pCon, HWBusy); @@ -366,17 +384,15 @@ static void ErrWrite(char *txt, SCStore *conn) pExe = GetExecutor(); pCon = GetExeOwner(pExe); - if (!pCon) + if (pCon) { - pCon = SCLoad(conn); + SCWrite(pCon,txt,eWarning); } - if(pCon) - { - SCWrite(pCon,txt,eWarning); - } else { - ServerWriteGlobal(txt,eWarning); + pCon = SCStorePush(conn); + SCWrite(pCon, txt, eWarning); + SCStorePop(conn); } } /*-----------------------------------------------------------------------*/ @@ -390,7 +406,7 @@ static void ErrReport(pEVControl self) { sprintf(pBueffel,"WARNING: %s is out of range by %g", self->pName,fDelta); - ErrWrite(pBueffel, &self->conn); + ErrWrite(pBueffel, self->conn); self->iWarned = 1; } } @@ -463,7 +479,7 @@ static void ErrReport(pEVControl self) snprintf(pBueffel,255, "ERROR: %s while processing errorscript for %s", pTcl->result,self->pName); - ErrWrite(pBueffel, &self->conn); + ErrWrite(pBueffel, self->conn); } /* assume that everything is fine again after the script @@ -477,7 +493,7 @@ static void ErrReport(pEVControl self) snprintf(pBueffel,255, "ERROR: script error handling requested for %s, but no script given", self->pName); - ErrWrite(pBueffel, &self->conn); + ErrWrite(pBueffel, self->conn); } return 1; @@ -507,7 +523,7 @@ static void ErrReport(pEVControl self) ErrReport(self); - ErrWrite("Running to safe value", &self->conn); + ErrWrite("Running to safe value", self->conn); self->pDriv->SetValue(self->pDriv, ObVal(self->pParam,SAFEVALUE)); self->eMode = EVIdle; self->iWarned = 0; @@ -599,7 +615,7 @@ static void ErrReport(pEVControl self) { sprintf(pBueffel,"Environment device %s back in tolerances again", self->pName); - ErrWrite(pBueffel, &self->conn); + ErrWrite(pBueffel, self->conn); self->iWarned = 0; } return 1; @@ -780,6 +796,7 @@ static void ErrReport(pEVControl self) pRes->pName = strdup(pName); pRes->eMode = EVIdle; pRes->iWarned = 0; + pRes->conn = NULL; /* a terminal error gives a -1 in iRet */ if(iRet < 0) @@ -848,6 +865,14 @@ static void ErrReport(pEVControl self) { free(self->creationArgs); } + if (self->runScript != NULL) + { + free(self->runScript); + } + if (self->conn != NULL) + { + SCStoreFree(self->conn); + } free(self); } /*--------------------------------------------------------------------------*/ @@ -855,7 +880,7 @@ static void ErrReport(pEVControl self) { char pBueffel[256], pError[132]; int iRet; - + assert(self); assert(pCon); @@ -951,12 +976,15 @@ static void ErrReport(pEVControl self) { ObPar *pPar = NULL; char pBueffel[512]; - int iRet; + int iRet, savedStatus; assert(self); assert(pCon); + savedStatus = GetStatus(); /* fool status check in ObParSet (avoid "Cannot change parameter while running" message */ + SetStatus(eBatch); iRet = ObParSet(self->pParam,self->pName,name,fVal,pCon); + SetStatus(savedStatus); if(!iRet) { return iRet; @@ -1025,7 +1053,14 @@ static void ErrReport(pEVControl self) snprintf(pBueffel,255,"%s.errorScript = UNDEFINED", self->pName); } SCWrite(pCon,pBueffel, eValue); - + if(self->runScript != NULL) + { + SCPrintf(pCon, eValue, "%s.runScript = %s", self->pName, self->runScript); + } + else + { + SCPrintf(pCon, eValue, "%s.runScript = none", self->pName); + } return 1; } /*-------------------------------------------------------------------------*/ @@ -1075,8 +1110,11 @@ static void ErrReport(pEVControl self) iRet = EVCGetPos(self,pCon,&fPos); if(iRet) { +/* sprintf(pBueffel,"%s.%s = %g",self->pName,"CurrentValue", fPos); SCWrite(pCon,pBueffel,eValue); +*/ + SCPrintf(pCon, eValue, "%s = %g", argv[0], fPos); return 1; } return 0; @@ -1161,10 +1199,10 @@ static void ErrReport(pEVControl self) } else /* parameter request */ { + strtolower(argv[1]); /* catch case of errorScript */ - strtolower(argv[1]); if(strcmp(argv[1],"errorscript") == 0) { if(self->errorScript != NULL) @@ -1180,6 +1218,22 @@ static void ErrReport(pEVControl self) SCWrite(pCon,pBueffel,eValue); return 1; } + /* + catch case of runScript + */ + if(strcmp(argv[1],"runscript") == 0) + { + if(self->runScript != NULL) + { + SCPrintf(pCon, eValue,"%s.runScript = %s",self->pName, + self->runScript); + } + else + { + SCPrintf(pCon, eValue,"%s.runScript = none",self->pName); + } + return 1; + } /* catch case for drivername */ @@ -1208,10 +1262,10 @@ static void ErrReport(pEVControl self) } else /* try to set parameter */ { + strtolower(argv[1]); /* first catch case of errorScript */ - strtolower(argv[1]); if(strcmp(argv[1],"errorscript") == 0) { if(self->errorScript != NULL) @@ -1223,6 +1277,24 @@ static void ErrReport(pEVControl self) SCparChange(pCon); return 1; } + /* + catch case of runScript + */ + if(strcmp(argv[1],"runscript") == 0) + { + if(self->runScript != NULL) + { + free(self->runScript); + } + if (strcasecmp(argv[2],"none") == 0) { + self->runScript = NULL; + } else { + self->runScript = Arg2Tcl(argc-2,&argv[2],NULL,0); + } + SCSendOK(pCon); + SCparChange(pCon); + return 1; + } iRet = Tcl_GetDouble(pSics->pTcl,argv[2],&dVal); if(iRet != TCL_OK) { @@ -1263,6 +1335,9 @@ static int EVSaveStatus(void *pData, char *name, FILE *fil) if(evc->errorScript != NULL) { fprintf(fil, " %s errorScript %s\n", evc->pName, evc->errorScript); } + if(evc->runScript != NULL) { + fprintf(fil, " %s runScript %s\n", evc->pName, evc->runScript); + } } fprintf(fil, "}\n"); } diff --git a/evcontroller.h b/evcontroller.h index 9bff66b5..96fc8d08 100644 --- a/evcontroller.h +++ b/evcontroller.h @@ -1,5 +1,5 @@ -#line 227 "evcontroller.w" +#line 232 "evcontroller.w" /*-------------------------------------------------------------------------- E N V I R O N M E N T C O N T R O L L E R @@ -14,7 +14,7 @@ #define SICSEVCONTROL #include "varlog.h" -#line 148 "evcontroller.w" +#line 153 "evcontroller.w" /*--------------------------- live & death --------------------------------*/ typedef struct __EVControl *pEVControl; @@ -45,6 +45,6 @@ -#line 240 "evcontroller.w" +#line 245 "evcontroller.w" #endif diff --git a/evcontroller.i b/evcontroller.i index 966d7c3f..ad57cdbc 100644 --- a/evcontroller.i +++ b/evcontroller.i @@ -1,5 +1,5 @@ -#line 244 "evcontroller.w" +#line 249 "evcontroller.w" /*------------------------------------------------------------------------- Environment controller datastructure @@ -41,11 +41,12 @@ int iWarned; int iTcl; int iStop; - SCStore conn; + SCStore *conn; char *creationArgs; + char *runScript; void *pPrivate; void (*KillPrivate)(void *pData); } EVControl; -#line 262 "evcontroller.w" +#line 267 "evcontroller.w" diff --git a/evcontroller.tex b/evcontroller.tex index 06900005..c51b8ee5 100644 --- a/evcontroller.tex +++ b/evcontroller.tex @@ -52,12 +52,13 @@ $\langle$evdata {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ int iWarned;@\\ \mbox{}\verb@ int iTcl;@\\ \mbox{}\verb@ int iStop;@\\ -\mbox{}\verb@ SCStore conn;@\\ +\mbox{}\verb@ SCStore *conn;@\\ \mbox{}\verb@ char *creationArgs;@\\ +\mbox{}\verb@ char *runScript;@\\ \mbox{}\verb@ void *pPrivate;@\\ \mbox{}\verb@ void (*KillPrivate)(void *pData);@\\ \mbox{}\verb@ } EVControl;@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -88,6 +89,10 @@ holding the logging information. Then there is a switch, iWarned, which is used to prevent execessive output on environment controller error handling. iTcl is a boolean stating if the driver used is a proper C language driver or a Tcl driver. + creationArgs are the arguments needed to recreate the device. runScript +is a script called on every run or drive command. This script is intended +to set control parameters depending on the targetValue. The script is +called with the target temperature as argument. This is followed by the void pointer for use by a derived class. KillPrivate is a pointer to a function capable of deleting pPrivate properly. @@ -118,7 +123,7 @@ $\langle$evdriv {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ void *pPrivate;@\\ \mbox{}\verb@ void (*KillPrivate)(void *pData);@\\ \mbox{}\verb@ } EVDriver;@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -203,7 +208,7 @@ $\langle$dvfunc {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ int argc, char *argv[]);@\\ \mbox{}\verb@ @\\ \mbox{}\verb@@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -281,7 +286,7 @@ See the documentation for commands understood. \mbox{}\verb@#include "varlog.h"@\\ \mbox{}\verb@@$\langle$dvfunc {\footnotesize ?}$\rangle$\verb@@\\ \mbox{}\verb@#endif@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-2ex} \end{minipage}\\[4ex] @@ -310,7 +315,7 @@ See the documentation for commands understood. \mbox{}\verb@#define SETTLE 8@\\ \mbox{}\verb@@\\ \mbox{}\verb@@$\langle$evdata {\footnotesize ?}$\rangle$\verb@@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-2ex} \end{minipage}\\[4ex] @@ -335,7 +340,7 @@ See the documentation for commands understood. \mbox{}\verb@/*-------------------- life & death of a driver --------------------------*/@\\ \mbox{}\verb@ pEVDriver CreateEVDriver(int argc, char *argv[]);@\\ \mbox{}\verb@ void DeleteEVDriver(pEVDriver pDriv);@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-2ex} \end{minipage}\\[4ex] diff --git a/evcontroller.w b/evcontroller.w index ee8304b1..0146f28f 100644 --- a/evcontroller.w +++ b/evcontroller.w @@ -47,8 +47,9 @@ used by EVControl: int iWarned; int iTcl; int iStop; - SCStore conn; + SCStore *conn; char *creationArgs; + char *runScript; void *pPrivate; void (*KillPrivate)(void *pData); } EVControl; @@ -76,6 +77,10 @@ holding the logging information. Then there is a switch, iWarned, which is used to prevent execessive output on environment controller error handling. iTcl is a boolean stating if the driver used is a proper C language driver or a Tcl driver. + creationArgs are the arguments needed to recreate the device. runScript +is a script called on every run or drive command. This script is intended +to set control parameters depending on the targetValue. The script is +called with the target temperature as argument. This is followed by the void pointer for use by a derived class. KillPrivate is a pointer to a function capable of deleting pPrivate properly. diff --git a/evdriver.i b/evdriver.i index 42f8701d..7cf16623 100644 --- a/evdriver.i +++ b/evdriver.i @@ -1,5 +1,5 @@ -#line 265 "evcontroller.w" +#line 270 "evcontroller.w" /*------------------------------------------------------------------------- Environment device driver datastructure @@ -12,7 +12,7 @@ #define DEVREDO 2 -#line 87 "evcontroller.w" +#line 92 "evcontroller.w" typedef struct __EVDriver { int (*SetValue)(pEVDriver self, float fNew); @@ -31,7 +31,7 @@ void (*KillPrivate)(void *pData); } EVDriver; -#line 276 "evcontroller.w" +#line 281 "evcontroller.w" /*-------------------- life & death of a driver --------------------------*/ pEVDriver CreateEVDriver(int argc, char *argv[]); diff --git a/event.c b/event.c index de2fa970..98ab43be 100644 --- a/event.c +++ b/event.c @@ -64,6 +64,11 @@ "BATCHAREA", "BATCHEND", "DRIVSTAT", + "STATUS", + "POSITION", + "HDBVAL", + "STATESTART", + "STATEEND", NULL }; diff --git a/event.h b/event.h index a990d827..ee92c179 100644 --- a/event.h +++ b/event.h @@ -1,5 +1,5 @@ -#line 89 "event.w" +#line 100 "event.w" /*---------------------------------------------------------------------------- E V E N T @@ -18,7 +18,7 @@ int Text2Event(char *pText); -#line 102 "event.w" +#line 113 "event.w" @@ -42,21 +42,26 @@ #define BATCHAREA 15 #define BATCHEND 16 #define DRIVSTAT 17 -#define STATUS 18 -#define POSITION 19 /* Position event for motors - ffr */ -#line 104 "event.w" +#define STATUS 18 +#define POSITION 19 +#define HDBVAL 20 +#define STSTART 21 +#define STEND 22 + +#line 115 "event.w" /*--------------- Signals for the Signalfunction of each task ------------*/ -#line 73 "event.w" +#line 82 "event.w" #define SICSINT 300 #define SICSBROADCAST 301 #define TOKENGRAB 302 #define TOKENRELEASE 303 #define COMLOG 304 +#define CRONLIST 305 -#line 107 "event.w" +#line 118 "event.w" #endif diff --git a/event.tex b/event.tex index b19694ee..c3856e01 100644 --- a/event.tex +++ b/event.tex @@ -17,7 +17,7 @@ $\langle$eFunc {\footnotesize ?}$\rangle\equiv$ \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ int Text2Event(char *pText);@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -53,7 +53,12 @@ $\langle$VE {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@#define BATCHAREA 15@\\ \mbox{}\verb@#define BATCHEND 16@\\ \mbox{}\verb@#define DRIVSTAT 17@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@#define STATUS 18@\\ +\mbox{}\verb@#define POSITION 19@\\ +\mbox{}\verb@#define HDBVAL 20@\\ +\mbox{}\verb@#define STSTART 21@\\ +\mbox{}\verb@#define STEND 22@\\ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -87,6 +92,10 @@ operation. \item[BATCHEND] signals the end of the batch buffers processing. \item[DRIVSTAT] signals a change in the status of a driving operation (start, finished, fault) +\item[STATUS] ANSTO defined code. +\item[POSITION] ANSTO defined code +\item[HDBVAL] The Hdb is notified of a value change. The eventData will be + the object on which the data changed. \end{description} Furthermore event contains system wide signal codes which are interpreted in @@ -106,7 +115,8 @@ $\langle$VSIG {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@#define TOKENGRAB 302@\\ \mbox{}\verb@#define TOKENRELEASE 303@\\ \mbox{}\verb@#define COMLOG 304@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@#define CRONLIST 305@\\ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -123,6 +133,7 @@ data is the string to send. \item[TOKENGRAB] A connection has successfully grabbed the control token. \item[TOKENRELEASE] A connection has released the control token. \item[COMLOG] A command log message. This is to implement listen mode to the command log. +\item[CRONLIST] Tell the cron tasks to inform about themselves. \end{description} \begin{flushleft} \small \begin{minipage}{\linewidth} \label{scrap4} @@ -149,7 +160,7 @@ data is the string to send. \mbox{}\verb@/*--------------- Signals for the Signalfunction of each task ------------*/@\\ \mbox{}\verb@@$\langle$VSIG {\footnotesize ?}$\rangle$\verb@ @\\ \mbox{}\verb@#endif@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-2ex} \end{minipage}\\[4ex] diff --git a/event.w b/event.w index b165cf6a..fc8e584d 100644 --- a/event.w +++ b/event.w @@ -36,6 +36,11 @@ if the event code is not known, else the apropriate event code. #define BATCHAREA 15 #define BATCHEND 16 #define DRIVSTAT 17 +#define STATUS 18 +#define POSITION 19 +#define HDBVAL 20 +#define STSTART 21 +#define STEND 22 @} \begin{description} \item[VALUECHANGE] This is a variable changing its value. As event data a pointer to the @@ -62,6 +67,10 @@ operation. \item[BATCHEND] signals the end of the batch buffers processing. \item[DRIVSTAT] signals a change in the status of a driving operation (start, finished, fault) +\item[STATUS] ANSTO defined code. +\item[POSITION] ANSTO defined code +\item[HDBVAL] The Hdb is notified of a value change. The eventData will be + the object on which the data changed. \end{description} Furthermore event contains system wide signal codes which are interpreted in @@ -76,6 +85,7 @@ possible codes are defined. #define TOKENGRAB 302 #define TOKENRELEASE 303 #define COMLOG 304 +#define CRONLIST 305 @} \begin{description} \item[SICSINT] An interrupt has ocurred. The signal data is the interrupt @@ -85,6 +95,7 @@ data is the string to send. \item[TOKENGRAB] A connection has successfully grabbed the control token. \item[TOKENRELEASE] A connection has released the control token. \item[COMLOG] A command log message. This is to implement listen mode to the command log. +\item[CRONLIST] Tell the cron tasks to inform about themselves. \end{description} @o event.h -d @{ /*---------------------------------------------------------------------------- diff --git a/exe.w b/exe.w index 3afd21ba..cd01232c 100644 --- a/exe.w +++ b/exe.w @@ -89,6 +89,19 @@ The interface to this buffer system comprises: */ int exeBufProcess(pExeBuf self, SicsInterp *pSics, SConnection *pCon, pICallBack pCall, int echo); + /** + * Process an exe buffer, but store commands causing errors in a list. + * This is used for restoring status files. + * @@param self The exe buffer to process + * @@param pSics The SICS interpreter to use for processing + * @@param pCon The connection object providing the environment for + * processing this buffer. + * @@param errCommandList A lld string list for storing commands which + * had errors. + * @@return 1 on success, 0 on error + */ + int exeBufProcessErrList(pExeBuf self, SicsInterp *pSics, + SConnection *pCon, int errCommandList); /** * retrieves the executing range * @@param self The exe buffer to query @@ -171,6 +184,12 @@ int MakeExeManager(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); int ExeManagerWrapper(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); +int runExeBatchBuffer(void *pData, SConnection *pCon, SicsInterp *pSics, + char *name); +pDynString findBatchFile(SicsInterp *pSics, char *name); +int exeHdbBuffer(SConnection *pCon, + SicsInterp *pSics, char *name); +int exeHdbNode(pHdb exeNode, SConnection *pCon); @} @o exeman.i -d @{ diff --git a/exebuf.c b/exebuf.c index b38dfd7a..7ee47eb7 100644 --- a/exebuf.c +++ b/exebuf.c @@ -11,6 +11,7 @@ #include #include #include +#include "lld_str.h" #include "fortify.h" #include "sics.h" #include "exebuf.h" @@ -90,7 +91,9 @@ int exeBufLoad(pExeBuf self, char *filename){ return 0; } while(fgets(line,255,fd) != NULL){ - status = exeBufAppend(self,line); + /* Do not use exeBufAppend here. Lines longer than 255 would get + newline characters within the line */ + status = DynStringConcat(self->bufferContent,line); if(status != 1){ fclose(fd); return 0; @@ -166,13 +169,17 @@ int exeBufProcess(pExeBuf self, SicsInterp *pSics, self->lineno = 0; pTcl = InterpGetTcl(pSics); - InvokeCallBack(pCall,BATCHSTART,self->name); - + if(pCall != NULL){ + InvokeCallBack(pCall,BATCHSTART,self->name); + } + if (echo) { SCsetMacro(pCon,0); } while((command = findBlockEnd(self)) != NULL){ - InvokeCallBack(pCall,BATCHAREA,NULL); + if(pCall != NULL){ + InvokeCallBack(pCall,BATCHAREA,NULL); + } cmd = GetCharArray(command); if (echo) { @@ -223,7 +230,53 @@ int exeBufProcess(pExeBuf self, SicsInterp *pSics, SCSetInterrupt(pCon,eContinue); } } - InvokeCallBack(pCall,BATCHEND,self->name); + if(pCall != NULL){ + InvokeCallBack(pCall,BATCHEND,self->name); + } + return 1; +} +/*---------------------------------------------------------------------*/ +int exeBufProcessErrList(pExeBuf self, SicsInterp *pSics, + SConnection *pCon, int errList){ + pDynString command = NULL; + Tcl_Interp *pTcl = NULL; + int status; + + static int weWantLogging = 1; + char *cmd; + char cmdName[128]; + char *error; + char msg[132]; + char *ende; + int l; + + assert(self); + assert(pSics); + + self->start = 0; + self->end = -1; + self->lineno = 0; + pTcl = InterpGetTcl(pSics); + + while((command = findBlockEnd(self)) != NULL){ + cmd = GetCharArray(command); + + status = Tcl_Eval(pTcl,cmd); + if(status != TCL_OK){ + LLDstringAppend(errList,cmd); + error = (char *)Tcl_GetStringResult(pTcl); + snprintf(msg, sizeof msg, "#ERR: %s\n", error); + LLDstringAppend(errList,msg); + } + DeleteDynString(command); + if(SCGetInterrupt(pCon) >= eAbortBatch){ + SCWrite(pCon,"ERROR: batch processing interrupted",eError); + SetStatus(eEager); + return 0; + } else { + SCSetInterrupt(pCon,eContinue); + } + } return 1; } /*------------------------------------------------------------------------*/ diff --git a/exebuf.h b/exebuf.h index 9e1792f2..bb1edba4 100644 --- a/exebuf.h +++ b/exebuf.h @@ -1,5 +1,5 @@ -#line 207 "exe.w" +#line 226 "exe.w" /** * Buffer handling code for the Exe Buffer batch file processing @@ -63,6 +63,19 @@ */ int exeBufProcess(pExeBuf self, SicsInterp *pSics, SConnection *pCon, pICallBack pCall, int echo); + /** + * Process an exe buffer, but store commands causing errors in a list. + * This is used for restoring status files. + * @param self The exe buffer to process + * @param pSics The SICS interpreter to use for processing + * @param pCon The connection object providing the environment for + * processing this buffer. + * @param errCommandList A lld string list for storing commands which + * had errors. + * @return 1 on success, 0 on error + */ + int exeBufProcessErrList(pExeBuf self, SicsInterp *pSics, + SConnection *pCon, int errCommandList); /** * retrieves the executing range * @param self The exe buffer to query @@ -89,7 +102,7 @@ */ char *exeBufName(pExeBuf self); -#line 220 "exe.w" +#line 239 "exe.w" #endif diff --git a/exebuf.i b/exebuf.i index 3d4638ba..32da477f 100644 --- a/exebuf.i +++ b/exebuf.i @@ -1,5 +1,5 @@ -#line 198 "exe.w" +#line 217 "exe.w" /*-------------------------------------------------------------------- Internal header file for the exe buffer module. Do not edit. This is @@ -16,6 +16,6 @@ typedef struct __EXEBUF{ int lineno; } ExeBuf; -#line 203 "exe.w" +#line 222 "exe.w" diff --git a/exeman.c b/exeman.c index 468062b1..d5415592 100644 --- a/exeman.c +++ b/exeman.c @@ -15,14 +15,16 @@ #include #include "fortify.h" #include "sics.h" -#include "exebuf.h" -#include "exeman.h" #include "sdynar.h" #include "dynstring.h" #include "lld.h" -#include "exeman.i" #include "splitter.h" - +#include "exebuf.h" +#include "exeman.i" +#include "exeman.h" +#include "sicshipadaba.h" +#include "commandlog.h" +#include "protocol.h" /*-------------------------------------------------------------------*/ static void KillExeMan(void *data){ pExeMan self = (pExeMan)data; @@ -175,9 +177,46 @@ static pDynString locateBatchBuffer(pExeMan self, char *name){ DeleteDynString(result); return NULL; } -/*-------------------------------------------------------------------*/ +/*------------------------------------------------------------------- + * Generate a full path name for the argument in the first + * directory of batch path + * -------------------------------------------------------------------*/ +static int makeExePath(pExeMan self, SConnection *pCon, int argc, char *argv[]){ + char buffer[512], *pPtr = NULL, pPath[132]; + + if(argc < 3) { + SCWrite(pCon,"ERROR: require a file name for makepath",eError); + return 0; + } + strcpy(buffer,"exe.makepath = "); + /* + * do nothing to absolute path + */ + if(argv[2][0] == '/'){ + strncat(buffer,argv[2],511-strlen(buffer)); + SCWrite(pCon,buffer,eValue); + return 1; + } + pPtr = self->batchPath; + pPtr = stptok(pPtr,pPath,131,":"); + strncat(buffer,pPath,511-strlen(buffer)); + strncat(buffer,"/",511-strlen(buffer)); + strncat(buffer,argv[2],511-strlen(buffer)); + SCWrite(pCon,buffer,eValue); + + return 1; +} +/*--------------------------------------------------------------------*/ +pDynString findBatchFile(SicsInterp *pSics, char *name){ + pExeMan self = (pExeMan)FindCommandData(pSics,"exe","ExeManager"); + if(self == NULL){ + return NULL; + } + return locateBatchBuffer(self,name); +} +/*--------------------------------------------------------------------*/ static int runBatchBuffer(pExeMan self, SConnection *pCon, - SicsInterp *pSics, char *name){ + SicsInterp *pSics, char *name){ pDynString filePath = NULL; char pBueffel[256]; pExeBuf buffer = NULL; @@ -214,6 +253,221 @@ static int runBatchBuffer(pExeMan self, SConnection *pCon, self->exeStackPtr--; return status; } +/*-------------------------------------------------------------------*/ +static char bufferNode[512]; + +static int SCHdbWrite(SConnection *self, char *message, int outCode){ + pHdb node = NULL; + char pBueffel[512]; + commandContext cc; + hdbValue v; + pDynString val = NULL; + writeFunc defWrite = NULL; + + + cc = SCGetContext(self); + node = GetHipadabaNode(GetHipadabaRoot(),cc.deviceID); + if(node == NULL || strstr(cc.deviceID,bufferNode) == NULL){ + /* + * this means the deviceId is wrong and the output is for another + * operation. + */ + defWrite = GetProtocolWriteFunc(self); + if(defWrite == NULL){ + defWrite = SCNormalWrite; + } + defWrite(self,message,outCode); + return 1; + } + + SCFileWrite(self,message,outCode); + + if(SCinMacro(self) && (outCode != eError && outCode != eWarning) ){ + return 1; + } + + v = MakeHdbText(strdup("")); + GetHipadabaPar(node,&v,NULL); + v.dataType = HIPTEXT; + val = CreateDynString(128,128); + if(val == NULL){ + WriteToCommandLog("INTERNAL ERROR>>", + "No memory to append to log in SCHdbWrite"); + return 0; + } + if(v.v.text != NULL){ + DynStringConcat(val,v.v.text); + if(strrchr(v.v.text,(int)'\n') == NULL && strlen(v.v.text) > 1){ + DynStringConcatChar(val,'\n'); + } + } + DynStringConcat(val,message); + if(strrchr(message,(int)'\n') == NULL && strlen(message) > 1){ + DynStringConcatChar(val,'\n'); + } + if(v.v.text != NULL){ + free(v.v.text); + } + v.v.text = GetCharArray(val); + UpdateHipadabaPar(node,v,NULL); + DeleteDynString(val); + return 1; +} +/*--------------------------------------------------------------------*/ +int exeHdbNode(pHdb exeNode, SConnection *pCon){ + char pBueffel[512], *name = NULL; + pHdb node = NULL, log = NULL; + pExeBuf buffer = NULL; + hdbValue v; + int status; + commandContext cc; + writeFunc oldWrite; + + /* + * clear log buffer + */ + log = GetHipadabaNode(exeNode,"log"); + if(log == NULL){ + SCWrite(pCon,"ERROR: Hdb node not found or in wrong format",eError); + return 0; + } + v = MakeHdbText(strdup("")); + UpdateHipadabaPar(log,v,pCon); + /* + * prepare context + */ + name = GetHipadabaPath(log); + cc = SCGetContext(pCon); + strncpy(cc.deviceID, name,255); + strncpy(bufferNode,name,511); + + /* + * load commands into buffer + */ + node = GetHipadabaNode(exeNode,"commands"); + if(node == NULL){ + SCWrite(pCon,"ERROR: Hdb node not found or in wrong format",eError); + return 0; + } + + GetHipadabaPar(node,&v,pCon); + if(v.dataType != HIPTEXT || v.v.text == NULL){ + SCWrite(pCon,"ERROR: Hdb node is of wrong type or contains no data",eError); + return 0; + + } + + buffer = exeBufCreate(name); + if(!buffer){ + SCWrite(pCon,"ERROR: out of memory creating batch buffer",eError); + return 0; + } + exeBufAppend(buffer,v.v.text); + + strncpy(bufferNode,name,511); + oldWrite = SCGetWriteFunc(pCon); + SCSetWriteFunc(pCon,SCHdbWrite); + SCPushContext2(pCon,cc); + status = exeBufProcess(buffer,pServ->pSics,pCon,NULL,0); + SCSetWriteFunc(pCon,oldWrite); + SCPopContext(pCon); + exeBufDelete(buffer); + free(name); + if(strlen(log->value.v.text) < 2){ + v = MakeHdbText(strdup("OK\n")); + UpdateHipadabaPar(log,v,pCon); + ReleaseHdbValue(&v); + } + return status; +} +/*--------------------------------------------------------------------*/ +static int runHdbBuffer(pExeMan self, SConnection *pCon, + SicsInterp *pSics, char *name){ + char pBueffel[512]; + pExeBuf buffer = NULL; + pHdb node = NULL; + hdbValue v; + int status; + commandContext cc; + writeFunc oldWrite; + + if(!SCMatchRights(pCon,usUser)) { + return 0; + } + + /* + * clear log buffer + */ + snprintf(pBueffel,511,"%s/log",name); + node = GetHipadabaNode(GetHipadabaRoot(),pBueffel); + if(node == NULL){ + SCWrite(pCon,"ERROR: Hdb node not found or in wrong format",eError); + return 0; + } + v = MakeHdbText(strdup("")); + UpdateHipadabaPar(node,v,pCon); + /* + * prepare context + */ + cc = SCGetContext(pCon); + strcpy(cc.deviceID, pBueffel); + + /* + * load commands into buffer + */ + snprintf(pBueffel,511,"%s/commands",name); + node = GetHipadabaNode(GetHipadabaRoot(),pBueffel); + if(node == NULL){ + SCWrite(pCon,"ERROR: Hdb node not found or in wrong format",eError); + return 0; + } + + GetHipadabaPar(node,&v,pCon); + if(v.dataType != HIPTEXT || v.v.text == NULL){ + SCWrite(pCon,"ERROR: Hdb node is of wrong type or contains no data",eError); + return 0; + + } + + buffer = exeBufCreate(name); + if(!buffer){ + SCWrite(pCon,"ERROR: out of memory creating batch buffer",eError); + return 0; + } + exeBufAppend(buffer,v.v.text); + + strncpy(bufferNode,name,511); + oldWrite = SCGetWriteFunc(pCon); + SCSetWriteFunc(pCon,SCHdbWrite); + SCPushContext2(pCon,cc); + self->exeStackPtr++; + DynarPut(self->exeStack,self->exeStackPtr,buffer); + status = exeBufProcess(buffer,pSics,pCon,self->pCall,self->echo); + self->exeStackPtr--; + SCSetWriteFunc(pCon,oldWrite); + SCPopContext(pCon); + return status; +} +/*--------------------------------------------------------------------*/ +int exeHdbBuffer(SConnection *pCon, + SicsInterp *pSics, char *name){ + pExeMan self = (pExeMan)FindCommandData(pSics,"exe","ExeManager"); + if(self != NULL){ + return runHdbBuffer(self,pCon,pSics,name); + } + return 0; +} +/*-------------------------------------------------------------------*/ +int runExeBatchBuffer(void *pData, SConnection *pCon, + SicsInterp *pSics, char *name){ + int status, oldEcho; + pExeMan self = (pExeMan)pData; + oldEcho = self->echo; + self->echo = 1; + status = runBatchBuffer(self,pCon,pSics,name); + self->echo = oldEcho; + return status; +} /*========================== path management ========================*/ static int handleBatchPath(pExeMan self, SConnection *pCon, int argc, char *argv[]){ @@ -374,6 +628,18 @@ static int startUpload(pExeMan self, SConnection *pCon){ } return 1; } +/*-------------------------------------------------------------------*/ +static int clearUpload(pExeMan self, SConnection *pCon){ + if(SCGetRights(pCon) > usUser){ + SCWrite(pCon,"ERROR: no permission to clear buffer upload",eError); + return 0; + } + if(self->uploadBuffer != NULL){ + exeBufDelete(self->uploadBuffer); + self->uploadBuffer = NULL; + } + return 1; +} /*---------------------------------------------------------------------*/ static int appendLine(pExeMan self, SConnection *pCon, int argc, char *argv[]){ @@ -803,11 +1069,14 @@ static int printBuffer(pExeMan self, SConnection *pCon, DeleteDynString(filePath); return 0; } + DeleteDynString(filePath); + SCStartBuffering(pCon); while(fgets(pLine,511,fd) != NULL){ SCWrite(pCon,pLine,eValue); } fclose(fd); - DeleteDynString(filePath); + filePath = SCEndBuffering(pCon); + SCWrite(pCon,GetCharArray(filePath),eValue); return 1; } /*========================== run stack ===============================*/ @@ -914,6 +1183,7 @@ int ExeManagerWrapper(SConnection *pCon, SicsInterp *pSics, void *pData, char pBufferName[256]; int status; pDynString dirList = NULL; + pDynString fullPath = NULL; self = (pExeMan)pData; assert(self != NULL); @@ -958,6 +1228,12 @@ int ExeManagerWrapper(SConnection *pCon, SicsInterp *pSics, void *pData, SCSendOK(pCon); } return status; + }else if(strcmp(argv[1],"clearupload") == 0){ + status = clearUpload(self,pCon); + if(status){ + SCSendOK(pCon); + } + return status; }else if(strcmp(argv[1],"info") == 0){ status = infoHandler(self,pCon,argc,argv); return status; @@ -983,6 +1259,24 @@ int ExeManagerWrapper(SConnection *pCon, SicsInterp *pSics, void *pData, SCWrite(pCon,"Nothing found",eValue); } return 1; + }else if(strcmp(argv[1],"fullpath") == 0){ + if(argc < 2){ + SCWrite(pCon,"ERROR: not enough arguments to exe fullpath",eError); + return 0; + } + fullPath = locateBatchBuffer(self,argv[2]); + if(fullPath == NULL){ + SCWrite(pCon,"ERROR: buffer NOT found",eError); + return 0; + } else { + DynStringInsert(fullPath,"exe.fullpath=",0); + SCWrite(pCon,GetCharArray(fullPath),eValue); + DeleteDynString(fullPath); + return 1; + } + return 1; + }else if(strcmp(argv[1],"makepath") == 0){ + return makeExePath(self,pCon,argc,argv); }else if(strcmp(argv[1],"clear") == 0){ clearQueue(self); SCSendOK(pCon); @@ -1005,10 +1299,20 @@ int ExeManagerWrapper(SConnection *pCon, SicsInterp *pSics, void *pData, SCPrintf(pCon, eValue, "exe echo = %d", self->echo); } return 1; + }else if(strcmp(argv[1],"runhdb") == 0){ + if (argc < 2) { + SCWrite(pCon,"ERROR: require path to root of queue node",eError); + SCSendOK(pCon); + } + status = runHdbBuffer(self,pCon,pSics,argv[2]); + if(self->exeStackPtr < 0){ + SCWrite(pCon,"EXE TERMINATED",eWarning); + } + return status; } else { status = runBatchBuffer(self,pCon,pSics,pBufferName); if(self->exeStackPtr < 0){ - SCWrite(pCon,"EXE TERMINATED",eWarning); + SCWrite(pCon,"EXE TERMINATED",eWarning); } return status; } diff --git a/exeman.h b/exeman.h index 5ebbbfc8..ddee48dd 100644 --- a/exeman.h +++ b/exeman.h @@ -13,5 +13,11 @@ int MakeExeManager(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); int ExeManagerWrapper(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); +int runExeBatchBuffer(void *pData, SConnection *pCon, SicsInterp *pSics, + char *name); +pDynString findBatchFile(SicsInterp *pSics, char *name); +int exeHdbBuffer(SConnection *pCon, + SicsInterp *pSics, char *name); +int exeHdbNode(pHdb exeNode, SConnection *pCon); #endif diff --git a/exeman.i b/exeman.i index 1918f5a1..8b8512fb 100644 --- a/exeman.i +++ b/exeman.i @@ -1,12 +1,12 @@ -#line 176 "exe.w" +#line 195 "exe.w" /*------------------------------------------------------------------- Internal header file for the exe manager module. Do not edit. This is automatically generated from exe.w -------------------------------------------------------------------*/ -#line 138 "exe.w" +#line 151 "exe.w" typedef struct __EXEMAN{ pObjectDescriptor pDes; @@ -20,5 +20,5 @@ typedef struct __EXEMAN{ int echo; }ExeMan, *pExeMan; -#line 181 "exe.w" +#line 200 "exe.w" diff --git a/fitcenter.c b/fitcenter.c index ceb37452..d042bd0a 100644 --- a/fitcenter.c +++ b/fitcenter.c @@ -101,21 +101,6 @@ GetScanVar(self->pScan,0,self->fAxis,self->iNP); GetScanVarName(self->pScan,0,self->pName,131); - /* correct fAxis for softzero points and sign - when the scan variable is a motor - */ - if(!isScanVarSoft(self->pScan)) - { - pMot = FindMotor(pServ->pSics,self->pName); - if(pMot) - { - for(i = 0; i < self->iNP; i++) - { - self->fAxis[i] = MotorHardToSoftPosition(pMot,self->fAxis[i]); - } - } - } - return 1; } /*-------------------------------------------------------------------------*/ @@ -421,6 +406,7 @@ pFit self = NULL; int iRet; char pBueffel[256]; + pDynString buf = NULL; self = (pFit)pData; assert(self); @@ -466,10 +452,18 @@ sprintf(pBueffel,"%f", self->fCenter); SCWrite(pCon,pBueffel,eValue); return 1; + } + if(strcmp(argv[1],"data") == 0) + { + snprintf(pBueffel,255,"%f,%f,%ld", + self->fCenter, self->FWHM, self->lPeak); + SCWrite(pCon,pBueffel,eValue); + return 1; } } /* print results */ + SCStartBuffering(pCon); sprintf(pBueffel,"Estimated Peak Center: %f, StdDev: %f \n", self->fCenter,self->fStddev); SCWrite(pCon,pBueffel,eValue); @@ -477,6 +471,10 @@ SCWrite(pCon,pBueffel,eValue); sprintf(pBueffel,"Approximate FWHM: %f\n",self->FWHM); SCWrite(pCon,pBueffel,eValue); + buf = SCEndBuffering(pCon); + if(buf != NULL){ + SCWrite(pCon,GetCharArray(buf),eValue); + } return 1; } diff --git a/fomerge.c b/fomerge.c index 96f5333c..b6dc9d20 100644 --- a/fomerge.c +++ b/fomerge.c @@ -17,6 +17,8 @@ Mark Koennecke, March 2000 extended to support nxscripted file writing: Mark Koennecke, May 2004 + + extended to support GTSE, Mark Koennecke, May 2008 --------------------------------------------------------------------------*/ #include #include @@ -27,6 +29,7 @@ #include "fortify.h" #include "scan.h" #include "fitcenter.h" +#include "sicsdata.h" static pFit fitter = NULL; @@ -483,11 +486,34 @@ static int updateHMFMData(SicsInterp *pSics, SConnection *pCon) return 1; } /*-------------------------------------------------------------------*/ -static int *calculateSum(HistInt *data, int iDet, int iTime) +static int *calculateDetSum(HistInt *data, int iDet, int iTime) { int i, j, iIndex; int *sum = NULL; + sum = (int *)malloc(iDet*sizeof(int)); + if(!sum) + { + return NULL; + } + memset(sum,0,iDet*sizeof(int)); + + for(i = 0; i < iDet; i++) + { + iIndex = i * iTime; + for(j = 0; j < iTime; j++) + { + sum[i] += data[iIndex+j]; + } + } + return sum; +} +/*-------------------------------------------------------------------*/ +static int *calculateTimeSum(HistInt *data, int iDet, int iTime) +{ + int i, j; + int *sum = NULL; + sum = (int *)malloc(iTime*sizeof(int)); if(!sum) { @@ -495,16 +521,16 @@ static int *calculateSum(HistInt *data, int iDet, int iTime) } memset(sum,0,iTime*sizeof(int)); - for(i = 0; i < iDet; i++) + for(i = 0; i < iTime; i++) { - iIndex = i * iTime; - for(j = 0; j < iTime; j++) + for(j = 0; j < iDet; j++) { - sum[i] += data[iIndex+j]; + sum[i] += data[j*iTime + i]; } } return sum; } + /*--------------------------------------------------------------------*/ static void checkSum(HistInt *sum, int iDet, char *name, SConnection *pCon){ int i, count; @@ -557,7 +583,7 @@ static int putSum(SicsInterp *pSics, SConnection *pCon, return NX_ERROR; } - sum = calculateSum(data,iDet,iTime); + sum = calculateDetSum(data,iDet,iTime); if(!sum) { SCWrite(pCon,"ERROR: out of memory summing bank",eError); @@ -571,10 +597,100 @@ static int putSum(SicsInterp *pSics, SConnection *pCon, return status; } /*---------------------------------------------------------------------*/ -static int putElastic(SicsInterp *pSics, SConnection *pCon, - pNXScript pNexus, char *alias, float fElastic) -{ +static int TOFLambda(SicsInterp *pSics, SConnection *pCon, + int argc, char *argv[]){ + int status, iTime, iDet, i; + const float *fTimeBin = NULL; + int *sum = NULL; + long *lSum = NULL; + pHistMem pMem = NULL; + float fCenter, fFWHM, fStdDev, fVal; + float fMon, fData, distMonoDet, distFermiDet, tdiff, lambda; + pMem = (pHistMem)FindCommandData(pSics,"hm1","HistMem"); + if(pMem == NULL) + { + SCWrite(pCon, + "ERROR: need lower detector bank for lambda calculation", + eError); + return 0; + } + + /** + * locate elastic position in data + */ + fTimeBin = GetHistTimeBin(pMem,&iTime); + iDet = getFMdim(LOWER); + sum = calculateTimeSum(GetHistogramPointer(pMem,pCon),iDet,iTime); + if(!sum) + { + SCWrite(pCon,"ERROR: out of memory calculating lambda", + eError); + return 0; + } + if(fitter == NULL) + { + fitter = CreateFitCenter(NULL); + if(!fitter) + { + SCWrite(pCon,"ERROR: cannot allocate fitting structure",eError); + return 0; + } + } + /* + copy sum to make compiler happy + */ + lSum = (long *)malloc(iTime*sizeof(long)); + if(lSum == NULL) + { + SCWrite(pCon,"ERROR: out of memory in TOFLambda",eError); + free(sum); + return 0; + } + for(i = 0; i < iTime; i++) + { + lSum[i] = sum[i]; + } + status = CalculateFitFromData(fitter,(float *)fTimeBin,lSum,iTime); + if(status < 0) + { + SCWrite(pCon,"ERROR: no peak in data",eError); + free(sum); + free(lSum); + return 0; + } + GetFitResults(fitter,&fCenter,&fStdDev,&fFWHM,&fVal); + fData = fCenter; + + /* + * locate elastic position in tofmon + */ + GetHistogram(pMem, pCon, 0, iTime*iDet, iTime*(iDet+1), + sum, iTime*sizeof(HistInt)); + for(i = 0; i < iTime; i++) + { + lSum[i] = sum[i]; + } + status = CalculateFitFromData(fitter,(float *)fTimeBin,lSum,iTime); + GetFitResults(fitter,&fCenter,&fStdDev,&fFWHM,&fVal); + fMon = fCenter; + free(sum); + free(lSum); + + /* + * calculate + */ + distFermiDet = 3000.; + distMonoDet = distFermiDet - 215.7; + tdiff = fData - fMon; + lambda = tdiff/(252.78*distMonoDet*.001); + SCPrintf(pCon,eValue, "toflambda = %f", lambda); + + return 1; +} +/*---------------------------------------------------------------------*/ +static float calcElastic(SicsInterp *pSics, SConnection *pCon) +{ int status, iTime, iDet, i; const float *fTimeBin = NULL; int *sum = NULL; @@ -588,16 +704,16 @@ static int putElastic(SicsInterp *pSics, SConnection *pCon, SCWrite(pCon, "ERROR: need middle detector bank for elastic peak calculation", eError); - return NX_ERROR; + return -1.; } fTimeBin = GetHistTimeBin(pMem,&iTime); iDet = getFMdim(MIDDLE); - sum = calculateSum(GetHistogramPointer(pMem,pCon),iDet,iTime); + sum = calculateTimeSum(GetHistogramPointer(pMem,pCon),iDet,iTime); if(!sum) { SCWrite(pCon,"ERROR: out of memory calculating elastic peak position", eError); - return NX_ERROR; + return -1; } if(fitter == NULL) { @@ -605,7 +721,7 @@ static int putElastic(SicsInterp *pSics, SConnection *pCon, if(!fitter) { SCWrite(pCon,"ERROR: cannot allocate fitting structure",eError); - return NX_ERROR; + return -1.; } } /* @@ -616,7 +732,7 @@ static int putElastic(SicsInterp *pSics, SConnection *pCon, { SCWrite(pCon,"ERROR: out of memory in putElastic",eError); free(sum); - return NX_ERROR; + return -1.; } for(i = 0; i < iTime; i++) { @@ -624,38 +740,180 @@ static int putElastic(SicsInterp *pSics, SConnection *pCon, } status = CalculateFitFromData(fitter,(float *)fTimeBin,lSum,iTime); free(lSum); - if(status != 1) - { - SCWrite(pCon,"WARNING: problem locating elastic peak",eWarning); - } GetFitResults(fitter,&fCenter,&fStdDev,&fFWHM,&fVal); - fVal = fCenter - fElastic; - if(fVal < 0.) - fVal = - fVal; - /* bad value, leave at theoretical value */ - if(fVal > 10.) - { - SCWrite(pCon, - "WARNING: bad fit result, using theoretical elastic peak position", - eWarning); - } - else - { - fElastic = fCenter; - } free(sum); - - status = NXDputalias(pNexus->fileHandle, pNexus->dictHandle,alias, - &fElastic); - return status; + return fCenter; +} +/*---------------------------------------------------------------------*/ +#define ABS(x) (x < 0 ? -(x) : (x)) +static int putElastic(SicsInterp *pSics, SConnection *pCon, + pNXScript pNexus, char *alias, float fElastic) +{ + float fCalc; + int status; + + fCalc = calcElastic(pSics,pCon); + if(ABS(fElastic -fCalc) < 20) { + fElastic = fCalc; + } + + status = NXDputalias(pNexus->fileHandle, pNexus->dictHandle,alias, + &fElastic); + return status; +} +/*----------------------------------------------------------------------*/ +static int FMputTTH(SConnection *pCon, int argc, char *argv[]){ + pSICSData data = NULL; + int length = -1, i; + float *tthData = NULL; + + if(argc < 4){ + SCWrite(pCon,"ERROR: insufficient no of arguments to FMputTTH", + eError); + return 0; + } + + data = (pSICSData)FindCommandData(pServ->pSics,argv[3],"SICSData"); + if(data == NULL){ + SCWrite(pCon,"ERROR: SICSData object not found", eError); + return 0; + } + if(strcmp(argv[2],"upper") == 0) + { + length = getFMdim(UPPER); + tthData = getFMBankTheta(UPPER); + } + else if(strcmp(argv[2],"middle") == 0) + { + length = getFMdim(MIDDLE); + tthData = getFMBankTheta(MIDDLE); + } + else if(strcmp(argv[2],"lower") == 0) + { + length = getFMdim(LOWER); + tthData = getFMBankTheta(LOWER); + } + else if(strcmp(argv[2],"merged") == 0) + { + length = getFMdim(MERGED); + tthData = getFMBankTheta(MERGED); + } + else + { + SCWrite(pCon,"ERROR: requested two_theta for invalid detector bank", + eError); + return 0; + } + if(length < 0 || tthData == NULL){ + SCWrite(pCon,"ERROR: requested two_theta for invalid detector bank", + eError); + return 0; + } + clearSICSData(data); + for(i = 0; i < length; i++){ + setSICSDataFloat(data,i,tthData[i]); + } + SCSendOK(pCon); + return 1; +} +/*---------------------------------------------------------------------*/ +static int FMcopyMerged(SConnection *pCon, int argc, char *argv[]){ + pSICSData data = NULL; + int i, length; + HistInt *hmData = NULL; + + if(argc < 3){ + SCWrite(pCon,"ERROR: insufficient no of arguments to FMcopyMerged", + eError); + return 0; + } + + data = (pSICSData)FindCommandData(pServ->pSics,argv[2],"SICSData"); + if(data == NULL){ + SCWrite(pCon,"ERROR: SICSData object not found", eError); + return 0; + } + if(!updateHMFMData(pServ->pSics, pCon)){ + SCWrite(pCon,"ERROR: not enough HM's to merge or bad names in fomerge.c", + eError); + return 0; + } + + clearSICSData(data); + length = getFMdim(MERGED)*getFMdim(TIMEBIN); + hmData = getFMBankPointer(MERGED); + if(hmData == NULL){ + SCWrite(pCon,"ERROR: merged data not available", eError); + return 0; + } + for(i = 0; i < length; i++){ + setSICSDataInt(data,i,hmData[i]); + } + SCSendOK(pCon); + return 1; +} +/*---------------------------------------------------------------------*/ +static int FMcopyMergedSum(SConnection *pCon, int argc, char *argv[]){ + pSICSData data = NULL; + int i, length, tbin, j, row; + HistInt *hmData = NULL, *sumData = NULL; + + if(argc < 3){ + SCWrite(pCon,"ERROR: insufficient no of arguments to FMcopyMerged", + eError); + return 0; + } + + data = (pSICSData)FindCommandData(pServ->pSics,argv[2],"SICSData"); + if(data == NULL){ + SCWrite(pCon,"ERROR: SICSData object not found", eError); + return 0; + } + if(!updateHMFMData(pServ->pSics, pCon)){ + SCWrite(pCon,"ERROR: not enough HM's to merge or bad names in fomerge.c", + eError); + return 0; + } + + clearSICSData(data); + length = getFMdim(MERGED); + tbin = getFMdim(TIMEBIN); + hmData = getFMBankPointer(MERGED); + if(hmData == NULL){ + SCWrite(pCon,"ERROR: merged data not available", eError); + return 0; + } + sumData = malloc(tbin*sizeof(int)); + if(sumData == NULL){ + SCWrite(pCon,"ERROR: out-of-memory in FMcopyMergedSum", eError); + return 0; + } + memset(sumData,0,tbin*sizeof(int)); + for(j = 0; j < length; j++){ + row = j*tbin; + for(i = 0; i < tbin; i++){ + sumData[i] += hmData[row+i]; + } + } + for(i = 0; i < tbin; i++){ + setSICSDataInt(data,i,sumData[i]); + } + free(sumData); + SCSendOK(pCon); + return 1; } /*----------------------------------------------------------------------- Usage: focusmerge puttwotheta nxscriptmod bankname alias + focusmerge puttth bankname sicsdataname + focusmerge copymerged sicsdataname + focusmerge copymergedsum sicsdataname focusmerge putmerged nxscriptmod alias focusmerge putsum nxscriptmod bankname alias focusmerge putelastic nxscriptmod alias theoelastic + focusmerge toflambda + focusmerge elastic nxscriptmod = name of the nxscript module used for writing, must be open alias = The alias under which to write the data item @@ -677,6 +935,26 @@ int FocusMergeAction(SConnection *pCon, SicsInterp *pSics, void *pData, } strtolower(argv[1]); + + if(strcmp(argv[1],"puttth") == 0){ + return FMputTTH(pCon,argc,argv); + } + + if(strcmp(argv[1],"copymerged") == 0){ + return FMcopyMerged(pCon,argc,argv); + } + if(strcmp(argv[1],"copymergedsum") == 0){ + return FMcopyMergedSum(pCon,argc,argv); + } + if(strcmp(argv[1],"toflambda") == 0){ + return TOFLambda(pSics, pCon,argc,argv); + } + if(strcmp(argv[1],"elastic") == 0){ + fElastic = calcElastic(pSics,pCon); + SCPrintf(pCon,eValue,"tofelastic = %f", fElastic); + return 1; + } + if(strcmp(argv[1],"puttwotheta") == 0) { if(argc < 4) diff --git a/fourlib.c b/fourlib.c index 96bffda1..f954f322 100644 --- a/fourlib.c +++ b/fourlib.c @@ -625,6 +625,10 @@ int findAllowedBisecting(double lambda, MATRIX z1, float fSet[4], return 0; } + if(testFunc(userData, fSet, mask) == 1){ + return 1; + } + for(psi = .0; psi < 360.; psi += .5){ rotatePsi(om,chi,phi,psi,&ompsi,&chipsi,&phipsi); fTest[0] = stt; @@ -638,6 +642,26 @@ int findAllowedBisecting(double lambda, MATRIX z1, float fSet[4], } return 1; } + /* + * if chi close to 0, or 180, try to wrap phi onto om + */ + if(ABS(fTest[2] - .0) < .1 || ABS(fTest[2] - 180.) < .1){ + fTest[1] -= fTest[3]; + fTest[3] = .0; + if(fTest[1] < 0.){ + fTest[1] += 360.; + } + if(fTest[1] > 360.0){ + fTest[1] -= 360.; + } + status = testFunc(userData,fTest,mask); + if(status == 1){ + for(i = 0; i < 4; i++){ + fSet[i] = fTest[i]; + } + return 1; + } + } if(mask[0] == 0) { /* * useless: when two theta problem there is no solution diff --git a/fourtable.h b/fourtable.h index a67718ef..0348975d 100644 --- a/fourtable.h +++ b/fourtable.h @@ -11,14 +11,14 @@ #ifndef FOURTABLE #define FOURTABLE - int MakeFourCircleTable(void); + int MakeFourCircleTable(); void DeleteFourCircleTable(int handle); - int HandleFourCircleCommands(int *table, SConnection *pCon, + int HandleFourCircleCommands(int *handle, SConnection *pCon, int argc, char *argv[], int *err); char *GetFourCircleScanVar(int handle, double two_theta); - int GetFourCircleScanNP(int handle, double two_theta); double GetFourCircleStep(int handle, double two_theta); - float GetFourCirclePreset(int handle, double two_theta); int SaveFourCircleTable(int handle, char *objName, FILE *fd); + float GetFourCirclePreset(int handle, double twoTheta); + int GetFourCircleScanNP(int handle, double twoTheta); #endif diff --git a/genericcontroller.c b/genericcontroller.c new file mode 100644 index 00000000..75586942 --- /dev/null +++ b/genericcontroller.c @@ -0,0 +1,622 @@ +/** + * This is a generic controller for devices in SICS. It is configurable via Tcl + * scripts. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, November 2007 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +/*--------------------------------------------------------------------------*/ +static hdbCallbackReturn GenConSetCallback(pHdb node, void *userData, + pHdbMessage message){ + pSICSOBJ self = (pSICSOBJ)userData; + SConnection *pCon = NULL; + pGenController priv = NULL; + char command[1024]; + char value[80]; + int status, privilege; + pDynString data; + pHdbDataMessage mm = NULL; + + assert(self != NULL); + + if((mm = GetHdbSetMessage(message)) == NULL){ + return hdbContinue; + } + + priv = (pGenController)self->pPrivate; + pCon = mm->callData; + + /* + * check rights + */ + memset(value,0,80); + if(GetHdbProperty(node,"priv",value,80) && pCon != NULL){ + privilege = usInternal; + if(strcmp(value,"manager") == 0){ + privilege = usMugger; + } + if(strcmp(value,"user") == 0){ + privilege = usUser; + } + if(!SCMatchRights(pCon,privilege)){ + return hdbAbort; + } + } + + /* + * check writeCommand + */ + memset(value,0,80); + GetHdbProperty(node,"writeCommand",value,80); + if(strlen(value) < 2){ + if(pCon != NULL){ + SCWrite(pCon,"ERROR: parameter is read-only",eError); + return hdbAbort; + } + return hdbAbort; + } + + /* + * check status + */ + memset(value,0,80); + GetHdbProperty(node,"status",value,80); + if(strstr(value,"idle") == NULL){ + return hdbAbort; + } + SetHdbProperty(node,"status","setting"); + data = formatValue(*(mm->v), node); + if(data != NULL){ + SetHdbProperty(node,"target",GetCharArray(data)); + DeleteDynString(data); + } + + /* + * issue command + */ + if(priv->enqueueNodeHead != NULL){ + priv->enqueueNodeHead(self,pCon,node); + } else { + if(pCon != NULL){ + SCWrite(pCon,"ERROR: generic controller NOT configured", + eError); + } + return hdbAbort; + } + + return hdbContinue; +} +/*--------------------------------------------------------------------------*/ +static hdbCallbackReturn GenConGetCallback(pHdb node, void *userData, + pHdbMessage message){ + pSICSOBJ self = (pSICSOBJ)userData; + SConnection *pCon = NULL; + pGenController priv = NULL; + char command[1024]; + char value[256]; + int status, privilege; + pHdbDataMessage mm = NULL; + + assert(self != NULL); + + if((mm = GetHdbGetMessage(message)) == NULL){ + return hdbContinue; + } + pCon = mm->callData; + + priv = (pGenController)self->pPrivate; + + /* + * check status + */ + memset(value,0,80); + GetHdbProperty(node,"status",value,80); + if(strstr(value,"idle") == NULL){ + return hdbContinue; + } + SetHdbProperty(node,"status","getting"); + + /* + * check readCommand + */ + memset(value,0,256); + GetHdbProperty(node,"readCommand",value,255); + if(strlen(value) < 2){ + return hdbAbort; + } else { + if(priv->enqueueNode != NULL){ + priv->enqueueNode(self,pCon, node); + } else { + if(pCon != NULL){ + SCWrite(pCon,"ERROR: generic controller connection NOT configured", + eError); + } + return hdbAbort; + } + } + + /* + * Upper Level GetHipadabaPar will automatically return the + * node value. Which should have been updated through an update + * during the execution of enqueueNode + */ + + return hdbContinue; +} +/*---------------------------------------------------------------------------*/ +static pHdb MakeGenConPar(pSICSOBJ self, char *name, int type, int length){ + pHdb result = NULL; + pHdbCallback kalle = NULL; + + result = MakeHipadabaNode(name,type,length); + if(result == NULL){ + return NULL; + } + + kalle = MakeHipadabaCallback(GenConSetCallback, + self, + NULL); + if(kalle == NULL){ + return NULL; + } + AppendHipadabaCallback(result,kalle); + + kalle = MakeHipadabaCallback(GenConGetCallback, + self, + NULL); + + if(kalle == NULL){ + return NULL; + } + AppendHipadabaCallback(result,kalle); + + SetHdbProperty(result,"priv","manager"); + SetHdbProperty(result,"readCommand",""); + SetHdbProperty(result,"writeCommand",""); + SetHdbProperty(result,"replyCommand",""); + SetHdbProperty(result,"status","idle"); + + return result; +} +/*---------------------------------------------------------------------------*/ +static int MakeGenPar(pSICSOBJ self, SConnection *pCon, + char *argv[], int argc){ + char buffer[2048]; + int type, length = 1; + pHdb node = NULL , parent; + char *pPtr = NULL; + + if(argc < 5){ + snprintf(buffer,2048,"ERROR: insufficient arguments to %s makepar", + argv[0]); + SCWrite(pCon,buffer, eError); + return 0; + } + type = convertHdbType(argv[4]); + if(argc > 5){ + length = atoi(argv[5]); + } + strncpy(buffer,argv[3],2047); + pPtr = strrchr(buffer,'/'); + if(pPtr == NULL){ + node = MakeGenConPar(self, argv[3], type, length); + parent = self->objectNode; + } else { + *pPtr = '\0'; + pPtr++; + node = MakeGenConPar(self, pPtr, type, length); + parent = GetHipadabaNode(self->objectNode,buffer); + } + if(node == NULL || parent == NULL){ + SCWrite(pCon,"ERROR: failed to create node or parent not found", + eError); + return 0; + } + AddHipadabaChild(parent, node, pCon); + SCSendOK(pCon); + return 1; +} +/*=============================== ========================================== + * This stuff is for the Tcl - AsynQueue implementation of GenericController + * ==========================================================================*/ + typedef struct { + pHdb node; + pSICSOBJ obj; + SConnection *pCon; + pGenController priv; + pAsyncUnit assi; + pAsyncTxn trans; + commandContext comCon; + char replyCommand[2048]; + } GenContext, *pGenContext; + /*-------------------------------------------------------------------------- + * This is called by AsyncQueue when a reply has been received. + * -------------------------------------------------------------------------*/ + static int GenConTxnHandler(pAsyncTxn pTxn){ + pGenContext genCon = NULL; + char reply[10240]; + + genCon = (pGenContext)pTxn->cntx; + assert(genCon != NULL); + + memset(reply,0,10240*sizeof(char)); + switch(pTxn->txn_state){ + case ATX_NULL: + case ATX_ACTIVE: + return 1; + break; + case ATX_TIMEOUT: + strcpy(reply,"TIMEOUT"); + break; + case ATX_DISCO: + strcpy(reply,"DISCONNECTED"); + break; + case ATX_COMPLETE: + strncpy(reply,pTxn->inp_buf,10240); + break; + } + + if(genCon->pCon != NULL){ + SCPushContext2(genCon->pCon, genCon->comCon); + } + genCon->priv->replyCallback(genCon->obj, genCon->pCon, + genCon->node, genCon->replyCommand, reply, strlen(reply)); + if(genCon->pCon != NULL){ + SCPopContext(genCon->pCon); + } + free(genCon); + + return 1; + } +/*--------------------------------------------------------------------------*/ +static char *formatCommand(pHdb node, SConnection *pCon){ + pDynString com = NULL; + char value[512]; + Tcl_Interp *pTcl = NULL; + int status; + + com = CreateDynString(256,128); + if(com == NULL){ + return NULL; + } + + memset(value,0,512); + GetHdbProperty(node,"status",value,512); + if(strstr(value,"set") != NULL){ + memset(value,0,512); + GetHdbProperty(node,"writeCommand",value,512); + DynStringConcat(com,value); + DynStringConcatChar(com,' '); + memset(value,0,512); + GetHdbProperty(node,"target",value,512); + DynStringConcat(com,value); + } else { + memset(value,0,512); + GetHdbProperty(node,"readCommand",value,512); + DynStringConcat(com,value); + } + pTcl = InterpGetTcl(pServ->pSics); + if(pCon != NULL){ + MacroPush(pCon); + } + status = Tcl_Eval(pTcl, GetCharArray(com)); + if(pCon != NULL){ + MacroPop(); + } + DeleteDynString(com); + if(status != TCL_OK){ + SetHdbProperty(node,"result", (char *)Tcl_GetStringResult(pTcl)); + return NULL; + } + return strdup(Tcl_GetStringResult(pTcl)); +} +/*--------------------------------------------------------------------------*/ +static pGenContext PrepareToEnque(pSICSOBJ self, SConnection *pCon, pHdb node){ + pGenContext result = NULL; + char *command = NULL; + pGenController priv = NULL; + + priv = (pGenController)self->pPrivate; + assert(priv != NULL); + + command = formatCommand(node, pCon); + if(command == NULL){ + return NULL; + } + + result = malloc(sizeof(GenContext)); + if(result == NULL){ + return NULL; + } + memset(result,0,sizeof(GenContext)); + if(!GetHdbProperty(node,"replyCommand",result->replyCommand,2048)){ + if(pCon != NULL){ + SCWrite(pCon,"ERROR: no replyCommand found",eError); + } + free(result); + return NULL; + } + + result->assi = AsyncUnitFromQueue((pAsyncQueue)priv->comContext); + if(result->assi == NULL){ + return NULL; + } + result->trans = AsyncUnitPrepareTxn(result->assi, + command,strlen(command), + GenConTxnHandler, + result, + 2048); + if(result->trans == NULL){ + return NULL; + } + result->node = node; + result->priv = priv; + result->obj = self; + result->pCon = pCon; + priv->comError = GCOK; + result->comCon = SCGetContext(pCon); + free(command); + + return result; +} +/*---------------------------------------------------------------------------*/ +static int AsyncEnqueueNode(pSICSOBJ self, SConnection *pCon, pHdb node){ + pGenContext genCon = NULL; + + genCon = PrepareToEnque(self, pCon, node); + if(genCon == NULL){ + return 0; + } + return AsyncUnitEnqueueTxn(genCon->assi, genCon->trans); +} +/*---------------------------------------------------------------------------*/ +static int AsyncEnqueueNodeHead(pSICSOBJ self, SConnection *pCon, pHdb node){ + pGenContext genCon = NULL; + + genCon = PrepareToEnque(self, pCon, node); + if(genCon == NULL){ + return 0; + } + return AsyncUnitEnqueueHead(genCon->assi, genCon->trans); +} +/*---------------------------------------------------------------------------*/ +static int AsyncReply(pSICSOBJ self, SConnection *pCon, pHdb node, + char *replyCommand, char *reply, int replylen){ + pDynString com = NULL; + Tcl_Interp *pTcl; + int status; + + SetHdbProperty(node,"result",reply); + + com = CreateDynString(256,128); + if(com == NULL){ + return 0; + } + DynStringConcat(com, replyCommand); + DynStringConcat(com," \""); + DynStringConcat(com, reply); + DynStringConcat(com,"\"\0"); + if(pCon != NULL){ + MacroPush(pCon); + } + pTcl = InterpGetTcl(pServ->pSics); + status = Tcl_Eval(pTcl,GetCharArray(com)); + if(pCon != NULL){ + MacroPop(); + } + DeleteDynString(com); + if(status != TCL_OK){ + SetHdbProperty(node,"lastError",(char *)Tcl_GetStringResult(pTcl)); + } + return status; +} +/*============= GenController Object Functions ==============================*/ +static int ConnectAsync(pSICSOBJ self, SConnection *pCon, + char *argv[], int argc){ + pGenController priv = NULL; + pAsyncQueue assi = NULL; + char buffer[2048]; + pAsyncUnit uni = NULL; + + priv = (pGenController)self->pPrivate; + assert(priv != NULL); + + if(argc < 4){ + snprintf(buffer,2048,"ERROR: insufficient arguments to %s asynconnect", + argv[0]); + SCWrite(pCon,buffer, eError); + return 0; + } + + assi = (pAsyncQueue)FindCommandData(pServ->pSics, argv[3],"AsyncQueue"); + if(assi == NULL){ + snprintf(buffer,2048,"ERROR: %s not found or no AsyncQueue", argv[3]); + SCWrite(pCon,buffer,eError); + return 0; + } + + priv->comContext = assi; + priv->killComContext = NULL; /* not ours, cleaned up by AsyncQueue module */ + priv->enqueueNode = AsyncEnqueueNode; + priv->enqueueNodeHead = AsyncEnqueueNodeHead; + priv->replyCallback = AsyncReply; + priv->comError = GCOK; + + /* + * This unit is solely for the purpose of receiving event notifications + */ + uni = AsyncUnitFromQueue(assi); + + SCSendOK(pCon); + return 1; +} +/*---------------------------------------------------------------------------*/ +int GenControllerConfigure(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]){ + pSICSOBJ controller = NULL; + pGenController self = NULL; + char buffer[2048]; + + if(argc < 3){ + snprintf(buffer,2048,"ERROR: insufficient arguments to %s", argv[0]); + SCWrite(pCon,buffer,eError); + return 0; + } + + controller = (pSICSOBJ)FindCommandData(pSics,argv[2], "GenericController"); + if(controller == NULL){ + snprintf(buffer,2048,"ERROR: controller %s not found", argv[2]); + SCWrite(pCon,buffer,eError); + return 0; + } + + strtolower(argv[1]); + if(strcmp(argv[1],"makepar") == 0){ + return MakeGenPar(controller,pCon,argv,argc); + } else if(strcmp(argv[1],"asynconnect") == 0){ + return ConnectAsync(controller, pCon, argv, argc); + } else { + SCWrite(pCon,"ERROR: argument to GenControllerConfigure not found", + eError); + return 0; + } + + return 1; +} +/*---------------------------------------------------------------------------*/ +static void killGeneric(void *data){ + pGenController self = (pGenController)data; + + if(self == NULL){ + return; + } + if(self->comContext != NULL && self->killComContext != NULL){ + self->killComContext(self->comContext); + } + free(self); +} +/*---------------------------------------------------------------------------*/ +static int EnqueFunc(pSICSOBJ self, SConnection *pCon, pHdb commandNode, + pHdb par[], int nPar){ + pGenController priv = NULL; + pHdb node = NULL; + char buffer[512]; + + priv = (pGenController)self->pPrivate; + assert(priv != NULL); + assert(nPar >= 1); + + node = GetHipadabaNode(self->objectNode,par[0]->value.v.text); + if(node == NULL){ + snprintf(buffer,511,"ERROR: node %s to enqueue not found", + par[0]->value.v.text); + SCWrite(pCon,buffer,eError); + return 0; + } + + if(priv->enqueueNode != NULL){ + priv->enqueueNode(self, pCon, node); + } else { + SCWrite(pCon,"ERROR: GenController NOT configured",eError); + return 0; + } + return 1; +} +/*---------------------------------------------------------------------------*/ +static int EnqueHeadFunc(pSICSOBJ self, SConnection *pCon, Hdb commandNode, + pHdb par[], int nPar){ + pGenController priv = NULL; + pHdb node = NULL; + char buffer[512]; + + priv = (pGenController)self->pPrivate; + assert(priv != NULL); + assert(nPar >= 1); + + node = GetHipadabaNode(self->objectNode,par[0]->value.v.text); + if(node == NULL){ + snprintf(buffer,511,"ERROR: node %s to enqueue not found", + par[0]->value.v.text); + SCWrite(pCon,buffer,eError); + return 0; + } + + if(priv->enqueueNodeHead != NULL){ + priv->enqueueNodeHead(self, pCon, node); + } else { + SCWrite(pCon,"ERROR: GenController NOT configured",eError); + return 0; + } + return 1; +} +/*---------------------------------------------------------------------------*/ +int GenControllerFactory(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]){ + pSICSOBJ pNew = NULL; + pGenController priv = NULL; + hdbValue funcValue, textValue; + pHdb node = NULL; + char line[132]; + int status; + + if(argc < 2){ + SCWrite(pCon, + "ERROR: insufficient number of arguments to GenControllerFactrory", + eError); + return 0; + } + priv = malloc(sizeof(GenController)); + if(priv == NULL){ + SCWrite(pCon,"ERROR: out of memory in GenControllerFactory", + eError); + return 0; + } + memset(priv,0,sizeof(GenController)); + + pNew = MakeSICSOBJ(argv[1],"GenericController"); + if(pNew == NULL){ + SCWrite(pCon,"ERROR: out of memory in GenControllerFactory", + eError); + return 0; + } + pNew->pPrivate = priv; + pNew->KillPrivate = killGeneric; + + textValue = MakeHdbText("Undefined"); + funcValue = MakeHdbFunc((voidFunc *)EnqueFunc); + node = MakeSICSHdbPar("enqueue",usUser, funcValue); + AddSICSHdbPar(node,"node",usUser,textValue); + AppendHipadabaCallback(node,MakeSICSFuncCallback(pNew)); + AddHipadabaChild(pNew->objectNode,node,NULL); + + funcValue = MakeHdbFunc((voidFunc *)EnqueHeadFunc); + node = MakeSICSHdbPar("enqueuehead",usUser, funcValue); + AddSICSHdbPar(node,"node",usUser,textValue); + AppendHipadabaCallback(node,MakeSICSFuncCallback(pNew)); + AddHipadabaChild(pNew->objectNode,node,NULL); + + status = AddCommand(pSics, + argv[1], + InvokeSICSOBJ, + KillSICSOBJ, + pNew); + if(status != 1){ + KillSICSOBJ(pNew); + SCPrintf(pCon,eError,"ERROR: failed create duplicate command %s", argv[1]); + return 0; + } + SCSendOK(pCon); + + return 1; +} + diff --git a/genericcontroller.h b/genericcontroller.h new file mode 100644 index 00000000..2ba805a7 --- /dev/null +++ b/genericcontroller.h @@ -0,0 +1,37 @@ +/** + * This is a generic controller for devices in SICS. In its default configuration it + * will be configurable via Tcl scripts and used AsynqQueue for communication. But + * it is suitably generic to support other mechanisms as well. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, November 2007 + */ +#ifndef GENERICCONTROLLER_H_ +#define GENERICCONTROLLER_H_ +#include +#include + +#define GCTIMEOUT 5001 +#define GCDISCONNECT 5002 +#define GCOK 5000 +#define GCRECONNECT 5003 +#define GCRETRY 5004 + +typedef struct { + int (*enqueueNode)(pSICSOBJ self, SConnection *pCon, pHdb node); + int (*enqueueNodeHead)(pSICSOBJ self, SConnection *pCon, pHdb node); + int (*replyCallback)(pSICSOBJ self, SConnection *pCon, pHdb node, + char *replyCommand, char *reply, int replylen); + void *comContext; + void (*killComContext)(void *data); + int comError; +}GenController, *pGenController; +/*---------------------------------------------------------------------------*/ +int GenControllerFactory(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]); +/*---------------------------------------------------------------------------*/ +int GenControllerConfigure(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]); + +#endif /*GENERICCONTROLLER_H_*/ diff --git a/geninter.c b/geninter.c new file mode 100644 index 00000000..0567494b --- /dev/null +++ b/geninter.c @@ -0,0 +1,19 @@ +/** + * This is the implementation of various SICS internal interfaces based on + * parameters in a generic controller. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, November 2007 + */ +#include +#include +#include +#include + +/*---------------------------------------------------------------------------*/ +int GenDrivableFactory(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]){ + + return 1; +} diff --git a/geninter.h b/geninter.h new file mode 100644 index 00000000..05a469cd --- /dev/null +++ b/geninter.h @@ -0,0 +1,19 @@ +/** + * This is the implementation of various SICS internal interfaces based on + * parameters in a generic controller. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, November 2007 + */ +#ifndef GENINTER_H_ +#define GENINTER_H_ +/** + * make a drivable parameter: + * Usage: + * MakeGenDrivable name par-node control-node + */ +int GenDrivableFactory(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]); + +#endif /*GENINTER_H_*/ diff --git a/hdbqueue.c b/hdbqueue.c new file mode 100644 index 00000000..88757825 --- /dev/null +++ b/hdbqueue.c @@ -0,0 +1,497 @@ +/** + * This is the new Hipadaba based queuing system in support of the MountainGum + * user interface. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, July 2007 + */ +#include +#include +#include +#include "sicsobj.h" +#include "hdbqueue.h" +#include "sicshipadaba.h" +#include "dynstring.h" +#include "exeman.h" +#include "macro.h" +/*--------------------------------------------------------------------------*/ +typedef struct { + int iStop; + int isRunning; + SConnection *pCon; +}HdbQueue, *pHdbQueue; +/*--------------------------------------------------------------------------*/ +static pHdbCallback CopyCallbackChain(pHdbCallback source){ + pHdbCallback current = NULL; + pHdbCallback result = NULL; + pHdbCallback head = NULL; + pHdbCallback tail = NULL; + + current = source; + while(current != NULL){ + result = MakeHipadabaCallback(current->userCallback, + current->userData, + NULL, + current->id, + current->internalID); + if(head == NULL){ + head = result; + tail = result; + } else { + tail->next = result; + result->previous = tail; + tail = result; + } + current = current->next; + } + return head; +} +/*---------------------------------------------------------------------------*/ +static pHdb MakeNewEntry(char *name, pHdbCallback update){ + pHdb entry = NULL, child = NULL; + hdbValue v; + + v = MakeHdbText("Undefined"); + entry = MakeHipadabaNode(name,HIPNONE, 1); + entry->updateCallbacks = CopyCallbackChain(update); + child = MakeSICSHdbPar("description",usUser,v); + child->updateCallbacks = CopyCallbackChain(update); + AddHipadabaChild(entry,child,NULL); + child = MakeSICSHdbPar("commands",usUser,v); + child->updateCallbacks = CopyCallbackChain(update); + AddHipadabaChild(entry,child,NULL); + child = MakeSICSHdbPar("log",usUser,v); + child->updateCallbacks = CopyCallbackChain(update); + AddHipadabaChild(entry,child,NULL); + + return entry; +} +/*---------------------------------------------------------------------------*/ +static int EnqueFunc(pSICSOBJ self, SConnection *pCon, Hdb commandNode, + pHdb par[], int nPar){ + pHdb entry = NULL; + pHdb work = NULL; + char name[80]; + hdbValue v; + + if(nPar < 1){ + SCWrite(pCon,"ERROR: internal: not enough parameters to EnqueFunc",eError); + return 0; + } + + /* + * new entry + */ + memset(&v,0,sizeof(hdbValue)); + work = GetHipadabaNode(self->objectNode,"control/maxEntry"); + assert(work != NULL); + snprintf(name,80,"%3.3d", work->value.v.intValue); + entry = MakeNewEntry(name, work->updateCallbacks); + if(entry == NULL){ + SCWrite(pCon,"ERROR: out of memory in EnqueFunc",eError); + return 0; + } + /* + * Update maxEntry + */ + cloneHdbValue(&work->value,&v); + v.v.intValue++; + UpdateHipadabaPar(work,v,pCon); + + work = GetHipadabaNode(self->objectNode,"queue"); + assert(work != NULL); + AddHipadabaChild(work, entry, pCon); + + /* + * save description + */ + work = GetHipadabaNode(entry,"description"); + assert(work != NULL); + UpdateHipadabaPar(work,par[0]->value,pCon); + + + return 1; +} +/*---------------------------------------------------------------------------*/ +static int AddCmdData(pSICSOBJ self, SConnection *pCon, Hdb comNode, + pHdb par[], int nPar){ + pHdb work = NULL; + pHdb commandNode = NULL; + char name[80]; + pDynString txt = NULL; + + if(nPar < 1){ + SCWrite(pCon,"ERROR: internal: not enough parameters to AddCmdData",eError); + return 0; + } + + work = GetHipadabaNode(self->objectNode,"control/maxEntry"); + assert(work != NULL); + snprintf(name,80,"queue/%3.3d/commands", work->value.v.intValue-1); + commandNode = GetHipadabaNode(self->objectNode,name); + if(commandNode == NULL){ + SCWrite(pCon,"ERROR: Internal error in AddCommand",eError); + return 0; + } + txt = CreateDynString(80,80); + if(strstr(commandNode->value.v.text,"Undefined") == NULL){ + DynStringCopy(txt,commandNode->value.v.text); + } + DynStringConcat(txt,par[0]->value.v.text); + DynStringConcat(txt,"\n"); + free(commandNode->value.v.text); + commandNode->value.v.text = strdup(GetCharArray(txt)); + NotifyHipadabaPar(commandNode,pCon); + DeleteDynString(txt); + return 1; +} +/*--------------------------------------------------------------------------*/ +static void sequentialNames(pHdb obj,SConnection *pCon){ + pHdb work = NULL; + pHdb current = NULL; + int count = 0; + char name[80]; + + work = GetHipadabaNode(obj,"queue"); + assert(work != NULL); + current = work->child; + while(current != NULL){ + snprintf(name,80,"%3.3d",count); + if(current->name != NULL){ + free(current->name); + } + current->name = strdup(name); + count++; + current = current->next; + } + InvokeCallbackChain(work->treeChangeCallbacks,work,pCon,work->value); + + work = GetHipadabaNode(obj,"control/maxEntry"); + assert(work != NULL); + work->value.v.intValue = count; + NotifyHipadabaPar(work,pCon); +} +/*---------------------------------------------------------------------------*/ +static int Dequeue(pSICSOBJ self, SConnection *pCon, pHdb commandNode, + pHdb par[], int nPar){ + pHdb work = NULL; + char name[80]; + pHdbQueue priv = (pHdbQueue)self->pPrivate; + + if(priv->isRunning == 1){ + SCWrite(pCon,"ERROR: cannot dequeue while running",eError); + return 0; + } + + + if(nPar < 1){ + SCWrite(pCon,"ERROR: internal: not enough parameters to Dequeue",eError); + return 0; + } + + snprintf(name,80,"queue/%3.3d", par[0]->value.v.intValue); + work = GetHipadabaNode(self->objectNode,name); + if(work != NULL){ + DeleteHipadabaNode(work,pCon); + sequentialNames(self->objectNode, pCon); + return 1; + } + + return 0; +} +/*---------------------------------------------------------------------------*/ +static int Clean(pSICSOBJ self, SConnection *pCon,Hdb commandNode, + pHdb par[], int nPar){ + int i; + pHdb current = NULL, queue = NULL; + pHdb currentEntry = NULL, tmp = NULL; + pHdbQueue priv = (pHdbQueue)self->pPrivate; + + if(priv->isRunning == 1){ + SCWrite(pCon,"ERROR: cannot clean while running",eError); + return 0; + } + currentEntry = GetHipadabaNode(self->objectNode,"control/currentEntry"); + queue = GetHipadabaNode(self->objectNode,"queue"); + current = queue->child; + for(i = 0; i < currentEntry->value.v.intValue; i++){ + if(current != NULL){ + tmp = current->next; + DeleteNodeData(current); + current = tmp; + } + } + queue->child = tmp; + currentEntry->value.v.intValue = 0; + sequentialNames(self->objectNode, pCon); + NotifyHipadabaPar(currentEntry,pCon); + return 1; +} +/*---------------------------------------------------------------------------*/ +static int CleanAll(pSICSOBJ self, SConnection *pCon, pHdb commandNode, + pHdb par[], int nPar){ + int i; + pHdb current = NULL, queue = NULL; + pHdb currentEntry = NULL, tmp; + pHdbQueue priv = (pHdbQueue)self->pPrivate; + + if(priv->isRunning == 1){ + SCWrite(pCon,"ERROR: cannot clear queue while executing",eError); + return 0; + } + currentEntry = GetHipadabaNode(self->objectNode,"control/currentEntry"); + queue = GetHipadabaNode(self->objectNode,"queue"); + current = queue->child; + while(current != NULL){ + tmp = current->next; + DeleteNodeData(current); + current = tmp; + } + queue->child = NULL; + currentEntry->value.v.intValue = 0; + sequentialNames(self->objectNode, pCon); + NotifyHipadabaPar(currentEntry,pCon); + return 1; +} +/*----------------------------------------------------------------------------*/ +static int QueueTask(void *pData){ + pSICSOBJ self = (pSICSOBJ)pData; + int status, pos; + pHdb work = NULL; + pHdb exeNode = NULL; + pHdb max = NULL; + char name[80]; + pHdbQueue priv = (pHdbQueue)self->pPrivate; + + if(priv->iStop == 1){ + priv->isRunning = 0; + return 0; + } + + work = GetHipadabaNode(self->objectNode,"control/currentEntry"); + max = GetHipadabaNode(self->objectNode,"control/maxEntry"); + assert(work != NULL && max != NULL); + pos = work->value.v.intValue; + snprintf(name,80,"queue/%3.3d", pos); + + exeNode = GetHipadabaNode(self->objectNode,name); + if(exeNode != NULL){ + MacroPush(priv->pCon); + exeHdbNode(exeNode, priv->pCon); + MacroPop(); + } + if(priv->iStop == 1 || SCGetInterrupt(priv->pCon) != eContinue){ + priv->isRunning = 0; + return 0; + } + + pos++; + work->value.v.intValue = pos; + NotifyHipadabaPar(work,priv->pCon); + if(pos >= max->value.v.intValue){ + priv->isRunning = 0; + return 0; + } + + return 1; +} +/*---------------------------------------------------------------------------*/ +static int Start(pSICSOBJ self, SConnection *pCon, pHdb commandNode, + pHdb par[], int nPar){ + pHdbQueue priv = (pHdbQueue)self->pPrivate; + + priv->iStop = 0; + priv->pCon = pCon; + + if(priv->isRunning == 1){ + SCWrite(pCon,"ERROR: Hdbqueue is already running",eError); + return 0; + } + priv->isRunning = 1; + + TaskRegister(pServ->pTasker, + QueueTask, + NULL, + NULL, + self, + 10); + return 1; +} +/*---------------------------------------------------------------------------*/ +static int Restart(pSICSOBJ self, SConnection *pCon, pHdb commandNode, + pHdb par[], int nPar){ + pHdbQueue priv = (pHdbQueue)self->pPrivate; + pHdb maxCurrent = NULL; + + maxCurrent = GetHipadabaNode(self->objectNode,"control/currentEntry"); + if(maxCurrent != NULL){ + maxCurrent->value.v.intValue = 0; + NotifyHipadabaPar(maxCurrent,pCon); + } + + return Start(self,pCon,commandNode,par,nPar); +} +/*---------------------------------------------------------------------------*/ +static int Stop(pSICSOBJ self, SConnection *pCon, pHdb commandNode, + pHdb par[], int nPar){ + pHdbQueue priv = (pHdbQueue)self->pPrivate; + + priv->iStop = 1; + return 1; +} +/*----------------------------------------------------------------------------*/ +static int Move(pSICSOBJ self, SConnection *pCon, pHdb commandNode, + pHdb par[], int nPar){ + pHdb moveNode = NULL; + pHdb insertNode = NULL; + pHdb prevNode = NULL, queueNode = NULL; + pHdb tmp; + char name[80]; + pHdbQueue priv = (pHdbQueue)self->pPrivate; + + if(priv->isRunning == 1){ + SCWrite(pCon,"ERROR: cannot move while running",eError); + return 0; + } + + if(nPar < 2){ + SCWrite(pCon,"ERROR: internal: not enough parameters to Move",eError); + return 1; + } + + if(par[1]->value.v.intValue == par[0]->value.v.intValue + 1){ + /* + * already in right sequence, nothing to do + */ + return 1; + } + + snprintf(name,80,"queue/%3.3d", par[1]->value.v.intValue); + moveNode = GetHipadabaNode(self->objectNode,name); + + snprintf(name,80,"queue/%3.3d", par[0]->value.v.intValue); + insertNode = GetHipadabaNode(self->objectNode,name); + if(moveNode == NULL || insertNode == NULL){ + SCWrite(pCon,"ERROR: move not possible, participating nodes not found", + eError); + return 0; + } + queueNode = GetHipadabaNode(self->objectNode,"queue"); + + if(moveNode == queueNode->child){ + queueNode->child = queueNode->child->next; + moveNode->next = insertNode->next; + insertNode->next = moveNode; + } else { + prevNode = queueNode->child; + while(prevNode != NULL && prevNode->next != moveNode){ + prevNode = prevNode->next; + } + if(insertNode == queueNode->child ){ + /* + * insert at top + */ + tmp = queueNode->child; + queueNode->child = moveNode; + prevNode->next = moveNode->next; + moveNode->next = tmp; + } else { + tmp = insertNode->next; + insertNode->next = moveNode; + prevNode->next = moveNode->next; + moveNode->next = tmp; + } + } + sequentialNames(self->objectNode, pCon); + + return 1; +} +/*---------------------------------------------------------------------------*/ +static void Configure(pSICSOBJ self){ + pHdb n = NULL, par = NULL; + hdbValue intValue, textValue, funcValue; + pHdb obj = self->objectNode; + + intValue = MakeHdbInt(0); + textValue = MakeHdbText("Undefined"); + + n = MakeHipadabaNode("control",HIPNONE,1); + AddHipadabaChild(obj,n,NULL); + AddSICSHdbPar(n,"maxEntry",usInternal,intValue); + AddSICSHdbPar(n,"currentEntry",usInternal,intValue); + + + n = MakeHipadabaNode("queue",HIPNONE,1); + AddHipadabaChild(obj,n, NULL); + + funcValue = MakeSICSFunc(EnqueFunc); + n = MakeSICSHdbPar("enqueue",usUser, funcValue); + AddSICSHdbPar(n,"description",usUser,textValue); + AddHipadabaChild(obj,n,NULL); + AppendHipadabaCallback(n,HCBSET,MakeSICSFuncCallback(self)); + + funcValue = MakeSICSFunc(AddCmdData); + n = MakeSICSHdbPar("addcommand",usUser, funcValue); + AddSICSHdbPar(n,"command",usUser,textValue); + AddHipadabaChild(obj,n,NULL); + AppendHipadabaCallback(n,HCBSET,MakeSICSFuncCallback(self)); + + + funcValue = MakeSICSFunc(Dequeue); + n = MakeSICSHdbPar("dequeue",usUser,funcValue); + AddHipadabaChild(obj,n,NULL); + AddSICSHdbPar(n,"index",usUser,intValue); + AppendHipadabaCallback(n,HCBSET,MakeSICSFuncCallback(self)); + + + funcValue = MakeSICSFunc(Clean); + n = MakeSICSHdbPar("clean",usUser, funcValue); + AddHipadabaChild(obj,n,NULL); + AppendHipadabaCallback(n,HCBSET,MakeSICSFuncCallback(self)); + + funcValue = MakeSICSFunc(CleanAll); + n = MakeSICSHdbPar("cleanall",usUser, funcValue); + AddHipadabaChild(obj,n,NULL); + AppendHipadabaCallback(n,HCBSET,MakeSICSFuncCallback(self)); + + funcValue = MakeSICSFunc(Start); + n = MakeSICSHdbPar("start",usUser, funcValue); + AddHipadabaChild(obj,n,NULL); + AppendHipadabaCallback(n,HCBSET,MakeSICSFuncCallback(self)); + + funcValue = MakeSICSFunc(Restart); + n = MakeSICSHdbPar("restart",usUser, funcValue); + AddHipadabaChild(obj,n,NULL); + AppendHipadabaCallback(n,HCBSET,MakeSICSFuncCallback(self)); + + funcValue = MakeSICSFunc(Stop); + n = MakeSICSHdbPar("stop",usUser, funcValue); + AddHipadabaChild(obj,n,NULL); + AppendHipadabaCallback(n,HCBSET,MakeSICSFuncCallback(self)); + + funcValue = MakeSICSFunc(Move); + n = MakeSICSHdbPar("move",usUser,funcValue); + AddHipadabaChild(obj,n,NULL); + AddSICSHdbPar(n,"moveindex",usUser,intValue); + AddSICSHdbPar(n,"insertindex",usUser,intValue); + AppendHipadabaCallback(n,HCBSET,MakeSICSFuncCallback(self)); + +} +/*---------------------------------------------------------------------------*/ +int MakeHDBQueue(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pSICSOBJ self = NULL; + pHdbQueue priv = NULL; + + priv = (pHdbQueue)malloc(sizeof(HdbQueue)); + self = SetupSICSOBJ(pCon,pSics, pData,argc, argv); + if(self == NULL || priv == NULL){ + return 0; + } + Configure(self); + memset(priv,0,sizeof(HdbQueue)); + self->pPrivate = priv; + self->KillPrivate = free; + return 1; +} diff --git a/hdbqueue.h b/hdbqueue.h new file mode 100644 index 00000000..a1663da8 --- /dev/null +++ b/hdbqueue.h @@ -0,0 +1,15 @@ +/** + * This is the new Hipadab based queuing system in support of the MountainGum + * user interface. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, July 2007 + */ +#ifndef HDBQUEUE_H_ +#define HDBQUEUE_H_ + +int MakeHDBQueue(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + +#endif /*HDBQUEUE_H_*/ diff --git a/hipadaba.c b/hipadaba.c new file mode 100644 index 00000000..087fadc7 --- /dev/null +++ b/hipadaba.c @@ -0,0 +1,956 @@ +/*------------------------------------------------------------------------- + The hierarchical parameter database code. For more information, see + hipadaba.h + + copyright: GPL + + Mark Koennecke, June 2006 +---------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include "hipadaba.h" + +#define ABS(x) (x < 0 ? -(x) : (x)) +#define HDBMAGICK 77119900 +/*================== Message Stuff ========================================*/ +static char set[] = {"set"}; +static char get[] = {"get"}; +static char update[] = {"update"}; +static char treeChange[] = {"treeChange"}; +static char dataSearch[] = {"dataSearch"}; +static char killNode[] = {"killNode"}; +/*------------------------------------------------------------------------*/ +pHdbDataMessage GetHdbSetMessage(pHdbMessage toTest){ + if(toTest->type == set){ + return (pHdbDataMessage)toTest; + } + return NULL; +} +/*------------------------------------------------------------------------*/ +pHdbDataMessage GetHdbGetMessage(pHdbMessage toTest){ + if(toTest->type == get){ + return (pHdbDataMessage)toTest; + } + return NULL; +} +/*------------------------------------------------------------------------*/ +pHdbDataMessage GetHdbUpdateMessage(pHdbMessage toTest){ + if(toTest->type == update){ + return (pHdbDataMessage)toTest; + } + return NULL; +} +/*-------------------------------------------------------------------------*/ +pHdbTreeChangeMessage GetHdbTreeChangeMessage(pHdbMessage toTest){ + if(toTest->type == treeChange){ + return (pHdbTreeChangeMessage)toTest; + } + return NULL; +} +/*-------------------------------------------------------------------------*/ +pHdbDataSearch GetHdbDataSearchMessage(pHdbMessage toTest){ + if(toTest->type == dataSearch){ + return (pHdbDataSearch)toTest; + } + return NULL; +} +/*-------------------------------------------------------------------------*/ +pHdbMessage GetHdbKillNodeMessage(pHdbMessage toTest){ + if(toTest->type == killNode){ + return toTest; + } + return NULL; +} +/*================== internal functions ===================================*/ +void DeleteCallbackChain(pHdb node){ + pHdbCallback current = NULL, thisEntry; + hdbMessage killNodeMsg; + + killNodeMsg.type = killNode; + InvokeCallbackChain(node, &killNodeMsg); + + current = node->callBackChain; + while(current != NULL){ + if(current->killFunc != NULL){ + current->killFunc(current->userData); + } + thisEntry = current; + current = (pHdbCallback)current->next; + free(thisEntry); + } +} +/*----------------------------------------------------------------------*/ +void RecurseCallbackChains(pHdb node, pHdbMessage message){ + pHdb current = NULL; + + InvokeCallbackChain(node,message); + current = node->child; + while(current != NULL){ + RecurseCallbackChains(current,message); + current = current->next; + } +} +/*-----------------------------------------------------------------------*/ +void DeleteNodeData(pHdb node){ + pHdb tmp = NULL; + + if(node == NULL){ + return; + } + + DeleteCallbackChain(node); + if(node->properties != NULL){ + DeleteStringDict(node->properties); + } + + if(node->name != NULL){ + free(node->name); + } + ReleaseHdbValue(&node->value); + node->magic = 000000; + + while(node->child != NULL){ + tmp = node->child; + node->child = node->child->next; + DeleteNodeData(tmp); + } + free(node); +} +/*------------------------------------------------------------------------*/ +static pHdbCallback CleanCallbackChain(pHdbCallback head){ + pHdbCallback current = head; + pHdbCallback next; + pHdbCallback *ptr2last = &head; + + while(current != NULL){ + if(current->killFlag == 1){ + next = current->next; + /* + * unlink + */ + *ptr2last = next; + /* + * delete + */ + if(current->killFunc != NULL){ + current->killFunc(current->userData); + } + free(current); + /* + * move on + */ + current = next; + } else { + ptr2last = ¤t->next; + current = current->next; + } + } + + return head; +} +/*-------------------------------------------------------------------------*/ +int InvokeCallbackChain(pHdb node, pHdbMessage message){ + pHdbCallback current = node->callBackChain; + hdbCallbackReturn status; + int killFlag = 0; + + while(current != NULL){ + status = current->userCallback(node, current->userData,message); + switch(status){ + case hdbAbort: + return 0; + break; + case hdbKill: + current->killFlag = 1; + killFlag = 1; + break; + case hdbContinue: + break; + } + current = current->next; + } + if(killFlag == 1){ + node->callBackChain = CleanCallbackChain(node->callBackChain); + } + return 1; +} +/*-----------------------------------------------------------------------*/ +static void SendTreeChangeMessage(pHdb node, void *callData){ + hdbTreeChangeMessage treeChangeMes; + treeChangeMes.type = treeChange; + treeChangeMes.callData = callData; + InvokeCallbackChain(node, (pHdbMessage)&treeChangeMes); +} +/*------------------------------------------------------------------------*/ +void RemoveHdbNodeFromParent(pHdb node, void *callData){ + pHdb parent = NULL; + pHdb current = NULL; + hdbTreeChangeMessage treeChangeMes; + + parent = node->mama; + if(parent != NULL){ + if(parent->child == node){ + parent->child = node->next; + } else { + current = parent->child; + while(current->next != node){ + current = current->next; + } + current->next = current->next->next; + } + SendTreeChangeMessage(parent,callData); + node->mama = NULL; + } +} +/*----------------------------------------------------------------------*/ +int CountHdbChildren(pHdb node){ + int count = 0; + pHdb current = NULL; + current = node->child; + while(current != NULL){ + current = current->next; + count++; + } + return count; +} +/*----------------------------------------------------------------------*/ +char *hdbTrim(char *str) +{ + char *ibuf = str, *obuf = str; + int i = 0, cnt = 0; + + /* + ** Trap NULL + */ + + if (str) + { + /* + ** Remove leading spaces (from RMLEAD.C) + */ + + for (ibuf = str; *ibuf && isspace(*ibuf); ++ibuf) + ; + if (str != ibuf) + memmove(str, ibuf, ibuf - str); + + /* + ** Collapse embedded spaces (from LV1WS.C) + */ + + while (*ibuf) + { + if (isspace(*ibuf) && cnt) + ibuf++; + else + { + if (!isspace(*ibuf)) + cnt = 0; + else + { + *ibuf = ' '; + cnt = 1; + } + obuf[i++] = *ibuf++; + } + } + obuf[i] = '\0'; + + /* + ** Remove trailing spaces (from RMTRAIL.C) + */ + + while (--i >= 0) + { + if (!isspace(obuf[i])) + break; + } + obuf[++i] = '\0'; + } + return str; +} +/*------------------------------------------------------------------------*/ +static pHdb locateChild(pHdb root, char *name){ + pHdb current = NULL; + + current = root->child; + while(current != NULL){ + if(strcmp(current->name,name) == 0){ + return current; + } + current = current->next; + } + return NULL; +} +/*================= data functions ========================================*/ +hdbValue makeHdbValue(int datatype, int length){ + hdbValue val; + + memset(&val,0,sizeof(hdbValue)); + val.dataType = datatype; + val.doNotFree = 0; + + switch(datatype){ + case HIPINTAR: + case HIPINTVARAR: + val.arrayLength = length; + val.v.intArray = malloc(length*sizeof(int)); + if(val.v.intArray != NULL){ + memset(val.v.intArray,0,length*sizeof(int)); + } + break; + case HIPFLOATAR: + case HIPFLOATVARAR: + val.arrayLength = length; + val.v.floatArray = malloc(length*sizeof(double)); + if(val.v.floatArray != NULL){ + memset(val.v.floatArray,0,length*sizeof(double)); + } + break; + case HIPTEXT: + val.v.text = strdup("UNKNOWN"); + val.arrayLength = length; + break; + } + return val; +} +/*-------------------------------------------------------------------------*/ +hdbValue MakeHdbInt(int initValue){ + hdbValue result; + + result.dataType = HIPINT; + result.arrayLength = 1; + result.v.intValue = initValue; + return result; +} +/*-------------------------------------------------------------------------*/ +hdbValue MakeHdbFloat(double initValue){ + hdbValue result; + + result.dataType = HIPFLOAT; + result.arrayLength = 1; + result.v.doubleValue = initValue; + return result; +} +/*-------------------------------------------------------------------------*/ +hdbValue MakeHdbText(char *initText){ + hdbValue result; + + result.dataType = HIPTEXT; + result.v.text = initText; /* no strdup here ! */ + result.arrayLength = strlen(initText); + return result; +} +/*-------------------------------------------------------------------------*/ +hdbValue MakeHdbIntArray(int length, int *data){ + hdbValue result; + + result.dataType = HIPINTAR; + result.arrayLength = length; + result.v.intArray = data; + return result; +} +/*-------------------------------------------------------------------------*/ +hdbValue MakeHdbFloatArray(int length, double *data){ + hdbValue result; + + result.dataType = HIPFLOATAR; + result.arrayLength = length; + result.v.floatArray = data; + return result; +} +/*-------------------------------------------------------------------------*/ +hdbValue MakeHdbFunc(voidFunc *func){ + hdbValue result; + + result.dataType = HIPFUNC; + result.v.func = func; + return result; +} +/*-------------------------------------------------------------------------*/ +hdbValue MakeHdbObj(void *obj){ + hdbValue result; + + result.dataType = HIPOBJ; + result.v.obj = obj; + return result; +} +/*-------------------------------------------------------------------------*/ +void ReleaseHdbValue(hdbValue *v){ + + if(v->doNotFree == 1){ + return; + } + switch(v->dataType){ + case HIPTEXT: + if(v->v.text != NULL){ + free(v->v.text); + } + break; + case HIPINTAR: + case HIPINTVARAR: + if(v->v.intArray != NULL){ + free(v->v.intArray); + } + break; + case HIPFLOATAR: + case HIPFLOATVARAR: + if(v->v.floatArray != NULL){ + free(v->v.floatArray); + } + break; + } +} +/*------------------------------------------------------------------------*/ +int compareHdbValue(hdbValue v1, hdbValue v2){ + int i; + + if(v1.dataType != v2.dataType){ + return 0; + } + switch(v1.dataType){ + case HIPNONE: + return 0; + break; + case HIPINT: + if(v1.v.intValue == v2.v.intValue){ + return 1; + } else { + return 0; + } + break; + case HIPFLOAT: + if(ABS(v1.v.doubleValue - v2.v.doubleValue) < .01){ + return 1; + } else { + return 0; + } + break; + case HIPTEXT: + if(v1.v.text == NULL || v2.v.text == NULL){ + return 0; + } + if(strcmp(v1.v.text,v2.v.text) == 0){ + return 1; + } else { + return 0; + } + break; + case HIPINTAR: + case HIPINTVARAR: + if(v1.arrayLength != v2.arrayLength){ + return 0; + } + if(v1.v.intArray == NULL || v2.v.intArray == NULL){ + return 0; + } + for(i = 0; i < v1.arrayLength; i++){ + if(v1.v.intArray[i] != v2.v.intArray[i]){ + return 0; + } + } + return 1; + break; + case HIPFLOATAR: + case HIPFLOATVARAR: + if(v1.arrayLength != v2.arrayLength){ + return 0; + } + if(v1.v.floatArray == NULL || v2.v.floatArray == NULL){ + return 0; + } + for(i = 0; i < v1.arrayLength; i++){ + if(ABS(v1.v.floatArray[i] - v2.v.floatArray[i]) > .01){ + return 0; + } + } + return 1; + break; + case HIPOBJ: + if(v2.v.obj == v1.v.obj) { + return 1; + } else { + return 0; + } + break; + case HIPFUNC: + if(v2.v.func == v1.v.func) { + return 1; + } else { + return 0; + } + break; + default: + assert(0); + break; + } + return 0; +} +/*-------------------------------------------------------------------------*/ +int cloneHdbValue(hdbValue *source, hdbValue *clone){ + + memset(clone,0,sizeof(hdbValue)); + clone->v.text = NULL; /* this sets all pointers in the union to NULL */ + clone->dataType = source->dataType; + return copyHdbValue(source, clone); +} +/*-------------------------------------------------------------------------*/ +int getHdbValueLength(hdbValue v){ + int length = 0; + switch(v.dataType){ + case HIPNONE: + break; + case HIPINT: + length = sizeof(int); + break; + case HIPFLOAT: + length = sizeof(double); + break; + case HIPINTAR: + case HIPINTVARAR: + length = v.arrayLength * sizeof(int); + break; + case HIPFLOATAR: + case HIPFLOATVARAR: + length = v.arrayLength * sizeof(double); + break; + case HIPTEXT: + length = strlen(v.v.text); + break; + case HIPOBJ: + length = sizeof(void *); + break; + case HIPFUNC: + length = sizeof(voidFunc *); + break; + } + return length; +} +/*================= node functions ========================================*/ +pHdb MakeHipadabaNode(char *name, int datatype, int length){ + pHdb pNew = NULL; + + pNew = malloc(sizeof(Hdb)); + if(pNew == NULL){ + return NULL; + } + memset(pNew,0,sizeof(Hdb)); + pNew->magic = HDBMAGICK; + pNew->name = strdup(name); + pNew->value.dataType = datatype; + pNew->properties = CreateStringDict(); + if(pNew->properties == NULL || pNew->name == NULL){ + return NULL; + } + switch(datatype){ + case HIPINTAR: + case HIPINTVARAR: + pNew->value.arrayLength = length; + pNew->value.v.intArray = malloc(length*sizeof(int)); + if(pNew->value.v.intArray == NULL){ + return NULL; + } + memset(pNew->value.v.intArray,0,length*sizeof(int)); + break; + case HIPFLOATAR: + case HIPFLOATVARAR: + pNew->value.arrayLength = length; + pNew->value.v.floatArray = malloc(length*sizeof(double)); + if(pNew->value.v.floatArray == NULL){ + return NULL; + } + memset(pNew->value.v.floatArray,0,length*sizeof(double)); + break; + case HIPTEXT: + pNew->value.arrayLength = length; + pNew->value.v.text = strdup("UNKNOWN"); + break; + } + return pNew; +} +/*-------------------------------------------------------------------------*/ +void AddHipadabaChild(pHdb parent, pHdb child, void *callData){ + pHdb current = NULL, prev = NULL; + + assert(parent != NULL); + if(child == NULL){ + return; + } + + current = parent->child; + child->mama = parent; + if(current == NULL){ + parent->child = child; + child->next = NULL; + } else { + /* + * step to end of child chain + */ + while(current != NULL){ + prev = current; + current = current->next; + } + child->next = NULL; + prev->next = child; + } + SendTreeChangeMessage(parent,callData); +} +/*--------------------------------------------------------------------------*/ +void DeleteHipadabaNode(pHdb node, void *callData){ + pHdb current = NULL, tmp = NULL; + + if(node == NULL){ + return; + } + + RemoveHdbNodeFromParent(node, callData); + + DeleteNodeData(node); +} +/*--------------------------------------------------------------------------*/ +int isHdbNodeValid(pHdb node){ + if(node == NULL){ + return 0; + } + if(node->magic == HDBMAGICK){ + return 1; + } else { + return 0; + } +} +/*--------------------------------------------------------------------------*/ +pHdb GetHipadabaNode(pHdb root, char *puth){ + pHdb resultNode = NULL; + char *separator = NULL; + char *path = NULL, *pathData; + + /* + * I need to make a copy in order to get the path in writable memory. + * Otherwise we SEGFAULT in hdbTrim when this function is called with + * a string constant in puth + */ + pathData = strdup(puth); + path = pathData; + if(path == NULL){ + return NULL; + } + path = hdbTrim(path); + + if(strcmp(path,"/") == 0 || strlen(path) == 0){ + free(pathData); + return root; + } + + if(path[0] == '/'){ + path++; + } + + separator = strchr(path,'/'); + if(separator == NULL){ + resultNode = locateChild(root,path); + free(pathData); + return resultNode; + } else { + *separator = '\0'; + resultNode = locateChild(root, path); + if(resultNode == NULL){ + free(pathData); + return NULL; + } else { + separator++; + resultNode = GetHipadabaNode(resultNode,separator); + free(pathData); + return resultNode; + } + } +} +/*--------------------------------------------------------------------------*/ +char *GetHipadabaPath(pHdb node){ + pHdb nodeStack[64]; + int depth = 0, length = 0, i; + pHdb current = NULL; + char *pPtr = NULL; + + /** + * build a nodestack and find out required string length for path + */ + current = node; + while(current != NULL){ + length += strlen(current->name) + 1; + nodeStack[depth] = current; + depth++; + assert(depth < 64); + current = current->mama; + } + + pPtr = malloc(length*sizeof(char)); + if(pPtr == NULL){ + return NULL; + } + memset(pPtr,0,length*sizeof(char)); + + /* + * we wish to decremement by one because above loop + * increments one to many and we wish to ignore the + * root node + */ + for(i = depth - 2; i >= 0; i--){ + strcat(pPtr,"/"); + strcat(pPtr,nodeStack[i]->name); + } + return pPtr; +} +/*==================== Callback Functions ==================================*/ +pHdbCallback MakeHipadabaCallback(hdbCallbackFunction func, + void *userData, killUserData killFunc){ + pHdbCallback pNew = NULL; + + assert(func != NULL); + + pNew = malloc(sizeof(hdbCallback)); + if(pNew == NULL){ + return NULL; + } + memset(pNew,0,sizeof(hdbCallback)); + + pNew->userCallback = func; + pNew->userData = userData; + pNew->killFunc = killFunc; + return pNew; +} +/*-------------------------------------------------------------------*/ +void AppendHipadabaCallback(pHdb node, pHdbCallback newCB){ + pHdbCallback current = NULL; + + assert(node); + current = node->callBackChain; + newCB->next = NULL; + if(current == NULL){ + node->callBackChain = newCB; + return; + } + while(current->next != NULL){ + current = (pHdbCallback)current->next; + } + current->next = newCB; +} +/*---------------------------------------------------------------------------*/ +void PrependHipadabaCallback(pHdb node,pHdbCallback newCB){ + assert(node != NULL); + + newCB->next = node->callBackChain; + node->callBackChain = newCB; +} +/*----------------------------------------------------------------------------*/ +void *FindHdbCallbackData(pHdb node, void *userPtr){ + hdbDataSearch dsm; + + dsm.type = dataSearch; + dsm.testPtr = userPtr; + dsm.result = NULL; + + InvokeCallbackChain(node, (pHdbMessage)&dsm); + return dsm.result; + +} +/*=================== parameter interface ====================================*/ +static int canCopy(hdbValue *source, hdbValue *target){ + if(target->dataType == HIPINTVARAR) { + if(source->dataType == HIPINTAR || + source->dataType == HIPINTVARAR){ + return 1; + } + } + if(target->dataType == HIPFLOATVARAR) { + if(source->dataType == HIPFLOATAR || + source->dataType == HIPFLOATVARAR){ + return 1; + } + } + if(source->dataType != target->dataType){ + return 0; + } else { + return 1; + } +} +/*----------------------------------------------------------------------------*/ +int copyHdbValue(hdbValue *source, hdbValue *target){ + int i; + + if(!canCopy(source,target)){ + return 0; + } + + switch(source->dataType){ + case HIPNONE: + break; + case HIPINT: + target->v.intValue = source->v.intValue; + break; + case HIPFLOAT: + target->v.doubleValue = source->v.doubleValue; + break; + case HIPTEXT: + if(target->v.text != NULL){ + free(target->v.text); + } + target->v.text = strdup(source->v.text); + break; + case HIPINTAR: + case HIPINTVARAR: + if(target->arrayLength != source->arrayLength || target->v.intArray == NULL){ + if(target->v.intArray != NULL){ + free(target->v.intArray); + } + target->v.intArray = malloc(source->arrayLength * sizeof(int)); + if(target->v.intArray == NULL){ + return 0; + } + memset(target->v.intArray,0,source->arrayLength + * sizeof(int)); + target->arrayLength = source->arrayLength; + } + if(source->v.intArray != NULL){ + for(i = 0; i < source->arrayLength; i++){ + target->v.intArray[i] = source->v.intArray[i]; + } + } + break; + case HIPFLOATAR: + case HIPFLOATVARAR: + if(target->arrayLength != source->arrayLength + || target->v.floatArray == NULL){ + if(target->v.floatArray != NULL){ + free(target->v.floatArray); + } + target->v.floatArray = + malloc(source->arrayLength * sizeof(double)); + if(target->v.floatArray == NULL){ + return 0; + } + memset(target->v.floatArray,0,source->arrayLength * + sizeof(double)); + target->arrayLength = source->arrayLength; + } + if(source->v.floatArray != NULL){ + for(i = 0; i < source->arrayLength; i++){ + target->v.floatArray[i] = source->v.floatArray[i]; + } + } + break; + case HIPOBJ: + target->v.obj = source->v.obj; + break; + case HIPFUNC: + target->v.func = source->v.func; + break; + default: + /* + * unknown data type + */ + assert(0); + break; + } + return 1; +} +/*---------------------------------------------------------------------------*/ +static int SendDataMessage(pHdb node, char *type, + hdbValue v, void *callData){ + hdbDataMessage dataMes; + + assert(type == set || type == get || type == update); + dataMes.type = type; + dataMes.v = &v; + dataMes.callData = callData; + return InvokeCallbackChain(node, (pHdbMessage)&dataMes); +} +/*----------------------------------------------------------------------------*/ +int SetHipadabaPar(pHdb node, hdbValue v, void *callData){ + return SendDataMessage(node, set, v,callData); +} +/*-----------------------------------------------------------------------------*/ +int UpdateHipadabaPar(pHdb node, hdbValue v, void *callData){ + int status; + + status = SendDataMessage(node, update, v,callData); + if(status == 1){ + copyHdbValue(&v,&node->value); + } + return status; +} +/*-----------------------------------------------------------------------------*/ +int NotifyHipadabaPar(pHdb node,void *callData){ + SendDataMessage(node, update, node->value,callData); + return 1; +} +/*-----------------------------------------------------------------------------*/ +int GetHipadabaPar(pHdb node, hdbValue *v, void *callData){ + int status; + + v->dataType = node->value.dataType; + v->doNotFree = 0; + v->v.text = NULL; /* this sets all pointers in the union to NULL */ + + status = SendDataMessage(node, get, *v,callData); + if(status != 1 ){ + return status; + } + copyHdbValue(&node->value,v); + return 1; +} +/*----------------------------------------------------------------------------*/ +static int calcDataLength(pHdb node, int testLength){ + int length = 0; + + length = getHdbValueLength(node->value); + if(node->value.dataType == HIPFLOATVARAR || + node->value.dataType == HIPINTVARAR || + node->value.dataType == HIPTEXT){ + length = testLength; + } + return length; +} +/*============================= Property Functions ==========================*/ + void SetHdbProperty(pHdb node, char *key, char *value){ + if(node != NULL && key != NULL && node->properties != NULL){ + if (value == NULL) { + StringDictDelete(node->properties, key); + } else if(StringDictExists(node->properties, key)){ + StringDictUpdate(node->properties,key,value); + } else { + StringDictAddPair(node->properties,key,value); + } + } + } +/*---------------------------------------------------------------------------*/ +int GetHdbProperty(pHdb node, char *key, char *value, int len){ + if(node != NULL && node->properties != NULL){ + return StringDictGet(node->properties,key,value,len); + } else { + return 0; + } +} +/*---------------------------------------------------------------------------*/ +char *GetHdbProp(pHdb node, char *key){ + if(node != NULL && node->properties != NULL){ + return StringDictGetShort(node->properties,key); + } else { + return NULL; + } +} +/*---------------------------------------------------------------------------*/ +void InitHdbPropertySearch(pHdb node){ + if(node != NULL && node->properties != NULL){ + StringDictKillScan(node->properties); + } +} +/*--------------------------------------------------------------------------*/ +const char *GetNextHdbProperty(pHdb node, char *value ,int len){ + if(node != NULL && node->properties != NULL) { + return StringDictGetNext(node->properties, value, len); + } else { + return NULL; + } +} diff --git a/hipadaba.h b/hipadaba.h new file mode 100644 index 00000000..e94c7234 --- /dev/null +++ b/hipadaba.h @@ -0,0 +1,439 @@ +/** @file + * Hipadaba is a hierarchical database of parameters. Parameters can be of + * various types. What happens when a parameter is being set, updated or read + * is largely determined through callbacks which can be registered on + * parameters. This can implement permission checking, range checking, + * automatic notifications and whatever comes up. + * + * There is some subtlety here between updating and setting a parameter. The + * issue is that in instrument control there are two types of parameters: + * Instant program parameters and external parameters like motors which are + * dependent on some possibly slow and inaccurate hardware. Let us consider + * the latter: Setting the parameter should do all necessary checks on the + * parameter and tell the hardware where to go. Some internal code may be + * watching the hardware; that code should use Update which justs sets a new value + * and invokes callbacks which notify interested parties about the new parameter + * value. For program parameters, a callback shall be installed which calls update + * directly after setting the parameter. Thus notification callbacks shall always be + * connected to the update chain. + * + * copyright: GPL + * + * Mark Koennecke, June 2006 + * + * Added treeChange callback, Mark Koennecke, November 2006 + * + * Added support for properties, Mark Koennecke, January 2007 + * + * Refactored callback handling, Markus Zolliker, Mark Koennecke, March 2008 + */ +#ifndef HIPADABA +#define HIPADABA +#include + +/*------- datatypes */ +#define HIPNONE -1 +#define HIPINT 0 +#define HIPFLOAT 1 +#define HIPTEXT 2 +#define HIPINTAR 3 +#define HIPFLOATAR 4 +#define HIPINTVARAR 5 +#define HIPFLOATVARAR 6 +#define HIPOBJ 7 +#define HIPFUNC 8 +/*--------- error codes */ +#define HDBTYPEMISMATCH -7701 +#define HDBLENGTHMISMATCH -7702 +/*===================== structure definitions ===================================*/ +typedef void voidFunc(void); + +typedef struct __hdbValue { + int dataType; + int arrayLength; + int doNotFree; + union __value { + int intValue; + double doubleValue; + char *text; + int *intArray; + double *floatArray; + voidFunc *func; + void *obj; + }v; +}hdbValue; +/*------------------------------------------------------------------------------*/ +typedef struct __hipadaba { + int magic; + struct __hipadaba *mama; + struct __hipadaba *child; + struct __hipadaba *next; + struct __hdbcallback *callBackChain; + char *name; + hdbValue value; + int protected; + pStringDict properties; + }Hdb, *pHdb; +/*-------------- return values for callback functions -------------------------*/ +typedef enum {hdbContinue, + hdbAbort, + hdbKill } hdbCallbackReturn; +/*======================== Messages ===========================================*/ +typedef struct __hdbMessage { + char *type; +} hdbMessage, *pHdbMessage; +/*-----------------------------------------------------------------------------*/ +typedef struct { + char *type; + hdbValue *v; + void *callData; +}hdbDataMessage, *pHdbDataMessage; +/*-------------------------------------------------------------------------------*/ +typedef struct { + char *type; + void *callData; +}hdbTreeChangeMessage, *pHdbTreeChangeMessage; +/*-------------------------------------------------------------------------------*/ +typedef struct { + char *type; + void *testPtr; + void *result; +}hdbDataSearch, *pHdbDataSearch; +/*-------------------------------------------------------------------------------*/ +typedef hdbCallbackReturn (*hdbCallbackFunction)(pHdb currentNode, + void *userData, + pHdbMessage message); + +typedef void (*killUserData)(void *data); +/*-------------------------------------------------------------------------------*/ +typedef struct __hdbcallback { + void *userData; + killUserData killFunc; + hdbCallbackFunction userCallback; + int killFlag; + struct __hdbcallback *next; + struct __hdbcallback *previous; + }hdbCallback, *pHdbCallback; +/*============= Message Test Functions ==========================================*/ +/** + * Test a message if it is a set message + * @param toTest The message to test. + * @return NULL if the message is no set message or a message pointer if it is. + */ +pHdbDataMessage GetHdbSetMessage(pHdbMessage toTest); +/** + * Test a message if it is a set message + * @param toTest The message to test. + * @return NULL if the message is no set message or a message pointer if it is. + */ +pHdbDataMessage GetHdbGetMessage(pHdbMessage toTest); +/** + * Test a message if it is a update message + * @param toTest The message to test. + * @return NULL if the message is no update message or a message pointer if + * it is. + */ +pHdbDataMessage GetHdbUpdateMessage(pHdbMessage toTest); +/** + * Test a message if it is a tree change message + * @param toTest The message to test. + * @return NULL if the message is no tree change message or a message + * pointer if it is. + */ +pHdbTreeChangeMessage GetHdbTreeChangeMessage(pHdbMessage toTest); +/** + * Test a message if it is a data search message + * @param toTest The message to test. + * @return NULL if the message is no data search message or a message + * pointer if it is. + */ +pHdbDataSearch GetHdbDataSearchMessage(pHdbMessage toTest); +/** + * Test a message if it is a kill node message + * @param toTest The message to test. + * @return NULL if the message is no kill node message or a message + * pointer if it is. + */ +pHdbMessage GetHdbKillNodeMessage(pHdbMessage toTest); +/*======================== Function protoypes: hdbData ========================*/ +/** + * make a hdbValue with the given datatype and length + * Do not initialise + * @param datatype The datatype of the hdbValue + * @param length The array length of the hdbValue + * @return a suitably defined hdbValue + */ +hdbValue makeHdbValue(int datatype, int length); +/** + * wrap an integer as an hdbValue + * @param initValue the initial value of the int + * @return: A properly initialized hdbValue structure + */ +hdbValue MakeHdbInt(int initValue); +/** + * wrap a float as an hdbValue + * @param initValue the initial value of the float + * @return: A properly initialized hdbValue structure + */ +hdbValue MakeHdbFloat(double initValue); +/** + * wrap a text string as an hdbValue + * @param initText the initial value of the text. WARNING: MakeHdbText does + * not copy the data. The Hdb code only copies data on updates. Normally this + * no problem; however in complicated cenarios it is better if initText points + * to dynamically allocated memory. + * @return: A properly initialized hdbValue structure + */ +hdbValue MakeHdbText(char *initText); +/** + * wrap a int array as an hdbValue + * @param length The length of the int array + * @param data the initial content of the int array. WARNING: MakeHdbIntArray + * does not copy the data. The Hdb code only copies data on updates. Normally + * this no problem; however in complicated scenarios it is better if + * data points to dynamically allocated memory. + * @return: A properly initialized hdbValue structure + */ +hdbValue MakeHdbIntArray(int length, int *data); +/** + * wrap a float array as an hdbValue + * @param length The length of the int array + * @param data the initial content of the float array. WARNING: MakeHdbFloatArray + * does not copy the data. The Hdb code only copies data on updates. Normally + * this no problem; however in complicated scenarios it is better if + * data points to dynamically allocated memory. + * @return: A properly initialized hdbValue structure + */ +hdbValue MakeHdbFloatArray(int length, double *data); +/** + * wrap a function as an hdbValue + * @param func the function + * @return: A properly initialized hdbValue structure + */ +hdbValue MakeHdbFunc(voidFunc *func); +/** + * wrap an object as an hdbValue + * @param obj the object + * @return: A properly initialized hdbValue structure + */ +hdbValue MakeHdbObj(void *obj); +/** + * release any dynamic memory associated with v + * @param v The hdbValue to check for dynamic memory allocation to be + * released. + */ +void ReleaseHdbValue(hdbValue *v); +/** + * copy a hipadaba value field. Takes care of memory allocation + * @param source The hdbValue to copy from + * @param target The hdbValue to copy to. + * @return 1 on success, 0 when out of memory or when type mismatch + */ +int copyHdbValue(hdbValue *source, hdbValue *target); +/** + * compares two hdbValues for identity + * @param v1 The first hdbValue + * @param v2 The second hdbValue + * @return 1 when identical, 0 else + */ +int compareHdbValue(hdbValue v1, hdbValue v2); +/** + * create a hdbValue structure with the identical properties + * as the one given as parameter. Datatypes are copied, memory is + * allocated etc. Data is copied, too + * @param source The hdbValue type to clone + * @param clone the target hdbValue structure + * @return 1 on success, 0 on when out of memory + */ +int cloneHdbValue(hdbValue *source, hdbValue *clone); +/** + * get the length of the hdbValue in bytes. + * @param v The hdbValue to calculate the length for + * @return the number of data bytes + */ +int getHdbValueLength(hdbValue v); +/*========================== function protoypes: Nodes =======================*/ +/** + * make a new hipadaba node + * @param name The name of the new node + * @param datatype The datatype of the new node + * @param length the array length + * @return a new node or NULL when out of memory + */ +pHdb MakeHipadabaNode(char *name, int datatype, int length); +/** + * add a child to a node at the end of the child list. + * @param parent The node to which to add the child + * @param child The node to add + * @param callData User data for the tree chnage callback. Can be NULL. + */ +void AddHipadabaChild(pHdb parent, pHdb child, void *callData); +/** + * Delete only the node data, without invoking any callbacks + * @param node The node to delete. + */ +void DeleteNodeData(pHdb node); +/** + * delete a hipadaba node and all its children. Then invoke the tree + * change callback to notify listeners. + * @param node The node to delete + * @param callData User data for the tree change callback + */ +void DeleteHipadabaNode(pHdb node, void *callData); +/* + * checks if a Hdb node is valid + * @param node The node to check + * @return 1 when valid, 0 else + */ +int isHdbNodeValid(pHdb node); +/** + * retrieve a node + * @param root The node where to start the search for the node + * @param path The unix path string for the node relative to parent + * @return The desired node or NULL when no such node exists + */ +pHdb GetHipadabaNode(pHdb root, char *path); +/** + * given a node, return the full path name to the node + * @param node The node to get the path for + * @return The full path to the node. This is dynamically allocated memory; + * the caller is reponsible for deleting it. Can be NULL when out of memory. + */ +char *GetHipadabaPath(pHdb node); +/** + * removes a node from the parents child list. + * @param node the node to remove + * @param callData User data for the tree change callback + */ +void RemoveHdbNodeFromParent(pHdb node, void *callData); +/** + * count the numbers of children in thie Hdb node + * @param node The node to count children for + * @return The number of children + */ +int CountHdbChildren(pHdb node); +/*===================== function protoypes: Callbacks ========================*/ +/** + * make a new hipdaba callback + * @param func The function to invoke for this callback + * @param userData userData to be associated with this callback. Can be NULL. + * @param killFunc A function for freeing the userData. Can be NULL, then it will + * not be invoked + * @return A new suitabaly initialised callback structure or NULL when required elements + * are missing or there is nor memory. + */ +pHdbCallback MakeHipadabaCallback(hdbCallbackFunction func, + void *userData, killUserData killFunc); +/** + * add a callback at the end of the callback chain + * @param node The node to which to append the callback + * @param newCB The callback to append + */ +void AppendHipadabaCallback(pHdb node,pHdbCallback newCB); +/** + * add a callback at the head of the callback chain + * @param node The node to which to append the callback + * @param newCB The callback prepend + */ +void PrependHipadabaCallback(pHdb node,pHdbCallback newCB); +/** + * find the callback data + * @param node the node from where callbacks have to be searched + * @param userPtr A pointer to some user data which the callback + * uses to determine if it is the right one. + * @return the found callback user data or NULL on failure + */ +void *FindHdbCallbackData(pHdb node, void *userPtr); +/** + * invoke a callback chain. + * @param node The node reponsible for this callback chain + * @param message the message to send + * @return 1 on success, 0 on failure + */ +int InvokeCallbackChain(pHdb node, pHdbMessage message); +/** + * Deletes the callback chain of a node. This is internal, normal users + * should not use this function. Or you create a mess! + * @param node The node + */ +void DeleteCallbackChain(pHdb node); +/** + * apply message to the node and all its children + * @param node Th node where to start recursing + * @param message The message to send + */ +void RecurseCallbackChains(pHdb node, pHdbMessage message); +/*============== Parameter Handling ===============================*/ +/** + * Set a hipadaba parameter. This is an external set for a parameter. It may cause + * motors to start driving etc. + * @param node The node for which to set the parameter + * @param v The new value for the node + * @param callData Additonal context data to be passed to the callback functions + * @return 0 on failure, 1 on success + */ +int SetHipadabaPar(pHdb node, hdbValue v, void *callData); +/** + * Update a hipadaba parameter. This is an internal update of a parameter, + * during driving etc. + * @param node The node for which to update the parameter + * @param v The new value for the node + * @param callData Additonal context data to be passed to the callback functions + * @return 0 on failure, 1 on success + */ +int UpdateHipadabaPar(pHdb node, hdbValue v, void *callData); +/** + * notify any update listeners that this node has been internally modifed. + * @param node The node modified + * @param callData Addtional data for the callback + * @return 1 on success, 0 on failure + */ +int NotifyHipadabaPar(pHdb node,void *callData); + +/** + * Read a hipadaba parameter + * @param node The node for which to read the parameter + * @param v The read value for the node + * @param callData Additonal context data to be passed to the callback functions + * @return 0 on failure, 1 on success + */ +int GetHipadabaPar(pHdb node, hdbValue *v, void *callData); +/*================================ Property Interface ==============================================*/ +/** + * set a property + * @param node The node to set the property for + * @param key The key for the property + * @param value The value of the property + */ + void SetHdbProperty(pHdb node, char *key, char *value); + /** + * get the value of a property + * @param node The node to get the property from + * @param key The properties key + * @param value The area to which to copy the property + * @param len The length of value + * @return 0 on failure, 1 on success + */ + int GetHdbProperty(pHdb node, char *key, char *value, int len); + /** + * get the value of a property + * @param node The node to get the property from + * @param key The properties key + * @return the property or NULL on failure. Warning: the string is + * only valid as long as the property has not changed + */ + char *GetHdbProp(pHdb node, char *key); + /** + * initialize a property scan on this node + * @param node The node for which to scan properties + */ +void InitHdbPropertySearch(pHdb node); +/** + * get the next property in a search + * @param node The node for which to search properties + * @param value An area where to copy the value of the property + * @param len The length of value + * @return The key of the property or NULL when the property list is exhausted + */ +const char *GetNextHdbProperty(pHdb node, char *value ,int len); +#endif diff --git a/histmem.c b/histmem.c index 11123427..ebf09b14 100644 --- a/histmem.c +++ b/histmem.c @@ -68,6 +68,14 @@ /* #define LOADDEBUG 1 */ +/* + * from histregress.c + */ +extern pHistDriver CreateRegressHM(pStringDict pOpt); +/* + * from slavehm.c + */ +extern pHistDriver MakeHMSlaveHM(pStringDict pOpt); /*------------------------------------------------------------------------*/ static int HistHalt(void *pData) { @@ -171,7 +179,9 @@ iRet = self->pDriv->Start(self->pDriv, pCon); if(iRet == OKOK) { - updateHMData(self->pDriv->data); + /* send a COUNTSTART event */ + InvokeCallBack(self->pCall,COUNTSTART,pCon); + updateHMData(self->pDriv->data); return iRet; } else @@ -450,8 +460,18 @@ } else if(strcmp(driver,"mcstas") == 0) { - pNew->pDriv = NewMcStasHM(pOption); - } + pNew->pDriv = NewMcStasHM(pOption); + } + else if(strcmp(driver,"regress") == 0) + { + pNew->pDriv = CreateRegressHM(pOption); + } +/* + else if(strcmp(driver,"slave") == 0) + { + pNew->pDriv = MakeHMSlaveHM(pOption); + } +*/ else { site = getSite(); @@ -721,8 +741,6 @@ void HistDirty(pHistMem self) { assert(self); - /* send a COUNTSTART event */ - InvokeCallBack(self->pCall,COUNTSTART,pCon); /* start */ return StartDevice(GetExecutor(),"HistogramMemory", self->pDes, self, @@ -744,9 +762,6 @@ void HistDirty(pHistMem self) return 0; } - /* send a COUNTSTART event */ - InvokeCallBack(self->pCall,COUNTSTART,pCon); - /* wait till end */ iRet = Wait4Success(GetExecutor()); if(iRet == DEVINT) @@ -819,7 +834,7 @@ void HistDirty(pHistMem self) return 0; } - if(iEnd > iDataLen/sizeof(HistInt)) + if( (iEnd -iStart) > iDataLen/sizeof(HistInt)) { SCWrite(pCon,"WARNING: truncating request to fit data space",eWarning); iEnd = (iDataLen/sizeof(HistInt)) - 1; @@ -1123,7 +1138,7 @@ static int checkHMEnd(pHistMem self, char *text){ return 0; } memset(pBuf,0,iRet+60); - HistGetOption(self,argv[2],pBuf,iRet); + HistGetOption(self,argv[2],pBuf,iRet+60); sprintf(pBueffel,"%s.%s = %s",argv[0],argv[2],pBuf); SCWrite(pCon,pBueffel,eValue); free(pBuf); @@ -1233,13 +1248,19 @@ static int checkHMEnd(pHistMem self, char *text){ } else if(strcmp(argv[1],"init") == 0) { + if(GetStatus() != eEager) + { + SCWrite(pCon,"ERROR: cannot initialize HM while running", + eError); + return 0; + } if(SCMatchRights(pCon,usMugger)) { iRet = HistConfigure(self,pCon,pSics); if(iRet) { self->iInit = 1; - SCSendOK(pCon); + SCSendOK(pCon); } else { @@ -1267,6 +1288,16 @@ static int checkHMEnd(pHistMem self, char *text){ SCSendOK(pCon); return 1; } + /* pause */ + else if(strcmp(argv[1],"pause") == 0) + { if(!SCMatchRights(pCon,usUser)) + { + return 0; + } + self->pDriv->Pause(self->pDriv,pCon); + SCSendOK(pCon); + return 1; + } /* normal counting*/ else if(strcmp(argv[1],"count") == 0) { @@ -1347,6 +1378,31 @@ static int checkHMEnd(pHistMem self, char *text){ } return iRet; } + else if(strcmp(argv[1],"initfile") == 0) /* initialize from a file */ + { + /* check user rights */ + if(!SCMatchRights(pCon,self->iAccess)) + { + return 0; + } + + /* enough arguments */ + if(argc < 3) + { + sprintf(pBueffel,"ERROR: insufficient number of arguments to %s %s", + argv[0], argv[1]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + iRet = loadHMData(self->pDriv->data,pCon,argv[2]); + self->pDriv->SetHistogram(self->pDriv,pCon,0,0,GetHistLength(self), + self->pDriv->data->localBuffer); + if(iRet) + { + SCSendOK(pCon); + } + return iRet; + } else if(strcmp(argv[1],"get") == 0) /* get a histogram */ { /* check parameters, first required: no of Hist */ @@ -1373,11 +1429,16 @@ static int checkHMEnd(pHistMem self, char *text){ iStart = 0; } - if(argc > 4){ - iEnd = checkHMEnd(self,argv[4]); - } else { - iEnd = checkHMEnd(self,NULL); - } + if(argc > 4){ + iEnd = checkHMEnd(self,argv[4]); + } else { + iEnd = checkHMEnd(self,NULL); + } + + if(iNum != 0 && argc > 4) + { + iEnd = atoi(argv[4]); + } /* allocate data storage and get it */ lData = (HistInt *)malloc(iEnd*sizeof(HistInt)); @@ -1387,8 +1448,14 @@ static int checkHMEnd(pHistMem self, char *text){ return 0; } memset(lData,0,iEnd*sizeof(HistInt)); - iRet = GetHistogram(self,pCon,iNum,iStart,iEnd, - lData,iEnd*sizeof(long)); + if(iNum == 0) + { + iRet = GetHistogram(self,pCon,iNum,iStart,iEnd, + lData,iEnd*sizeof(long)); + } else { + iRet = GetHistogramDirect(self,pCon,iNum,iStart, iEnd, + lData, iEnd*sizeof(long)); + } if(!iRet) { sprintf(pBueffel,"ERROR: cannot retrieve histogram %d",iNum); @@ -1555,10 +1622,10 @@ static int checkHMEnd(pHistMem self, char *text){ else if(strcmp(argv[1],"timebin") == 0) { Tcl_DStringInit(&tResult); - Tcl_DStringAppend(&tResult,"histogram.timebins = ",-1); + Tcl_DStringAppend(&tResult,"histogram.timebins =",-1); for(i = 0; i < self->pDriv->data->nTimeChan; i++) { - sprintf(pBueffel," %8.2f", self->pDriv->data->timeBinning[i]); + sprintf(pBueffel,"%.2f ", self->pDriv->data->timeBinning[i]); Tcl_DStringAppend(&tResult,pBueffel,-1); } /* Write it */ @@ -1576,8 +1643,8 @@ static int checkHMEnd(pHistMem self, char *text){ return 0; } if(GetStatus() == eCounting) - { - SCWrite(pCon,"ERROR: cannot modify timebinning while counting", + { + SCWrite(pCon,"ERROR: cannot modify timebinning while counting", eError); return 0; } diff --git a/histogram.tex b/histogram.tex index e5911e74..564af499 100644 --- a/histogram.tex +++ b/histogram.tex @@ -21,7 +21,7 @@ $\langle$Modes {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ ePSD,@\\ \mbox{}\verb@ eSANSTOF@\\ \mbox{}\verb@ } HistMode;@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -59,7 +59,7 @@ $\langle$Modes {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ eOCount,@\\ \mbox{}\verb@ eReflect@\\ \mbox{}\verb@ } OverFlowMode;@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -163,6 +163,9 @@ $\langle$HistType {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ SConnection *pCon);@\\ \mbox{}\verb@ float (*GetTime)(pHistDriver self,@\\ \mbox{}\verb@ SConnection *pCon);@\\ +\mbox{}\verb@ HistInt *(*SubSample)(pHistDriver self, @\\ +\mbox{}\verb@ SConnection *pCon,int bank,@\\ +\mbox{}\verb@ char *command); @\\ \mbox{}\verb@ int (*Preset)(pHistDriver self,@\\ \mbox{}\verb@ SConnection *pCon,@\\ \mbox{}\verb@ HistInt iVal);@\\ @@ -173,7 +176,7 @@ $\langle$HistType {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ int (*FreePrivate)(pHistDriver self);@\\ \mbox{}\verb@ void *pPriv;@\\ \mbox{}\verb@ } HistDriver;@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -276,7 +279,9 @@ $\langle$HistDrivProt {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ void DeleteHistDriver(pHistDriver self);@\\ \mbox{}\verb@ int HistDriverConfig(pHistDriver self, pStringDict pOpt,@\\ \mbox{}\verb@ SConnection *pCon);@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@ HistInt *DefaultSubSample(pHistDriver self, SConnection *pCon, @\\ +\mbox{}\verb@ int bank, char *command); @\\ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -315,7 +320,7 @@ $\langle$HistST {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ pICountable pCountInt;@\\ \mbox{}\verb@ pICallBack pCall;@\\ \mbox{}\verb@ } HistMem;@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -357,7 +362,7 @@ $\langle$Protos {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@@\\ \mbox{}\verb@ pHistMem CreateHistMemory(char *drivername);@\\ \mbox{}\verb@ void DeleteHistMemory(void *self);@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -387,7 +392,7 @@ $\langle$Protos {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ int HistGetOption(pHistMem self, char *name, char *result, int iResultLen);@\\ \mbox{}\verb@ int HistSetOption(pHistMem self, char *name, char *value);@\\ \mbox{}\verb@ int HistConfigure(pHistMem self, SConnection *pCon, SicsInterp *pSics);@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -438,7 +443,7 @@ $\langle$Protos {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ int HistBlockCount(pHistMem self, SConnection *pCon);@\\ \mbox{}\verb@ void HistDirty(pHistMem self); @\\ \mbox{}\verb@@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -478,7 +483,7 @@ $\langle$Protos {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ int i, int iStart, int iEnd, @\\ \mbox{}\verb@ HistInt *lData, int iDataLen);@\\ \mbox{}\verb@ int PresetHistogram(pHistMem self, SConnection *pCon, HistInt lVal);@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -532,7 +537,7 @@ $\langle$Protos {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ int HistAction(SConnection *pCon, SicsInterp *pSics, void *pData,@\\ \mbox{}\verb@ int argc, char *argv[]);@\\ \mbox{}\verb@ @\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -594,7 +599,7 @@ following. \mbox{}\verb@@$\langle$Protos {\footnotesize ?, \ldots\ }$\rangle$\verb@@\\ \mbox{}\verb@@\\ \mbox{}\verb@#endif@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-2ex} \end{minipage}\\[4ex] @@ -620,7 +625,7 @@ following. \mbox{}\verb@@$\langle$HistDrivProt {\footnotesize ?}$\rangle$\verb@@\\ \mbox{}\verb@@\\ \mbox{}\verb@#endif@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-2ex} \end{minipage}\\[4ex] @@ -643,7 +648,7 @@ following. \mbox{}\verb@@$\langle$HistST {\footnotesize ?}$\rangle$\verb@@\\ \mbox{}\verb@@\\ \mbox{}\verb@#endif@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-2ex} \end{minipage}\\[4ex] diff --git a/histogram.w b/histogram.w index f529081f..a5bed0e9 100644 --- a/histogram.w +++ b/histogram.w @@ -130,6 +130,9 @@ definition: SConnection *pCon); float (*GetTime)(pHistDriver self, SConnection *pCon); + HistInt *(*SubSample)(pHistDriver self, + SConnection *pCon,int bank, + char *command); int (*Preset)(pHistDriver self, SConnection *pCon, HistInt iVal); @@ -231,6 +234,8 @@ only these few functions operate on histogram memory drivers in general: void DeleteHistDriver(pHistDriver self); int HistDriverConfig(pHistDriver self, pStringDict pOpt, SConnection *pCon); + HistInt *DefaultSubSample(pHistDriver self, SConnection *pCon, + int bank, char *command); @} CreateHistDriver creates a new HistDriver data structure and returns it. Or diff --git a/histregress.c b/histregress.c new file mode 100644 index 00000000..8003e27f --- /dev/null +++ b/histregress.c @@ -0,0 +1,273 @@ +/*---------------------------------------------------------------------------- + + H I S T S I M + + A simulated histogram memory for regression tests. + + All the counting error stuff is redirected to a regression counter; see + documentation there. This just adds data handling. + + copyright: see file COPYRIGHT + + Mark Koennecke, October 2006 + ----------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include "fortify.h" +#include "sics.h" +#include "countdriv.h" +#include "counter.h" +#include "stringdict.h" +#include "HistMem.h" +#include "HistDriv.i" +#include "histsim.h" + +static int iSet = 0; +static HistInt iSetVal = 0; +static HistMode eHistMode; +/*--------------------------------------------------------------------------*/ +static int RegressConfig(pHistDriver self, SConnection *pCon, + pStringDict pOption, SicsInterp *pSics) +{ + int i, iLength = 1, status; + char pData[132]; + float fFail; + pCounterDriver count; + + count = (pCounterDriver)self->pPriv; + + if(eHistMode == eHTOF) + { + for(i = 0; i < self->data->rank; i++) + { + iLength *= self->data->iDim[i]; + } + iLength *= self->data->nTimeChan; + } + + /* + deal with error settings + */ + status = StringDictGet(pOption,"errortype",pData,131); + if(status) + { + + fFail = atof(pData); + count->Set(count,"errortype",1,fFail); + } + status = StringDictGet(pOption,"recover",pData,131); + if(status) + { + + fFail = atof(pData); + count->Set(count,"recover",1,fFail); + } + status = StringDictGet(pOption,"finish",pData,131); + if(status) + { + + fFail = atof(pData); + count->Set(count,"finish",1,fFail); + } + + /* + configured test value + */ + status = StringDictGet(pOption,"testval",pData,131); + if(status) + { + iSet = 1; + iSetVal = atoi(pData); + } + + return 1; + } +/*-------------------------------------------------------------------------*/ + static int RegressStart(pHistDriver self, SConnection *pCon) + { + pCounterDriver pDriv; + + pDriv = (pCounterDriver)self->pPriv; + pDriv->fPreset = self->fCountPreset; + pDriv->eMode = self->eCount; + return pDriv->Start(pDriv); + } +/*-------------------------------------------------------------------------*/ + static int RegressPause(pHistDriver self, SConnection *pCon) + { + pCounterDriver pDriv; + + pDriv = (pCounterDriver)self->pPriv; + pDriv->fPreset = self->fCountPreset; + pDriv->eMode = self->eCount; + return pDriv->Pause(pDriv); + } +/*------------------------------------------------------------------------*/ + static int RegressContinue(pHistDriver self, SConnection *pCon) + { + pCounterDriver pDriv; + + pDriv = (pCounterDriver)self->pPriv; + pDriv->fPreset = self->fCountPreset; + pDriv->eMode = self->eCount; + return pDriv->Continue(pDriv); + } +/*-------------------------------------------------------------------------*/ + static int RegressHalt(pHistDriver self) + { + pCounterDriver pDriv; + + pDriv = (pCounterDriver)self->pPriv; + return pDriv->Halt(pDriv); + } +/*-------------------------------------------------------------------------*/ + static int RegressGetCountStatus(pHistDriver self, SConnection *pCon) + { + pCounterDriver pDriv; + float fControl; + + pDriv = (pCounterDriver)self->pPriv; + return pDriv->GetStatus(pDriv,&fControl); + } +/*-------------------------------------------------------------------------*/ + static int RegressGetError(pHistDriver self, int *iCode, char *pError, int iLen) + { + pCounterDriver pDriv; + + pDriv = (pCounterDriver)self->pPriv; + return pDriv->GetError(pDriv, iCode,pError,iLen); + } +/*-------------------------------------------------------------------------*/ + static int RegressTryAndFixIt(pHistDriver self, int iCode) + { + pCounterDriver pDriv; + + pDriv = (pCounterDriver)self->pPriv; + return pDriv->TryAndFixIt(pDriv, iCode); + } +/*--------------------------------------------------------------------------*/ + static int RegressGetData(pHistDriver self, SConnection *pCon) + { + pCounterDriver pDriv; + + pDriv = (pCounterDriver)self->pPriv; + + return pDriv->ReadValues(pDriv); + } +/*--------------------------------------------------------------------------*/ + static int RegressGetHistogram(pHistDriver self, SConnection *pCon, + int i, int iStart, int iEnd, HistInt *lData) + { + int ii; + + if(i < 0) + { + SCWrite(pCon,"ERROR: histogram out of range",eError); + return 0; + } + + if(iSet == 1) + { + for(ii = iStart; ii < iEnd; ii++) + { + lData[ii-iStart] = iSetVal; + } + } + else + { + for(ii = iStart; ii < iEnd; ii++) + { + lData[ii-iStart] = random(); + } + } + return 1; + } +/*------------------------------------------------------------------------*/ + static int RegressSetHistogram(pHistDriver self, SConnection *pCon, + int i, int iStart, int iEnd, HistInt *lData) + { + iSet = 1; + iSetVal = lData[0]; + return 1; + } + +/*-------------------------------------------------------------------------*/ + static int RegressPreset(pHistDriver self, SConnection *pCon, HistInt iVal) + { + iSet = 1; + iSetVal = iVal; + return 1; + } +/*------------------------------------------------------------------------*/ + static int RegressFreePrivate(pHistDriver self) + { + pCounterDriver pDriv; + + pDriv = (pCounterDriver)self->pPriv; + DeleteCounterDriver(pDriv); + return 1; + } +/*------------------------------------------------------------------------*/ + static long RegressGetMonitor(pHistDriver self, int i, SConnection *pCon) + { + pCounterDriver pDriv; + long lVal; + + pDriv = (pCounterDriver)self->pPriv; + return pDriv->lCounts[i]; + + } +/*------------------------------------------------------------------------*/ + static float RegressGetTime(pHistDriver self, SConnection *pCon) + { + pCounterDriver pDriv; + long lVal; + + pDriv = (pCounterDriver)self->pPriv; + return pDriv->fTime; + + } +/*-------------------------------------------------------------------------*/ + pHistDriver CreateRegressHM(pStringDict pOpt) + { + pHistDriver pNew = NULL; + + /* create the general driver */ + pNew = CreateHistDriver(pOpt); + if(!pNew) + { + return NULL; + } + + /* put a Regresscounter in */ + pNew->pPriv = (void *)NewRegressCounter("HistoRegress"); + if(!pNew->pPriv) + { + DeleteHistDriver(pNew); + return NULL; + } + + /* configure all those functions */ + pNew->Configure = RegressConfig; + pNew->Start = RegressStart; + pNew->Halt = RegressHalt; + pNew->GetCountStatus = RegressGetCountStatus; + pNew->GetError = RegressGetError; + pNew->TryAndFixIt = RegressTryAndFixIt; + pNew->GetData = RegressGetData; + pNew->GetHistogram = RegressGetHistogram; + pNew->SetHistogram = RegressSetHistogram; + pNew->GetMonitor = RegressGetMonitor; + pNew->GetTime = RegressGetTime; + pNew->Preset = RegressPreset; + pNew->FreePrivate = RegressFreePrivate; + pNew->Pause = RegressPause; + pNew->Continue = RegressContinue; + StringDictAddPair(pOpt,"errortype","0"); + StringDictAddPair(pOpt,"recover","1"); + StringDictAddPair(pOpt,"testval","0"); + + return pNew; + } diff --git a/histsim.c b/histsim.c index 0d085a1e..98eab3df 100644 --- a/histsim.c +++ b/histsim.c @@ -183,6 +183,13 @@ lData[ii-iStart] = iSetVal; } } + else if(iSet == 2) + { + for(ii = iStart; ii < iEnd; ii++) + { + lData[ii-iStart] = self->data->localBuffer[ii]; + } + } else { for(ii = iStart; ii < iEnd; ii++) @@ -196,8 +203,14 @@ static int SimSetHistogram(pHistDriver self, SConnection *pCon, int i, int iStart, int iEnd, HistInt *lData) { - iSet = 1; + iSet = 2; + if(self->data->localBuffer == NULL){ + resizeBuffer(self->data); + } iSetVal = lData[0]; + if(iEnd <= getHMDataLength(self->data)){ + memcpy(self->data->localBuffer+iStart,lData,(iEnd - iStart)*sizeof(HistInt)); + } return 1; } @@ -237,6 +250,21 @@ return pDriv->fTime; } +/*-------------------------------------------------------------------------*/ +HistInt *DefaultSubSample(pHistDriver self, SConnection *pCon, + int bank, char *command){ + HistInt *data = NULL; + char error[132]; + + assert(bank == 0); /* no bank handling yet.. */ + + memset(error,0,132*sizeof(char)); + data = subSample(self->data, command, error, 132); + if(data == NULL){ + SCWrite(pCon,error,eError); + } + return data; +} /*-------------------------------------------------------------------------*/ pHistDriver CreateSIMHM(pStringDict pOpt) { @@ -273,6 +301,7 @@ pNew->FreePrivate = SimFreePrivate; pNew->Pause = SimPause; pNew->Continue = SimContinue; + pNew->SubSample = DefaultSubSample; StringDictAddPair(pOpt,"failrate","-1"); return pNew; diff --git a/hkl.c b/hkl.c index 8d50deab..4351f20c 100644 --- a/hkl.c +++ b/hkl.c @@ -52,7 +52,10 @@ return 1; } fprintf(fd,"#Crystallographic Settings\n"); - fprintf(fd,"%s lambda %f\n",name, self->fLambda); + if(self->iManual == 1) + { + fprintf(fd,"%s lambda %f\n",name, self->fLambda); + } fprintf(fd, "%s setub %8.6f %8.6f %8.6f %8.6f %8.6f %8.6f %8.6f %8.6f %8.6f\n", name, @@ -61,7 +64,6 @@ fprintf(fd,"%s hm %d\n",name, self->iHM); fprintf(fd,"%s scantolerance %f\n", name,self->scanTolerance); fprintf(fd,"%s nb %d\n", name, self->iNOR); - fprintf(fd,"%s phiom %d\n", name, self->iOMPHI); return 1; } @@ -689,7 +691,7 @@ int hklInRange(void *data, float fSet[4], int mask[4]) fSet[0] = dTheta; /* for omega check against the limits +- SCANBORDER in order to allow for - a omega scan + a omega scan. */ MotorGetPar(self->pOmega,"softlowerlim",&fLimit); if((float)fSet[1] < fLimit + self->scanTolerance){ @@ -721,7 +723,7 @@ static int calculateBisecting(MATRIX z1, pHKL self, SConnection *pCon, float fSet[4], double myPsi, int iRetry) { double stt, om, chi, phi, psi, ompsi, chipsi, phipsi; - int i, test; + int i, test, mask[4]; /* just the plain angle calculation @@ -731,16 +733,31 @@ static int calculateBisecting(MATRIX z1, pHKL self, SConnection *pCon, return 0; } + fSet[0] = stt; + fSet[1] = om; + fSet[2] = chi; + fSet[3] = phi; if(iRetry == 1) { - rotatePsi(om,chi,phi,psi,&ompsi,&chipsi,&phipsi); + rotatePsi(om,chi,phi,myPsi,&ompsi,&chipsi,&phipsi); fSet[1] = ompsi; fSet[2] = circlify(chipsi); fSet[3] = circlify(phipsi); return 1; } else { - return findAllowedBisecting(self->fLambda, z1, fSet, hklInRange,self); + if(hklInRange(self,fSet, mask) == 1){ + return 1; + } else { + if(tryOmegaTweak(self,z1, &stt, &om, &chi, &phi) == 1){ + fSet[0] = stt; + fSet[1] = om; + fSet[2] = chi; + fSet[3] = phi; + return 1; + } else { + return findAllowedBisecting(self->fLambda, z1, fSet, hklInRange,self); + } + } } - } /*-----------------------------------------------------------------------*/ static int calculateNormalBeam(MATRIX z1, pHKL self, SConnection *pCon, @@ -780,7 +797,7 @@ static int calculateNormalBeam(MATRIX z1, pHKL self, SConnection *pCon, status = z1mToNormalBeam(self->fLambda, z3, &gamma, &omnb, &nu); - omnb += 180.; + /* omnb += 180.; */ mat_free(z3); if(status != 1) { @@ -789,7 +806,11 @@ static int calculateNormalBeam(MATRIX z1, pHKL self, SConnection *pCon, if(checkNormalBeam(omnb, &gamma, nu,fSet,pCon,self)){ return 1; } else { - return 0; + if(checkNormalBeam(omnb + 360., &gamma, nu, fSet,pCon,self)){ + return 1; + } else { + return 0; + } } } /*---------------------------------------------------------------------*/ @@ -850,7 +871,7 @@ static int calculateNormalBeamOmega(MATRIX z1, pHKL self, { if(checkNormalBeam(om, &gamma, nu,fSet,pCon,self)) { - return 1; + return 1; } } return 0; @@ -903,31 +924,7 @@ static int calculateNormalBeamOmega(MATRIX z1, pHKL self, if(self->iNOR == 0) { status = calculateBisecting(z1,self,pCon,fSet, myPsi, iRetry); - /* - * Betrand mode: wrap phi rotation into omega - */ - if(self->iOMPHI > 0) { - if(ABS(fSet[2] - .0) < .1 || ABS(fSet[2] - 180.) < .1){ - fSet[1] -= fSet[3]; - /* - fSet[1] = 360. - fSet[3]; - */ - fSet[3] = .0; - if(fSet[1] < 0.){ - fSet[1] += 360.; - } - if(fSet[1] > 360.0){ - fSet[1] -= 360.; - } - } else { - snprintf(pBueffel,511, - "ERROR: for omphi mode chi must be 0 or 180, is %f", - fSet[2]); - SCWrite(pCon,pBueffel,eError); - return 0; - } - } - } + } else if(self->iNOR == 1) { status = calculateNormalBeam(z1,self,pCon,fSet, myPsi, iRetry); @@ -1302,6 +1299,9 @@ ente: double z1[3]; int i; + if(self->UBinv == NULL){ + return 0; + } z1FromAngles(self->fLambda,tth,om,chi,phi,z1); z1m = vectorToMatrix(z1); /* multiply with UBinv in order to yield HKL */ @@ -1414,8 +1414,8 @@ ente: if(strcmp(argv[1],"list") == 0 ) { sprintf(pBueffel, - "lambda = %f Normal Beam = %d PHIOM = %d Quadrant = %d HM = %d", - self->fLambda, self->iNOR, self->iOMPHI, + "lambda = %f Normal Beam = %d Quadrant = %d HM = %d", + self->fLambda, self->iNOR, self->iQuad,self->iHM); SCWrite(pCon,pBueffel,eValue); sprintf(pBueffel,"UB = { %f %f %f", @@ -1654,29 +1654,6 @@ ente: SCSendOK(pCon); return 1; } -/*------------- phi omega mode (to be removed) */ - else if(strcmp(argv[1],"phiom") == 0) - { - if(argc < 3) - { - snprintf(pBueffel,511,"%s.phiom = %d",argv[0],self->iOMPHI); - SCWrite(pCon,pBueffel,eValue); - return 1; - } - if(!SCMatchRights(pCon,usUser)) - { - return 0; - } - if(!isNumeric(argv[2])) - { - sprintf(pBueffel,"ERROR: %s was not recognized as a number", argv[2]); - SCWrite(pCon,pBueffel,eError); - return 0; - } - self->iOMPHI = atoi(argv[2]); - SCSendOK(pCon); - return 1; - } /*------------- quadrant */ else if(strcmp(argv[1],"quadrant") == 0) { diff --git a/hkl.i b/hkl.i index 9665a544..14cbaed0 100644 --- a/hkl.i +++ b/hkl.i @@ -15,7 +15,6 @@ int iManual; double fLastHKL[5]; int iNOR; - int iOMPHI; int iQuad; int iHM; pMotor pTheta; diff --git a/hkl.tex b/hkl.tex index e62e69e1..bed73427 100644 --- a/hkl.tex +++ b/hkl.tex @@ -26,7 +26,6 @@ $\langle$hkldat {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ int iManual;@\\ \mbox{}\verb@ double fLastHKL[5];@\\ \mbox{}\verb@ int iNOR;@\\ -\mbox{}\verb@ int iOMPHI;@\\ \mbox{}\verb@ int iQuad;@\\ \mbox{}\verb@ int iHM;@\\ \mbox{}\verb@ pMotor pTheta;@\\ diff --git a/hkl.w b/hkl.w index e22bb92c..a1b510d3 100644 --- a/hkl.w +++ b/hkl.w @@ -21,7 +21,6 @@ The object uses the following object data structure: int iManual; double fLastHKL[5]; int iNOR; - int iOMPHI; int iQuad; int iHM; pMotor pTheta; diff --git a/hmcontrol.c b/hmcontrol.c index 198fbc72..758ed638 100644 --- a/hmcontrol.c +++ b/hmcontrol.c @@ -310,7 +310,7 @@ int HMControlAction(SConnection *pCon, SicsInterp *pSics, assert(self); if(argc < 4) { - sprintf("ERROR: Usage %s start preset mode", argv[0]); + snprintf(pBueffel,131,"ERROR: Usage %s start preset mode", argv[0]); SCWrite(pCon,pBueffel,eError); return 0; } diff --git a/hmdata.c b/hmdata.c index b9201f66..627fae56 100644 --- a/hmdata.c +++ b/hmdata.c @@ -5,17 +5,23 @@ copyright: see file COPYRIGHT Mark Koennecke, January 2003 + + Added loading HM data from file, Mark Koennecke, November 2006 -------------------------------------------------------------------------*/ #include +#include #include #include #include +#include "splitter.h" #include "fortify.h" #include "hmdata.h" +#include #include "HistMem.h" #include "HistMem.i" #include "HistDriv.i" #include "countdriv.h" +#include "stptok.h" /*----------------------------------------------------------------------*/ pHMdata makeHMData(void) { pHMdata self = NULL; @@ -46,12 +52,12 @@ void clearHMData(pHMdata self){ size *= self->iDim[i]; } if(self->tofMode){ - size *= self->nTimeChan; + size *= getNoOfTimebins(self); } memset(self->localBuffer,0,size*sizeof(HistInt)); } /*----------------------------------------------------------------------*/ -static int resizeBuffer(pHMdata self){ +int resizeBuffer(pHMdata self){ long size; int i; @@ -60,7 +66,7 @@ static int resizeBuffer(pHMdata self){ size *= self->iDim[i]; } if(self->tofMode){ - size *= self->nTimeChan; + size *= getNoOfTimebins(self); } if(self->localBuffer != NULL){ free(self->localBuffer); @@ -80,6 +86,7 @@ int configureHMdata(pHMdata self, pStringDict pOpt, int status, i; float fVal; char pValue[80]; + pHistMem master = NULL; if(self->nTimeChan > 2) { self->tofMode = 1; @@ -111,6 +118,18 @@ int configureHMdata(pHMdata self, pStringDict pOpt, self->updateIntervall = (int)rint(fVal); } + status = StringDictGet(pOpt,"timeslave",pValue, 79); + if(status == 1) { + master = (pHistMem)FindCommandData(pServ->pSics,pValue,"HistMem"); + if(master == NULL){ + SCWrite(pCon,"ERROR: timeslave requested, but master HM not found", + eError); + } else { + self->timeslave = master->pDriv->data; + self->tofMode = 1; + } + } + /* invalidate buffer */ @@ -138,7 +157,7 @@ int configureHMdata(pHMdata self, pStringDict pOpt, int genTimeBinning(pHMdata self, float start, float step, int noSteps){ int i; - if(noSteps >= MAXCHAN){ + if(noSteps >= MAXCHAN || self->timeslave != NULL){ return 0; } for(i = 0; i < noSteps; i++){ @@ -150,6 +169,10 @@ int genTimeBinning(pHMdata self, float start, float step, int noSteps){ } /*----------------------------------------------------------------------*/ int setTimeBin(pHMdata self, int index, float value){ + if(self->timeslave != NULL){ + return 0; + } + if(index >= 0 && index < MAXCHAN){ self->timeBinning[index] = value; } else { @@ -157,7 +180,7 @@ int setTimeBin(pHMdata self, int index, float value){ } self->tofMode = 1; if(index > self->nTimeChan){ - self->nTimeChan = index; + self->nTimeChan = index+1; return resizeBuffer(self); } return 1; @@ -168,17 +191,27 @@ int isInTOFMode(pHMdata self){ } /*---------------------------------------------------------------------*/ int getNoOfTimebins(pHMdata self){ - return self->nTimeChan; + if(self->timeslave != NULL){ + return getNoOfTimebins(self->timeslave); + } else { + return self->nTimeChan; + } } /*---------------------------------------------------------------------*/ float *getTimeBinning(pHMdata self){ - return self->timeBinning; + if(self->timeslave != NULL){ + return getTimeBinning(self->timeslave); + } else { + return self->timeBinning; + } } /*-------------------------------------------------------------------*/ void clearTimeBinning(pHMdata self){ - self->nTimeChan = 1; - self->tofMode = 0; - resizeBuffer(self); + if(self->timeslave == NULL){ + self->nTimeChan = 1; + self->tofMode = 0; + resizeBuffer(self); + } } /*--------------------------------------------------------------------*/ void getHMDataDim(pHMdata self, int iDim[MAXDIM], int *rank){ @@ -193,7 +226,7 @@ long getHMDataLength(pHMdata self){ length *= self->iDim[i]; } if(self->tofMode){ - length *= self->nTimeChan; + length *= getNoOfTimebins(self); } return length; } @@ -227,6 +260,10 @@ static int updateHMbuffer(pHistMem hist, int bank, SConnection *pCon){ assert(self); + if(self->timeslave != NULL){ + resizeBuffer(self); + } + for(i = 0; i < 3; i++){ status = hist->pDriv->GetHistogram(hist->pDriv,pCon, bank,0,getHMDataLength(self), @@ -292,7 +329,7 @@ HistInt *getHMDataBufferPointer(pHistMem hist,SConnection *pCon){ assert(self); - if(self->localBuffer == NULL){ + if(self->localBuffer == NULL || self->timeslave != NULL){ resizeBuffer(self); } /* @@ -327,7 +364,7 @@ long sumHMDataRectangle(pHistMem hist, SConnection *pCon, int iStart[MAXDIM], int iEnd[MAXDIM]) { HistInt *iData; pHMdata self = hist->pDriv->data; - int i, iHistLength, status, iIndex; + int i, iHistLength, status, iIndex, myrank; char pBueffel[256]; unsigned long lSum, lRowSum; @@ -336,7 +373,12 @@ long sumHMDataRectangle(pHistMem hist, SConnection *pCon, /* error checking */ - for(i = 0; i < self->rank; i++){ + myrank = self->rank; + if(isInTOFMode(self)){ + self->iDim[self->rank] = getNoOfTimebins(self); + myrank++; + } + for(i = 0; i < myrank; i++){ if( (iStart[i] < 0) || (iStart[i] > self->iDim[i]) ) { sprintf(pBueffel,"ERROR: %d is out of data dimension range", iStart[i]); @@ -367,25 +409,35 @@ long sumHMDataRectangle(pHistMem hist, SConnection *pCon, iHistLength = getHMDataLength(self); /* actually sum */ - switch(self->rank) + switch(myrank) { case 1: lSum = SumRow(self->localBuffer, iHistLength, iStart[0], iEnd[0]); break; case 2: - lSum = 0; - for(i = iStart[0]; i < iEnd[0]; i++){ - iIndex = i*self->iDim[1]; - lRowSum = SumRow(self->localBuffer,iHistLength, + if(isInTOFMode(self)){ + lSum = 0; + for(i = iStart[0]; i < iEnd[0]; i++){ + iIndex = i*self->iDim[1]; + lRowSum = SumRow(self->localBuffer,iHistLength, iIndex+iStart[1], iIndex+iEnd[1]); - lSum += lRowSum; + lSum += lRowSum; + } + } else { + lSum = 0; + for(i = iStart[1]; i < iEnd[1]; i++){ + iIndex = i*self->iDim[0]; + lRowSum = SumRow(self->localBuffer,iHistLength, + iIndex+iStart[0], iIndex+iEnd[0]); + lSum += lRowSum; + } } break; default: sprintf(pBueffel, "ERROR: summing in %d dimensions not yet implemented", - self->rank); + myrank); SCWrite(pCon,pBueffel,eError); return -1; break; @@ -395,3 +447,194 @@ long sumHMDataRectangle(pHistMem hist, SConnection *pCon, } return lSum; } +/*--------------------------------------------------------------------------*/ +int loadHMData(pHMdata self, SConnection *pCon, char *filename){ + FILE *fd = NULL; + char buffer[1024], pNumber[80], *pPtr; + long i = 0, length; + HistInt *data = NULL; + + fd = fopen(filename,"r"); + if(fd == NULL){ + snprintf(buffer,1023,"ERROR: failed to open file %s", filename); + SCWrite(pCon,buffer,eError); + return 0; + } + length = getHMDataLength(self); + if(self->localBuffer == NULL || self->timeslave != NULL){ + resizeBuffer(self); + } + data = self->localBuffer; + if(data == NULL){ + SCWrite(pCon,"ERROR: failed to allocate HM", eError); + fclose(fd); + return 0; + } + while(i < length && fgets(buffer,1024,fd) != NULL){ + pPtr = buffer; + while(pPtr != NULL){ + pPtr = sicsNextNumber(pPtr,pNumber); + if(pPtr != NULL){ + data[i] = atoi(pNumber); + i++; + } + } + } + if(i < length-1){ + SCWrite(pCon,"WARNING: not enough data in file to fill HM",eWarning); + } + fclose(fd); + return 1; +} +/*========================================================================== + * subsampling was stolen from the SinqHTTP histogram memory code and + * thus contains some additional indirections. + * =========================================================================*/ +static pNXDS hmDataToNXDataset(pHMdata self){ + pNXDS result = NULL; + int i; + + result = malloc(sizeof(NXDS)); + if(result == NULL){ + return NULL; + } + memset(result,0,sizeof(NXDS)); + result->magic = MAGIC; + result->type = NX_INT32; + result->rank = self->rank; + if(isInTOFMode(self)){ + result->rank++; + } + result->dim = malloc(self->rank*sizeof(int)); + if(result->dim == NULL){ + free(result); + return NULL; + } + for(i = 0; i < self->rank; i++){ + result->dim[i] = self->iDim[i]; + } + if(isInTOFMode(self)){ + result->dim[result->rank-1] = getNoOfTimebins(self); + } + if(self->localBuffer == NULL){ + resizeBuffer(self); + } + result->u.iPtr = self->localBuffer; + return result; +} +/*---------------------------------------------------------------------------*/ +static pNXDS subSampleCommand(pNXDS source, char *command, + char *error, int errLen){ + int startDim[NX_MAXRANK], endDim[NX_MAXRANK]; + int dim = 0, start = 0, i; + char *pPtr = NULL, token[80]; + + + pPtr = stptok(command,token,79,":\0"); + while((pPtr = stptok(pPtr,token,79,":\0")) != NULL){ + if(start == 0){ + startDim[dim] = atoi(token); + start = 1; + } else { + endDim[dim] = atoi(token); + start = 0; + dim++; + } + } + + if(dim < source->rank - 1){ + strncpy(error,"ERROR: Not enough border values specified for subsampling",errLen); + return NULL; + } + for(i = 0; i < source->rank; i++){ + if(startDim[i] < 0 || startDim[i] >= source->dim[i]){ + snprintf(error,errLen,"ERROR: invalid start value %d for dimension %d", startDim[1], i); + return NULL; + } + if(endDim[i] < startDim[i] || endDim[i] >= source->dim[i]){ + snprintf(error,errLen,"ERROR: invalid end value %d for dimension %d", endDim[1], i); + return NULL; + } + } + + return cutNXDataset(source,startDim,endDim); +} +/*-----------------------------------------------------------------------------*/ +static pNXDS sumCommand(pNXDS source, char *command, char *error, int errlen){ + int dimNo = -1, start = -1, end = -1; + char *pPtr = NULL; + char token[80]; + + pPtr = stptok(command,token,79,":\0"); + pPtr = stptok(pPtr,token,79,":\0"); + if(pPtr != NULL){ + dimNo = atoi(token); + } + pPtr = stptok(pPtr,token,79,":\0"); + if(pPtr != NULL){ + start = atoi(token); + } + pPtr = stptok(pPtr,token,79,":\0"); + if(pPtr != NULL){ + end = atoi(token); + } + if(dimNo < 0 || dimNo > source->rank - 1){ + snprintf(error,errlen,"ERROR: invalid dimension %d requestd to sum", dimNo); + return NULL; + } + if(end < 0 || end > source->dim[dimNo] || start < 0 || start > end){ + snprintf(error,errlen,"ERROR: invalid summing limits %d to %d requested", start,end); + return NULL; + } + + return sumNXDataset(source,dimNo, start, end); + } +/*--------------------------------------------------------------------------*/ +HistInt *subSample(pHMdata self, char *command, + char *error, int errLen){ + pNXDS source = NULL, start = NULL; + pNXDS result = NULL; + char *pPtr = NULL; + char subCommand[132]; + HistInt *data = NULL; + int length; + + + start = hmDataToNXDataset(self); + if(start == NULL){ + strncpy(error,"Out-Of-Memory or no data while subsampling ", + errLen); + return NULL; + } + + source = start; + pPtr = command; + while((pPtr = stptok(pPtr,subCommand,131,";\0\r\n")) != NULL){ + if(strstr(subCommand,"sample") != NULL){ + result = subSampleCommand(source,subCommand,error, errLen); + } else if(strstr(subCommand,"sum") != NULL){ + result = sumCommand(source,subCommand,error, errLen); + } else { + strncpy(error,"ERROR: invalid subcommand to process requested",errLen); + return NULL; + } + if(result == NULL){ + return NULL; + } + if(source != start){ + dropNXDataset(source); + } + source = result; + } + length = getNXDatasetLength(result); + data = malloc((length+1)*sizeof(int)); + if(data == NULL){ + strncpy(error,"Out-Of-Mmeory in Subsample", errLen); + dropNXDataset(result); + return NULL; + } + data[0] = length; + memcpy(data+1,result->u.iPtr, length*sizeof(int)); + dropNXDataset(result); + return data; +} diff --git a/hmdata.h b/hmdata.h index c7f9f097..ebacd051 100644 --- a/hmdata.h +++ b/hmdata.h @@ -16,7 +16,7 @@ #define MAXDIM 3 - typedef struct { + typedef struct __hmdata{ int rank; int iDim[MAXDIM]; int nTimeChan; @@ -26,6 +26,7 @@ int updateIntervall; int updateFlag; HistInt *localBuffer; + struct __hmdata *timeslave; } HMdata, *pHMdata; @@ -36,6 +37,7 @@ int configureHMdata(pHMdata self, pStringDict pOpt, SConnection *pCon); + int resizeBuffer(pHMdata self); int genTimeBinning(pHMdata self, float start, float step, int noSteps); int setTimeBin(pHMdata self, int index, float value); @@ -56,7 +58,10 @@ long sumHMDataRectangle(pHistMem self, SConnection *pCon, int start[MAXDIM], int end[MAXDIM]); + int loadHMData(pHMdata self, SConnection *pCon, char *filename); + HistInt *subSample(pHMdata self, char *command, + char *error, int errLen); #endif diff --git a/hmdata.w b/hmdata.w index bdce46fe..41716d4a 100644 --- a/hmdata.w +++ b/hmdata.w @@ -11,7 +11,7 @@ display clients gone mad. This task is also handled through this class. In order to do this, the following data structure is needed: @d hmdatadat @{ - typedef struct { + typedef struct __hmdata{ int rank; int iDim[MAXDIM]; int nTimeChan; @@ -21,6 +21,7 @@ In order to do this, the following data structure is needed: int updateIntervall; int updateFlag; HistInt *localBuffer; + struct __hmdata *timeslave; } HMdata, *pHMdata; @} @@ -32,6 +33,7 @@ The following functions work on this data structure: int configureHMdata(pHMdata self, pStringDict pOpt, SConnection *pCon); + int resizeBuffer(pHMdata self); int genTimeBinning(pHMdata self, float start, float step, int noSteps); int setTimeBin(pHMdata self, int index, float value); @@ -52,6 +54,7 @@ The following functions work on this data structure: long sumHMDataRectangle(pHistMem self, SConnection *pCon, int start[MAXDIM], int end[MAXDIM]); + int loadHMData(pHMdata self, SConnection *pCon, char *filename); @} \begin{description} @@ -83,6 +86,8 @@ the histogram memory and not from the buffer the next time round. pointer of HMdata. Use with extra care! \item[sumHMDataRectangle] sums a rectangular box delimted by start and end from the histogram memory. +\item[loadHMData] loads histogram memory data from a file. This is for + debugging purposes. The file must contain enough numbers to fill the HM. \end{description} diff --git a/hmslave.c b/hmslave.c new file mode 100644 index 00000000..0a59538f --- /dev/null +++ b/hmslave.c @@ -0,0 +1,293 @@ +/** + * This is a histogram memory driver for a slave histogram. This is for + * supporting multiple banks of histogram memory data in one physical + * histogram memory, possibly with different dimensions. On HM will connect + * to the physical histogram memory server and configure and control it and + * also handle bank 0. Other HM's may use this slave drive to to connect to + * the main HM for retrieving further banks. Thus this HM just becomes a + * data container for bank data. And this is the driver for such slave HM's. + * It mostly implements empty functions as control is through the main HM. The + * execption is data loading which will load the associated bank from the + * main HM. + * + * This is as of March 2007 defunct. The reason is that there are problems + * with buffering the data. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, March 2007 + */ +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + pHistMem master; + int bank; + }*HMSlave, sHMSlave; +/*------------------------------------------------------------------- + Configures the HM from the options in pOpt and the HM data structure + Returns 1 on success, 0 on failure +---------------------------------------------------------------------*/ +static int HMSlaveConfigure(pHistDriver self, SConnection *pCon, + pStringDict pOpt, SicsInterp *pSics){ + HMSlave pPriv = NULL; + char buffer[80], error[256]; + + pPriv =(HMSlave)self->pPriv; + + if(StringDictGet(pOpt,"master",buffer, 79) == 1){ + pPriv->master = (pHistMem)FindCommandData(pServ->pSics,buffer,"HistMem"); + if(pPriv->master == NULL){ + snprintf(error,255,"ERROR: failed to find master HM %s", buffer); + SCWrite(pCon,error,eError); + return 0; + } + } else { + SCWrite(pCon,"ERROR: required configuration option master missing", + eError); + return 0; + } + + if(StringDictGet(pOpt,"bank",buffer, 79) == 1){ + pPriv->bank = atoi(buffer); + } else { + SCWrite(pCon,"ERROR: required configuration option bank missing", + eError); + return 0; + } + + return 1; +} +/*-------------------------------------------------------------------- + Start histogramming, Returns HWFault on failure, 1 on success +----------------------------------------------------------------------*/ +static int HMSlaveStart(pHistDriver self,SConnection *pCon){ + HMSlave pPriv = NULL; + + pPriv =(HMSlave)self->pPriv; + return 1; +} +/*-------------------------------------------------------------------- + Stops histogramming, Returns HWFault on failure, 1 on success +----------------------------------------------------------------------*/ +static int HMSlaveHalt(pHistDriver self){ + HMSlave pPriv = NULL; + + pPriv =(HMSlave)self->pPriv; + return 1; +} +/*-------------------------------------------------------------------- + Checks histogramming status, Returns HWFault on failure, + HWIdle when finished, HWBusy when counting +----------------------------------------------------------------------*/ +static int HMSlaveCountStatus(pHistDriver self,SConnection *pCon){ + HMSlave pPriv = NULL; + + pPriv =(HMSlave)self->pPriv; + return HWIdle; +} +/*-------------------------------------------------------------------- + Get info on error after last HWFault, returns 1 always. + Puts an int error code into *code and errLen chars of + error description into error +----------------------------------------------------------------------*/ +static int HMSlaveGetError(pHistDriver self,int *code, + char *error, int errLen){ + HMSlave pPriv = NULL; + + pPriv =(HMSlave)self->pPriv; + strncpy(error,"Weird status: slaves do not err..",errLen); + *code = -77; + return 1; +} +/*-------------------------------------------------------------------- + Try to fix the HM error in code. Returns COREDO when the last + operation needs to be redone, COTERM when the error cannot be + fixed. +----------------------------------------------------------------------*/ +static int HMSlaveFixIt(pHistDriver self,int code){ + HMSlave pPriv = NULL; + + pPriv =(HMSlave)self->pPriv; + return COTERM; +} +/*-------------------------------------------------------------------- + GetData reads updates the internal cache of monitor values + from the hardware, Returns 1 or HWFault +----------------------------------------------------------------------*/ +static int HMSlaveGetData(pHistDriver self,SConnection *pCon){ + HMSlave pPriv = NULL; + + pPriv =(HMSlave)self->pPriv; + return 1; +} +/*-------------------------------------------------------------------- + GetMonitor reads the monitor value i. Returns either the monitor + value or -9999 if no such monitor exists or an error occurred +----------------------------------------------------------------------*/ +static long HMSlaveGetMonitor(pHistDriver self,int i, SConnection *pCon){ + HMSlave pPriv = NULL; + + pPriv =(HMSlave)self->pPriv; + return -9999; +} +/*-------------------------------------------------------------------- + GetTime reads the total counting time. Returns either the + value or -9999.99 if no such value exists or an error occurred +----------------------------------------------------------------------*/ +static float HMSlaveGetTime(pHistDriver self,SConnection *pCon){ + HMSlave pPriv = NULL; + + pPriv =(HMSlave)self->pPriv; + return -9999.99; +} +/*-------------------------------------------------------------------- + Pause histogramming, Returns HWFault on failure, 1 on success +----------------------------------------------------------------------*/ +static int HMSlavePause(pHistDriver self,SConnection *pCon){ + HMSlave pPriv = NULL; + + pPriv =(HMSlave)self->pPriv; + return 1; +} +/*-------------------------------------------------------------------- + Continue histogramming, Returns HWFault on failure, 1 on success +----------------------------------------------------------------------*/ +static int HMSlaveContinue(pHistDriver self,SConnection *pCon){ + HMSlave pPriv = NULL; + + pPriv =(HMSlave)self->pPriv; + return 1; +} +/*-------------------------------------------------------------------- + Free the data associated with the private data structure of the driver +----------------------------------------------------------------------*/ +static int HMSlaveFree(pHistDriver self){ + HMSlave pPriv = NULL; + + pPriv =(HMSlave)self->pPriv; + if(pPriv != NULL){ + free(pPriv); + } + self->pPriv = NULL; + return 1; +} +/*------------------------------------------------------------------- + * fixTimebinning assures that our time binning dn the masters + * time binning are the same. So that diemsnions are right + * ------------------------------------------------------------------*/ + static int fixTimeBinning(pHistDriver self, SConnection *pCon){ + HMSlave pPriv = NULL; + + pPriv =(HMSlave)self->pPriv; + if(isInTOFMode(pPriv->master->pDriv->data) && + getNoOfTimebins(pPriv->master->pDriv->data) != + getNoOfTimebins(self->data)){ + self->data->tofMode = 1; + self->data->nTimeChan = getNoOfTimebins(pPriv->master->pDriv->data); + if(!resizeBuffer(self->data)) { + SCWrite(pCon, + "ERROR: out of memory allocating HMData for slave", + eError); + return 0; + } + } + return 1; + } +/*-------------------------------------------------------------------- + Set The HM data or a subset of it. Returns HWFault or 1 +----------------------------------------------------------------------*/ +static int HMSlaveSetHistogram(pHistDriver self, + SConnection *pCon, + int i, int iStart, int iEnd, HistInt *pData){ + HMSlave pPriv = NULL; + HistInt *start = NULL; + + pPriv =(HMSlave)self->pPriv; + if(fixTimeBinning(self,pCon) == 1){ + start = self->data->localBuffer + iStart; + memcpy(start,pData,(iEnd - iStart)*sizeof(HistInt)); + return 1; + } else { + return 0; + } +} +/*-------------------------------------------------------------------- + Set HM to a preset value, Returns HWFault on failure, 1 on success +----------------------------------------------------------------------*/ +static int HMSlavePreset(pHistDriver self,SConnection *pCon, + HistInt value){ + HMSlave pPriv = NULL; + int i; + + pPriv =(HMSlave)self->pPriv; + if(fixTimeBinning(self,pCon) == 1){ + for(i = 0; i < getHMDataLength(self->data); i++){ + self->data->localBuffer[i] = value; + } + return 1; + } else { + return 0; + } +} +/*-------------------------------------------------------------------- + get The HM data or a subset of it. Returns HWFault or 1 +----------------------------------------------------------------------*/ +static int HMSlaveGetHistogram(pHistDriver self, + SConnection *pCon, + int i, int iStart, int iEnd, HistInt *pData){ + HMSlave pPriv = NULL; + + pPriv = (HMSlave)self->pPriv; + return pPriv->master->pDriv->GetHistogram(pPriv->master->pDriv, + pCon, pPriv->bank, iStart, iEnd, pData); +} +/*-------------------------------------------------------------------- + Make the HMDriver, returns a driver or NULL on failure +----------------------------------------------------------------------*/ +pHistDriver MakeHMSlaveHM(pStringDict pOption){ + pHistDriver pNew = NULL; + HMSlave pPriv = NULL; + + /* create the general driver */ + pNew = CreateHistDriver(pOption); + if(!pNew){ + return NULL; + } + + /*Create private data structure*/ + pPriv = (HMSlave)malloc(sizeof(sHMSlave)); + if(pPriv == NULL){ + return NULL; + } + pNew->pPriv = pPriv; + + /* add our options */ + StringDictAddPair(pOption,"master","unknown"); + StringDictAddPair(pOption,"bank","1"); + + /* configure all those functions */ + pNew->Configure = HMSlaveConfigure; + pNew->Start = HMSlaveStart; + pNew->Halt = HMSlaveHalt; + pNew->GetCountStatus = HMSlaveCountStatus; + pNew->GetError = HMSlaveGetError; + pNew->TryAndFixIt = HMSlaveFixIt; + pNew->GetData = HMSlaveGetData; + pNew->GetHistogram = HMSlaveGetHistogram; + pNew->SetHistogram = HMSlaveSetHistogram; + pNew->GetMonitor = HMSlaveGetMonitor; + pNew->GetTime = HMSlaveGetTime; + pNew->Preset = HMSlavePreset; + pNew->FreePrivate = HMSlaveFree; + pNew->Pause = HMSlavePause; + pNew->Continue = HMSlaveContinue; + + return pNew; +} diff --git a/ifile.c b/ifile.c index 458d41b0..04640f5e 100644 --- a/ifile.c +++ b/ifile.c @@ -49,11 +49,12 @@ static IPair *CreateNewEntry(char *name, char *val, IPair *pN) { - IPair *pRes; + IPair *pRes = NULL; pRes = (IPair *)malloc(sizeof(IPair)); if(!pRes) return NULL; + memset(pRes,0,sizeof(IPair)); if(name) pRes->name = strdup(name); if(val) diff --git a/initializer.c b/initializer.c index 397e0d4d..4c8ee024 100644 --- a/initializer.c +++ b/initializer.c @@ -9,11 +9,13 @@ Markus Zolliker, March 2005 #include "sics.h" #include "initializer.h" +#include "splitter.h" typedef struct Item { struct Item *next; - const char *type; - const char *name; + char *type; /* "Object" for all commands created by makeobject, else something more general */ + char *name; /* the name for identifying an initializer */ + char *desc; /* a description of the initializer. not the same as pObjectDescriptor->name */ Initializer maker; int startupOnly; } Item; @@ -21,23 +23,25 @@ typedef struct Item { static Item *list = NULL; static int startup = 1; -void MakeInitializer(const char *type, const char *name, Initializer maker, int startupOnly) { +void MakeInitializer(const char *type, const char *name, Initializer maker, + int startupOnly, const char *desc) { Item *item; item = calloc(1, sizeof *item); assert(item); item->maker = maker; item->next = list; - item->type = type; - item->name = name; + item->type = strdup(type); + item->name = strdup(name); + item->desc = strdup(desc); item->startupOnly = startupOnly; list = item; } Initializer GetInitializer(const char *type, const char *name) { Item *p, **last; - - if (startup && pServ->pReader != NULL) { + + if (startup && !ServerIsStarting(pServ)) { /* pServ->pReader exists: startup finished */ startup = 0; /* remove startup initializers */ @@ -54,12 +58,10 @@ Initializer GetInitializer(const char *type, const char *name) { } } } - p = list; - while (p != NULL && (strcasecmp(p->name, name) != 0 || strcasecmp(p->type, type) != 0)) { - p = p->next; - } - if (p) { - return p->maker; + for (p = list; p != NULL; p = p->next) { + if (strcasecmp(p->name, name) == 0 && strcasecmp(p->type, type) == 0) { + return p->maker; + } } return NULL; } @@ -67,43 +69,95 @@ Initializer GetInitializer(const char *type, const char *name) { static int MakeObject(SConnection *con, SicsInterp *sics, void *data, int argc, char *argv[]) { CmdInitializer cmdin; - CommandList *command; - char *className; if (argc < 3) { - SCPrintf(con, eError, "%s needs more arguments", argv[0]); + SCPrintf(con, eError, "ERROR: should be: %s ...", argv[0]); return 0; } cmdin = (CmdInitializer)GetInitializer("Object", argv[2]); if (cmdin) { - return cmdin(con, argc, argv, ! startup); + return cmdin(con, argc, argv, strcasecmp(argv[0],"makeobject") == 0); } else { SCPrintf(con, eError, "do not know how to make a %s object", argv[2]); return 0; } } +static int DriverList(SConnection *con, SicsInterp *sics, + void *data, int argc, char *argv[]) { + Item *p; + char *name, *type; + + if (argc < 2 || strcasecmp(argv[1], "list") == 0) { + for (p = list; p != NULL; p = p->next) { + if (argc < 3) { + SCPrintf(con, eStatus, "%s %s %s", p->type, p->name, p->desc); + } else if (strcasecmp(argv[2], p->type) == 0) { + SCPrintf(con, eStatus, "%s %s", p->name, p->desc); + } + } + } else { + if (argc == 2) { + name = argv[1]; + type = "Object"; + } else { + name = argv[2]; + type = argv[1]; + } + p = list; + while (p != NULL && (strcasecmp(p->type, type) != 0 || strcasecmp(p->name, name) != 0)) { + p = p->next; + } + if (p) { + SCPrintf(con, eValue, "%s", p->desc); + } else { + SCPrintf(con, eValue, "notfound"); + } + } + return 1; +} + static int RemoveObject(SConnection *con, SicsInterp *sics, void *data, int argc, char *argv[]) { CmdInitializer cmdin; - CommandList *command; + ObjectDescriptor *desc; char *className; + char shortClassName[32]; + char *p; + int removeAllowed; + char *creationCommand; if (argc != 2) { - SCPrintf(con, eError, "%s has 1 argument", argv[0]); + SCPrintf(con, eError, "ERROR: should be: %s ", argv[0]); return 0; } - command = FindCommand(sics, argv[1]); - if (!command) { + desc = FindCommandDescriptor(sics, argv[1]); + if (!desc) { SCPrintf(con, eError, "ERROR: %s not found", argv[1]); return 0; } - className = ((pDummy)command->pData)->pDescriptor->name; - cmdin = (CmdInitializer)GetInitializer("Object", className); - if (cmdin) { - /* if we have an initializer, we are allowed to remove */ + + creationCommand = GetDescriptorKey(desc, "creationCommand"); + if (creationCommand != NULL) { + /* if there is a creationCommand, we are allowed to remove */ + removeAllowed = 1; + } else { + /* if we have an initializer: we are also allowed to remove */ + className = desc->name; + cmdin = (CmdInitializer)GetInitializer("Object", className); + if (cmdin == 0) { + /* allow also a longer descriptor starting with the initializer name and a blank */ + p = strchr(className, ' '); + if (p) { + snprintf(shortClassName, sizeof shortClassName, "%.*s", p - className, className); + cmdin = (CmdInitializer)GetInitializer("Object", shortClassName); + } + } + removeAllowed = (cmdin != NULL); + } + if (removeAllowed) { if (pServ->pExecutor && isInRunMode(pServ->pExecutor)) { SCPrintf(con, eError, "ERROR: cannot remove %s while running", argv[1]); return 0; @@ -117,6 +171,82 @@ static int RemoveObject(SConnection *con, SicsInterp *sics, } } +typedef struct { + int printHeader; + FILE *fil; +} SaveData; + +static int SaveCreationCommand(char *name, pDummy object, void *userData) { + SaveData *saveData = userData; + char *creationCommand; + + creationCommand = GetDescriptorKey(object->pDescriptor, "creationCommand"); + if (creationCommand && strcmp(creationCommand, "0") != 0) { + if (saveData->printHeader == 0) { + saveData->printHeader = 1; + fprintf(saveData->fil, "\n#--- BEGIN creation commands\n"); + } + fprintf(saveData->fil, "%s\n", creationCommand); + } + return 1; +} + +static int SaveCreationCommands(void *object, char *name, FILE *fil) { + SaveData saveData; + + saveData.fil = fil; + saveData.printHeader = 0; + ForEachCommand(SaveCreationCommand, &saveData); + if (saveData.printHeader == 1) { + fprintf(fil, "#--- END creation commands\n\n"); + } + return 1; +} + +static int CreationCommand(SConnection *con, SicsInterp *sics, + void *data, int argc, char *argv[]) { + CmdInitializer cmdin; + char *className; + char shortClassName[32]; + char *p; + int removeAllowed; + ObjectDescriptor *desc; + char *creationCommand; + char buf[256]; + + if (argc < 2) { + SCPrintf(con, eError, "ERROR: should be: %s []", argv[0]); + return 0; + } + + desc = FindCommandDescriptor(sics, argv[1]); + if (!desc) { + SCPrintf(con, eError, "ERROR: %s not found", argv[1]); + return 0; + } + creationCommand = GetDescriptorKey(desc, "creationCommand"); + if (argc < 3) { + if (creationCommand != NULL) { + SCPrintf(con, eValue, "%s", creationCommand); + } else { + SCPrintf(con, eValue, ""); + } + } else { + if (!creationCommand) { + SCPrintf(con, eValue, "ERROR: %s is a static object", argv[1]); + return 0; + } + creationCommand = Arg2Tcl(argc - 2, argv + 2, buf, sizeof buf); + if (creationCommand) { + SetDescriptorKey(desc, "creationCommand", creationCommand); + if (creationCommand != buf) free(creationCommand); + } else { + SetDescriptorKey(desc, "creationCommand", "0"); + } + } + return 1; +} + static void KillInitializers(void *data) { KillDummy(data); Item *item, *next; @@ -124,16 +254,27 @@ static void KillInitializers(void *data) { item = list; while (item) { next = item->next; + if (item->name) free(item->name); + if (item->type) free(item->type); + if (item->desc) free(item->desc); free(item); item = next; } list = NULL; } -void MakeDriver(const char *driver, CmdInitializer maker, int startupOnly) { - if (! FindCommand(pServ->pSics, "MakeObject")) { - AddCommandWithFlag(pServ->pSics, "MakeObject", MakeObject, KillInitializers, NULL, 0); - AddCommandWithFlag(pServ->pSics, "RemoveObject", RemoveObject, NULL, NULL, 0); - } - MakeInitializer("Object", driver, (Initializer)maker, startupOnly); +void MakeDriver(const char *driver, CmdInitializer maker, int startupOnly, const char *desc) { + MakeInitializer("Object", driver, (Initializer)maker, startupOnly, desc); +} + +void InitializerInit(void) { + pDummy cc = NULL; + AddCommandWithFlag(pServ->pSics, "MakeObject", MakeObject, KillInitializers, NULL, 0); + AddCommandWithFlag(pServ->pSics, "MakeStaticObject", MakeObject, NULL, NULL, 0); + AddCommandWithFlag(pServ->pSics, "RemoveObject", RemoveObject, NULL, NULL, 0); + AddCommandWithFlag(pServ->pSics, "DriverList", DriverList, NULL, NULL, 0); + cc = CreateDummy("creation commands"); + cc->pDescriptor->SaveStatus = SaveCreationCommands; + AddCommandWithFlag(pServ->pSics, "CreationCommand", CreationCommand, + KillDummy, cc, 0); } diff --git a/initializer.h b/initializer.h index dcd3820c..036a15a6 100644 --- a/initializer.h +++ b/initializer.h @@ -12,7 +12,8 @@ Markus Zolliker, March 2005 typedef void (*Initializer)(void); -void MakeInitializer(const char *type, const char *name, Initializer maker, int startupOnly); +void MakeInitializer(const char *type, const char *name, Initializer maker, int startupOnly, + const char *desc); /* install an initializer @@ -32,16 +33,19 @@ Initializer GetInitializer(const char *type, const char *name); type. */ +/* static int MakeObject(SConnection *con, SicsInterp *sics, void *data, int argc, char *argv[]); -/* + MakeObject has the following syntax: MakeObject objectName driver [ args ... ] It executes the initializer with the type "Object" and and the driver given as name. The found initalizer should use the given arguments - to create a driver. + to create a driver. Objects should be dynamic, i.e. there should be + a creation command in the status file, except when the creation command + was MakeStaticObject instead of MakeObject. */ @@ -51,10 +55,12 @@ typedef int (*CmdInitializer) (SConnection *pCon, int argc, char *argv[], int dy - pCon: the sics connection calling this initializer - argc: the total number of args - argv: the args (argv[0]: "MakeObject", argv[1]: object name, argv[3]: driver ...) - - dynamic: the initializer was called _after_ startup + - dynamic: 1, if the creation command was MakeObject, + 0, if the creation command was MakeStaticObject */ -void MakeDriver(const char *driver, CmdInitializer maker, int startupOnly); +void MakeDriver(const char *driver, CmdInitializer maker, int startupOnly, + const char *desc); /* Install a driver of type "Object" with the initializer function maker. - startupOnly: the driver creation should only be possible at startup diff --git a/lin2ang.c b/lin2ang.c index 671e5838..0c6de052 100644 --- a/lin2ang.c +++ b/lin2ang.c @@ -34,7 +34,7 @@ /*-------------------------- conversion routines -------------------------*/ static float ang2x(pLin2Ang self, float fAngle) { - return self->length*sin((fAngle+self->zero)/RD); + return self->length*tan((fAngle+self->zero)/RD); } /*-----------------------------------------------------------------------*/ static float x2ang(pLin2Ang self, float fX) @@ -44,7 +44,7 @@ assert(self->length > 0.); dt = fX/self->length; - return RD*asin(dt) - self->zero; + return RD*atan(dt) - self->zero; } /*============== functions in the interface ============================*/ static void *Lin2AngGetInterface(void *pData, int iID) @@ -70,7 +70,7 @@ return 0; fprintf(fd,"%s length %f\n",name, self->length); - fprintf(fd,"%s zero %f\n",name, self->zero); + fprintf(fd,"%s softzero %f\n",name, self->zero); return 1; } @@ -275,20 +275,20 @@ } } /* zero point */ - if(strcmp(argv[1],"zero") == 0) + if(strcmp(argv[1],"softzero") == 0) { if(argc >= 3) { iRet = Tcl_GetDouble(pSics->pTcl,argv[2],&dVal); if(iRet != TCL_OK) { - SCWrite(pCon,"ERROR: zero parameter not recognised as number", + SCWrite(pCon,"ERROR: softzero parameter not recognised as number", eError); return 0; } if(!SCMatchRights(pCon,usUser)) { - SCWrite(pCon,"ERROR: Insufficient privilege to change zero point", + SCWrite(pCon,"ERROR: Insufficient privilege to change softzero point", eError); return 0; } @@ -298,7 +298,7 @@ } else { - sprintf(pBueffel,"%s.zero = %f",argv[0],self->zero); + sprintf(pBueffel,"%s.softzero = %f",argv[0],self->zero); SCWrite(pCon,pBueffel,eValue); return 1; } diff --git a/linux_def b/linux_def index ea98bcdb..4feb8a3b 100644 --- a/linux_def +++ b/linux_def @@ -9,4 +9,4 @@ MFLAGS=-f makefile_linux$(DUMMY) -HDFROOT=/afs/psi.ch/project/sinq/sl-linux +HDFROOT=/usr/local diff --git a/lld.c b/lld.c index 819bdbad..0c6ce124 100644 --- a/lld.c +++ b/lld.c @@ -208,6 +208,10 @@ void LLDdelete( int List ) assert( (unsigned) List < ListCount ); + if(ListControl == NULL) + { + return; + } Tmp = ListControl[ List ].first ; /* dummies are also deleted !!! */ while( NULL != Tmp ) /* still assuming last node has */ { /* a NULL next pointer ... */ diff --git a/lld_blob.c b/lld_blob.c index 1db5bafc..99e560bb 100644 --- a/lld_blob.c +++ b/lld_blob.c @@ -28,6 +28,21 @@ int LLDblobCreate( void ) return LLDcreate( sizeof( struct BlobDesc )); } +/*---------------------------------------------------------------------*/ +int LLDdeleteBlob(int List) +{ + struct BlobDesc Blob ; + int status; + + status = LLDnodePtr2First(List); + while(status == 1){ + LLDnodeDataTo( List, & Blob ); + free(Blob.data); + status = LLDnodePtr2Next(List); + } + LLDdelete(List); + return 1; +} /* ---- LL blob node mangement ---------------------------------------- */ int LLDblobInsert( int List, void * Source, unsigned Size ) diff --git a/lld_blob.h b/lld_blob.h index 56386eb8..56ceab06 100644 --- a/lld_blob.h +++ b/lld_blob.h @@ -20,7 +20,8 @@ int LLDblobCreate( void ); /* returns list number to use or -1 on failure. */ /* MUST be called before using a list of blobs. */ - +int LLDdeleteBlob(int List); + /* deletes a list and all its data */ /* ---- Node management -------------------------------------------------- Functions changing current node pointer to the new node. A return value of -1 indicates a memory allocation problem. diff --git a/logger.c b/logger.c new file mode 100644 index 00000000..1f9eed5f --- /dev/null +++ b/logger.c @@ -0,0 +1,369 @@ +/*--------------------------------------------------------------------------- +logger.c + +Markus Zolliker, Sept 2004 +---------------------------------------------------------------------------- +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "logger.h" + +struct Logger { + char *name; + char *old; + int oldsize; + int period; + time_t last, lastWrite; + int numeric; + int exact; + Logger *next; +}; + +static char *dir = NULL; +static Logger *list; +static time_t lastLife = 0; +static time_t lastWritten = 0; +/*--------------------------------------------------------------------------*/ +char *LoggerName(Logger *log) { + return log->name; +} +/*--------------------------------------------------------------------------*/ +void LoggerSetNumeric(Logger *log, int numeric) { + if (log) { + log->numeric = numeric; + } +} +/*--------------------------------------------------------------------------*/ +Logger *LoggerFind(const char *name) { + Logger *p; + p = list; + while (p != NULL) { + if (0==strcasecmp(name, p->name)) { + return p; + } + p = p->next; + } + return NULL; +} +/*--------------------------------------------------------------------------*/ +#define LASTLOGTXT "#last logging entry at:\n" + +time_t LoggerGetLastLife(char *dirarg) { + char path[256], line[32]; + FILE *fil; + time_t t = 0; + + if (dirarg == NULL) { + return lastWritten; + } + snprintf(path, sizeof path, "%s/lastlife.dat", dirarg); + fil = fopen(path, "r"); + if (fil) { + fgets(line, sizeof line, fil); + if (strcmp(line, LASTLOGTXT) == 0) { + fgets(line, sizeof line, fil); + t = atol(line); + if (t < 1000000000) { + printf("bad lastLife %ld\n", (long)t); + } + } + fclose(fil); + } else { + /* printf("can not read %s\n", path); */ + } + return t; +} + +/*--------------------------------------------------------------------------*/ +time_t LoggerSetDir(char *dirarg) { + dir = dirarg; + lastLife = LoggerGetLastLife(dir); + return lastLife; +} +/*--------------------------------------------------------------------------*/ +char *LoggerGetDir(void) { + char path[256]; + FILE *fil; + static time_t last; + + lastWritten = time(NULL); + if (lastWritten != last) { /* do not write more than once per second */ + snprintf(path, sizeof path, "%s/lastlife.dat", dir); + fil = fopen(path, "w"); + if (fil) { + fprintf(fil, "%s%ld\n", LASTLOGTXT, (long)lastWritten); + fclose(fil); + } else { + printf("can not write %s\n", path); + } + last = lastWritten; + } + return dir; +} +/*--------------------------------------------------------------------------*/ +int LoggerVarPath(char *dir, char *path, int pathLen, char *name, struct tm *t) { + int l; + + l = strlen(dir); + if (l + strlen(name) + 8 >= pathLen) { + path[0]='\0'; + return 0; + } + strcpy(path, dir); + strftime(path + l, pathLen - l, "/%Y/", t); + l += 6; + for (;*name != '\0'; name++, l++) { + path[l] = tolower(*name); + } + path[l] = '/'; l++; + path[l] = '\0'; + return l; +} +/*--------------------------------------------------------------------------*/ +int LoggerWrite0(Logger *log, time_t now, int period, char *value) { + char path[256], stim[32], buf[32]; + struct tm tm, lasttm; + int l, ext, writeInfo; + FILE *fil; + time_t beforenow; + + LoggerGetDir(); + if (dir == NULL) return 0; + if (now == 0) { + printf("now==0\n"); + } + lasttm = *localtime(&log->last); + tm = *localtime(&now); + l = LoggerVarPath(dir, path, sizeof path, log->name, &tm); + + strftime(path + l, sizeof path - l, "%m-%d.log", &tm); + strftime(stim, sizeof stim, "#%Y-%m-%d %H:%M:%S", &tm); + + if (period <= 0) period = 1; + writeInfo = (tm.tm_isdst != lasttm.tm_isdst || + tm.tm_yday != lasttm.tm_yday || + (period != log->period && log->numeric)); + if (strcmp(value, log->old) != 0 || writeInfo) { + + fil = fopen(path, "r+"); + if (fil == NULL) { /* create new file */ + fil = fopen(path, "w+"); + if (fil == NULL) return 0; + fprintf(fil, "%s isdst %d period %d exact %d\n", stim, tm.tm_isdst, period, log->exact); + } else { /* check if file is from today */ + fgets(buf, sizeof buf, fil); + if (0 != strncmp(buf, stim, 11)) { + fclose(fil); /* it was file from an earlier year */ + fil=fopen(path, "w+"); /* overwrite old logfile */ + if (fil == NULL) return 0; + fprintf(fil, "%s isdst %d period %d exact %d\n", stim, tm.tm_isdst, period, log->exact); + } else { + fseek(fil, 0, SEEK_END); /* set position to end */ + if (writeInfo) { + fprintf(fil, "#isdst %d period %d exact %d\n", tm.tm_isdst, period, log->exact); + } + if (log->lastWrite != 0 && now >= log->lastWrite + 2 * period) { + /* this is only useful for direct access of the log files */ + beforenow = now - period; + lasttm = *localtime(&beforenow); + if (lasttm.tm_yday == tm.tm_yday) { + strftime(stim, sizeof stim,"%H:%M:%S", &lasttm); + } else { + snprintf(stim, sizeof stim, "00:00:00"); + } + fprintf(fil, "%s\t%s\n", stim, log->old); + } + } + } + strftime(stim, sizeof stim,"%H:%M:%S", &tm); + fprintf(fil, "%s\t%s\n", stim, value); + log->lastWrite = now; + fclose(fil); + + } + log->period = period; + + l = strlen(value); + if (l >= log->oldsize) { /* increase log->old size, optimized for linux/i386 */ + ext = ((l - log->oldsize)/16 + 1) * 16; + if (ext < log->oldsize / 4) ext += (log->oldsize / 64) * 16; + log->oldsize += ext; + free(log->old); + log->old = calloc(1, log->oldsize); + } + assert(log->old); + assert(l < log->oldsize); + strcpy(log->old, value); + assert(log->old[l] == '\0'); + log->last = now; + return 1; +} +/*--------------------------------------------------------------------------*/ +int LoggerWrite(Logger *log, time_t now, int period, char *value) { + Logger *p; + time_t h0; + static int yday = -1; + struct tm *tm; + + int l; + FILE *fil; + char path[256]; + char tim[256]; + + tm = localtime(&now); + if (tm->tm_yday != yday) { + tm->tm_hour = 0; + tm->tm_min = 0; + tm->tm_sec = 0; + h0 = mktime(tm); + + yday = tm->tm_yday; + + /* -- debug logging if dir/debug exists */ + l = LoggerVarPath(dir, path, sizeof path, "debug", tm); + strftime(path + l, sizeof path - l, "%m-%d.log", tm); + fil=fopen(path, "a"); + if (fil) { + strftime(tim, sizeof tim, "h0 %m-%d %H:%M:%S\n", localtime(&h0)); + fputs(tim, fil); + } + + /* log old values (forced midnight log) */ + p = list; + while (p != NULL) { + + if (fil) { /* debug logging */ + strftime(tim, sizeof tim, "last %m-%d %H:%M:%S, ", localtime(&p->last)); + fputs(tim, fil); + fprintf(fil, "period %d, name %s, old %s\n", p->period, p->name, p->old); + } + + if (p->last < h0 && p->last != 0 && + p->period >= 0 && p->old[0] != '\0') { + LoggerWrite0(p, h0, p->period, p->old); + } + p = p->next; + } + if (fil) fclose(fil); + } + return LoggerWrite0(log, now, period, value); +} +/*--------------------------------------------------------------------------*/ +time_t LoggerLastTime(Logger *log) { + if (log->last != 0 && log->period > 0) { + return log->last; + } + return 0; +} +/*--------------------------------------------------------------------------*/ +int LoggerPeriod(Logger *log) { + return log->period; +} +/*--------------------------------------------------------------------------*/ +void LoggerSetPeriod(Logger *log, int period) { + LoggerWrite0(log, time(NULL), period, log->old); +} +/*--------------------------------------------------------------------------*/ +void LoggerKill(Logger *log) { + /* we do not really free the logger, it might be reused + for the same variable later. We set the value to undefined */ + + if (list != NULL) { /* LoggerFreeAll not yet called */ + LoggerWrite(log, time(NULL), 0, ""); + } +} +/*--------------------------------------------------------------------------*/ +static int LoggerMakeDir(char *path) { + static char buffer[4096]; + struct stat st; + int i, lpath, l; + char *slash; + + i = stat(path, &st); + if (i >= 0) { + if (((st.st_mode >> 12) & 15) != 4) { /* exists, but is no directory */ + return 0; + } + return 1; + } + i = mkdir(path, S_IRWXU+S_IRGRP+S_IXGRP+S_IROTH+S_IXOTH); + if (i < 0) { + if (errno != ENOENT) return 0; /* mkdir failed */ + snprintf(buffer, sizeof buffer, "%s", path); + lpath = strlen(buffer); + do { + slash = strrchr(buffer, '/'); + if (!slash) return 0; + *slash='\0'; + i = mkdir(buffer, S_IRWXU+S_IRGRP+S_IXGRP+S_IROTH+S_IXOTH); + } while (i < 0 && errno == ENOENT); + l = strlen(buffer); + while (lname = strdup(name); + if (log->name == NULL) { + free(log); + return NULL; + } + log->period = -1; + log->exact = exact; + log->old = calloc(1,12); + log->oldsize = 12; + log->last = 0; + log->lastWrite = 0; + log->numeric = 1; + log->next = list; + list = log; + t = time(NULL) -1; + if (lastLife != 0 && lastLife + period < t) { + t = lastLife + period; + } + LoggerWrite(log, t, period, ""); /* value was undefined since last life of server */ + } + return log; +} +/*--------------------------------------------------------------------------*/ +void LoggerFreeAll(void) { + Logger *p, *next; + + p = list; + while (p != NULL) { + next = p->next; + if (p->name) free(p->name); + if (p->old) free(p->old); + free(p); + p = next; + } + list = NULL; +} diff --git a/logger.h b/logger.h new file mode 100644 index 00000000..276a5494 --- /dev/null +++ b/logger.h @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------- +logger.h + +Markus Zolliker, Sept 2004 +---------------------------------------------------------------------------- +*/ + +#ifndef LOGGER_H +#define LOGGER_H + +#include + +typedef struct Logger Logger; + +Logger *LoggerMake(char *name, int period, int exact); +void LoggerKill(Logger *log); +int LoggerWrite(Logger *log, time_t now, int period, char *value); +char *LoggerName(Logger *log); +void LoggerSetNumeric(Logger *log, int numeric); +time_t LoggerSetDir(char *dirarg); +time_t LoggerGetLastLife(char *dirarg); +void LoggerWriteOld(Logger *log, time_t now); +time_t LoggerLastTime(Logger *log); +int LoggerPeriod(Logger *log); +void LoggerSetPeriod(Logger *log, int period); +int LoggerVarPath(char *dir, char *path, int pathLen, char *name, struct tm *t); +void LoggerFreeAll(void); + +#endif diff --git a/logreader.c b/logreader.c new file mode 100644 index 00000000..cfa8b618 --- /dev/null +++ b/logreader.c @@ -0,0 +1,524 @@ +#include +#include +#include +#include +#include +#include +#include +#include "logger.h" +#include "sics.h" + +#define LOGGER_NAN -999999. +#define ONE_YEAR (366*24*3600) +#define LLEN 1024 +/* max. number of dirs in path */ +#define MAX_DIRS 16 + +typedef enum { NUMERIC, TEXT } CompType; + +typedef struct { + time_t t; + float y; +} Point; + +typedef struct { + SConnection *pCon; + int exact; + CompType type; + time_t step; + time_t tlim; /* 0: initial state */ + Point best; /* best point, to be written if tlim > 0 */ + Point written; /* last written point */ + Point last; /* last point */ + char buf[LLEN]; + int np; + char *none; + char *header; + time_t period; + int omitEqual; +} Compressor; + +static char *dirs[MAX_DIRS] = {NULL}; +static int ndirs=0; + +static void InitCompressor(Compressor *c, SConnection *pCon, time_t step) { + c->pCon = pCon; + c->step = step; + c->tlim = 0; + c->last.y = LOGGER_NAN; + c->last.t = 0; + c->buf[0]='\0'; + c->written.t = 0; + c->written.y = LOGGER_NAN; + c->omitEqual = 1; +} + +static void OutFloat(Compressor *c, Point p) { + char *value; + + if (p.y == LOGGER_NAN) { + if (c->omitEqual && c->written.y == LOGGER_NAN) return; + if (c->none) { + value = c->none; + } else { + value = ""; + } + } else { + if (c->omitEqual && c->written.y == p.y) return; + snprintf(c->buf, sizeof(c->buf), "%g", p.y); + value = c->buf; + } + SCPrintf(c->pCon, eWarning, "%ld %s", (long)(p.t - c->written.t), value); + c->written = p; + c->np--; +} + +static void WriteHeader(Compressor *c) { + if (c->header) { + SCPrintf(c->pCon, eWarning, "*%s period %ld\n", c->header, c->period); + c->header = NULL; + } +} + +static void PutValue(Compressor *c, time_t t, char *value) { + char *p; + Point new; + + WriteHeader(c); + if (c->type == NUMERIC) { + new.y = strtod(value, &p); + if (p == value) { + new.y = LOGGER_NAN; + } else { + if (new.y == LOGGER_NAN) new.y *= 1.0000002; + } + new.t = t; + if (t >= c->tlim) { /* a new interval starts */ + if (c->tlim == 0) { + c->tlim = t; + } else if (c->best.y != c->written.y) { + OutFloat(c, c->best); + } + c->best = new; + c->tlim += c->step; + if (t > c->tlim) { + if (c->last.y != c->written.y) { + OutFloat(c, c->last); + } + c->tlim = t + c->step; + } + } else { /* not the first point */ + if (fabs(new.y - c->best.y) > fabs(c->written.y - c->best.y)) { + c->best = new; + } + } + c->last = new; + } else if (c->type == TEXT) { + if (0 != strncmp(value, c->buf, sizeof(c->buf)-1)) { + snprintf(c->buf, sizeof(c->buf), "%s", value); + SCPrintf(c->pCon, eWarning, "%ld %s\n", (long)(t - c->written.t), value); + c->written.t = t; + c->np--; + } + } +} + +static void PutFinish(Compressor *c, time_t now) { + char value[32]; + + if (c->tlim != 0) { /* there is data already */ + c->omitEqual = 0; + if (c->type == NUMERIC) { + if (now > c->last.t + c->period) { /* copy last value to the actual time */ + if (c->last.y != LOGGER_NAN) { + snprintf(value, sizeof value, "%g", c->last.y); + PutValue(c, now, value); + } + if (c->best.t > c->written.t) { + OutFloat(c, c->best); /* write last buffered value */ + } + } + } if (now > c->last.t) { + PutValue(c, now, c->buf); + } + } +} +/*--------------------------------------------------------------------------*/ +static long getOpt(char *line, int *isdst, int *exact) { + long lxs; + char *opt; + + opt = strstr(line, "isdst"); + if (opt) { + sscanf(opt, "isdst %d", isdst); + } + opt = strstr(line, "exact"); + if (opt) { + sscanf(opt, "exact %d", exact); + } + opt = strstr(line, "period"); + if (opt) { + sscanf(opt, "period %ld", &lxs); + return lxs; + } else { + return -1; + } +} +/*--------------------------------------------------------------------------*/ +static int LogReader(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) { + /* Usage: + graph [ none ] np [ ...] + graph text + graph [ ...] + + and are seconds since epoch (unix time) or, if the value + is below one day, a time relative to the actual time + + The is optional. if not given, unknown values are returned as empty strings + + is the maximal number of points to be returned. If more values + are present and the values are numeric, the data is reduced. If the data is not + numeric, the last values may be skipped in order to avoid overflow. + + is the name of a variable (several vaiables may be given). + Note that slashes are converted to dots, and that the first slash is ignored. + + The seconds variant is for text values, which can not be reduced. In any case, all values + are returned. + + The third variant is old style and can be replaced by the first variant, where + = ( - ) / + 2 + + + Output format: + First line: returning the actual time on the server (this time is used for relative times) + + For each variable which has data in the given interval, + the variable name is returned preceeded by a '*', followed by some infos* separated with + blanks. + After the header line for every data point a line follows with + a time relative to the last point and the value. + The first time value relative to zero, e.g. absolute. + The data value is valid until the next datapoint. Empty values are returned as an + empty string or as the . + + At the very end * is returned. + is 1, when the data had to be reduced, 0 else + is 1, when overflow occured, 0 else. Overflow may happen only + when np is given and a text variable was demanded. + + *actually only one info exists: period . This is the update rate in seconds. + As equal values are not transmitted, two points (t1,y1) and (t2,y2) with a distance + (t2 - t1) > period should not be connected directly. The plot software should generate + an intermediate point at (t2-period,y1). + + */ + time_t from, to, step, xs, lastt; + char *p, *varp; + int i, j, iarg, pathLen, iret, loss, np; + int inRange; + int yday=0; + time_t t, startim; + struct tm tm; + char stim[32], path[LLEN], line[LLEN], lastval[LLEN]; + char *lin, *val, *stp; + FILE *fil; + Compressor c={0}; + float yy, lasty; + CompType type0; + DIR *dr; + char *opt; + int isdst; + int overflow; + time_t now, nows[MAX_DIRS], nowi; + char var[256]; + char dirPathBuffer[1024]; + char *dirPath; + int idir; + char *colon; + + /* argtolower(argc, argv); */ + if (argc < 4) goto illarg; + now = time(NULL); + from = strtol(argv[1], &p, 0); /* unix time, not year 2038 safe */ + if (p == argv[1]) goto illarg; + to = strtol(argv[2], &p, 0); + if (p == argv[2]) goto illarg; + if (from < ONE_YEAR) { + from += now; + } + if (to < ONE_YEAR) { + to += now; + } + iarg = 3; + while (1) { + if (iarg>=argc) goto illarg; + if (strcasecmp(argv[iarg],"text") == 0) { /* non-numeric values */ + iarg++; + step = 1; + type0 = TEXT; + np = to - from + 2; + break; + } else if (strcasecmp(argv[iarg],"none") == 0) { /* none */ + iarg++; + if (iarg >= argc) goto illarg; + c.none = argv[iarg]; + iarg++; + } else if (strcasecmp(argv[iarg],"np") == 0) { /* max. number of points */ + iarg++; + if (iarg >= argc) goto illarg; + type0 = NUMERIC; + np = strtol(argv[iarg], &p, 0); + if (p == argv[iarg]) goto illarg; + iarg++; + if (to <= from) { + step = 1; + } else if (np <= 2) { + step = to - from; + } else { + step = (to - from) / (np - 2) + 1; + } + break; + } else { + step = strtol(argv[iarg], &p, 0); + if (p == argv[iarg]) goto illarg; + iarg++; + if (step <= 0) step = 1; + type0 = NUMERIC; + np = (from - to) / step + 2; + } + } + if (step <= 0) step = 1; + + snprintf(line, sizeof line, "%ld\n", (long)now); + SCWrite(pCon, line, eWarning); + + dirPath = IFindOption(pSICSOptions, "LogReaderPath"); + if (dirPath == NULL) { /* for compatibility, check */ + dirs[0] = IFindOption(pSICSOptions, "LoggerDir"); + if (dirs[0] == NULL) { + SCWrite(pCon, "ERROR: LoggerPath not found", eError); + return 0; + } + nows[0] = LoggerGetLastLife(NULL); + if (dirs[1] == NULL) { + dirs[1] = IFindOption(pSICSOptions, "LoggerDir2"); + nows[1] = LoggerGetLastLife(dirs[1]); + } + } else { + snprintf(dirPathBuffer, sizeof dirPathBuffer, "%s", dirPath); + dirPath = dirPathBuffer; + for (ndirs = 0; ndirs < MAX_DIRS; ndirs++) { + dirs[ndirs] = dirPath; + colon = strchr(dirPath, ':'); + if (colon != NULL) { + *colon = '\0'; + } + if (ndirs == 0) { + nows[0] = LoggerGetLastLife(NULL); + } else { + nows[ndirs] = LoggerGetLastLife(dirPath); + } + if (colon == NULL) { + ndirs++; + break; + } + dirPath = colon + 1; + } + } + + loss = 0; + overflow = 0; + for (i=iarg; i= 0) { + if (c.period == 0) { + c.type = TEXT; + } else { + c.type = type0; + xs = c.period; + } + if (xs < step) { + loss = 1; + xs = step; + } + } + } + } + } + if (fil == NULL) { + lin = NULL; + } else { + do { + lin = fgets(line, sizeof line, fil); + /* printf("%s\n", line); */ + if (lin == NULL) break; + if (line[0] == '#') { + c.period = getOpt(line, &isdst, &c.exact); + if (c.period >= 0) { + if (c.period == 0) { + c.type = TEXT; + } else { + c.type = type0; + xs = c.period; + } + if (xs < step) { + loss = 1; + xs = step; + } + } + lin[0]='\0'; + } else { + p = strchr(line, '\n'); if (p) *p='\0'; + p = strchr(line, '#'); if (p) *p='\0'; + } + } while (lin[0] == '\0'); + } + if (lin != NULL) { + /* printf(" %s\n", line); */ + p = strchr(line, '\t'); + if (p) { + *p='\0'; + val = p+1; + } else { + val = ""; + } + p = strchr(val, '\t'); + if (p) { + stp = p+1; + *p='\0'; + iret = sscanf(stp, "%ld", &c.period); + if (iret == 1) { + if (c.period == 0) { + c.type = TEXT; + } else { + c.type = type0; + xs = c.period; + } + if (xs < step) { + loss = 1; + xs = step; + } + } + } + iret = sscanf(line, "%d:%d:%d", &tm.tm_hour, &tm.tm_min, &tm.tm_sec); + if (iret != 3) { + lin = NULL; + } else { + tm.tm_isdst = isdst; + t=mktime(&tm); + if (!inRange) { + if (t < startim) { + lastval[0]='\0'; + strncat(lastval, val, sizeof lastval - 1); + lastt = t; + } else { + inRange=1; + if (lastt != 0) { + PutValue(&c, lastt, lastval); + } + PutValue(&c, t, val); + } + } else { + PutValue(&c, t, val); + } + } + } + if (lin == NULL) { + tm.tm_hour = 24; /* try next day */ + tm.tm_min = 0; + tm.tm_sec = 0; + tm.tm_isdst = -1; + startim=mktime(&tm); + continue; + } + } + if (!inRange) { + if (lastt != 0) { + PutValue(&c, lastt, lastval); + } + } + WriteHeader(&c); /* write header, if not already done */ + PutFinish(&c, nowi); + if (fil) { + fclose(fil); + fil = NULL; + } + if (c.np < 0) overflow = 1; + } + snprintf(line, sizeof line, "*%d %d\n", loss, overflow); + SCWrite(pCon, line, eWarning); + return 1; +illarg: + SCWrite(pCon, "illegal argument(s)", eError); + return 0; +} +/*--------------------------------------------------------------------------*/ +static void KillLogReader(void *data) { + Logger *p, *next; + + KillDummy(data); + LoggerFreeAll(); +} +/*--------------------------------------------------------------------------*/ +void LogReaderInit(void) { + AddCommand(pServ->pSics,"Graph",LogReader,KillLogReader,NULL); +} diff --git a/logsetup.c b/logsetup.c new file mode 100644 index 00000000..46059917 --- /dev/null +++ b/logsetup.c @@ -0,0 +1,113 @@ +#include "logger.h" +#include "sics.h" +#include "sicshipadaba.h" + +static char *loggerID = "loggerID"; + +static hdbCallbackReturn LoggerUpdateCallback(pHdb node, + void *userData, pHdbMessage message) { + Logger *logger = userData; + pDynString str; + SConnection *conn = NULL; + hdbValue value; + pHdbDataMessage mm = NULL; + pHdbDataSearch dsm = NULL; + + if ((dsm = GetHdbDataSearchMessage(message)) != NULL) { + if (dsm->testPtr == loggerID) { + dsm->result = userData; + return hdbAbort; + } + return hdbContinue; + } + + if((mm = GetHdbUpdateMessage(message)) == NULL){ + return hdbContinue; + } + + value = *(mm->v); + + str = formatValue(value, node); + LoggerWrite(logger, time(NULL), LoggerPeriod(logger), GetCharArray(str)); + DeleteDynString(str); + return hdbContinue; +} + +static int LogSetup(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) { + pHdb node; + pHdbCallback cb; + static char basepath[1024]="/"; + char buf[1024]; + char *p, *name; + static char *loggerDir=NULL; + int numeric, period; + Logger *logger; + + if (argc < 2) { + SCPrintf(pCon, eError, "ERROR: should be: logsetup [ []]"); + return 0; + } + if (strcasecmp(argv[1], "basepath") == 0) { + if (argc > 2) { + snprintf(basepath, sizeof basepath, "%s", argv[2]); + } + SCPrintf(pCon, eValue, "%s", basepath); + return 1; + } + if (loggerDir == NULL) { + loggerDir = IFindOption(pSICSOptions, "LoggerDir"); + if (loggerDir == NULL) loggerDir="./"; + LoggerSetDir(loggerDir); + } + if (strcasecmp(argv[1], "directory") == 0) { + if (argc > 2) { + loggerDir = strdup(argv[2]); + } + SCPrintf(pCon, eValue, "%s", loggerDir); + return 1; + } + node = FindHdbNode(basepath, argv[1], pCon); + if (node == NULL) { + SCPrintf(pCon, eError, "ERROR: %s not found", argv[1]); + return 0; + } + period = 0; + if (argc > 2) { + period = atoi(argv[2]); + } + if (argc > 3) { + snprintf(buf, sizeof buf, "%s", argv[3]); + } else { + snprintf(buf, sizeof buf, "%s", argv[1]); + } + for (p = buf; *p != '\0'; p++) { + if (*p =='/') *p = '.'; + } + if (buf[0] == '.') { + name = buf+1; + } else { + name = buf; + } + if (node->value.dataType == HIPFLOAT) { + numeric = 1; + } else { + numeric = 0; + } + logger = FindHdbCallbackData(node, loggerID); + if (logger != 0) { /* logger exists already, changed only period */ + LoggerSetPeriod(logger, period); + } else { + logger = LoggerMake(name, period, !numeric); + LoggerSetNumeric(logger, numeric); + cb = MakeHipadabaCallback(LoggerUpdateCallback, logger, (void (*)(void *))LoggerKill); + assert(cb); + AppendHipadabaCallback(node, cb); + } + + return 1; +} + +void LogSetupInit(void) { + AddCmd("LogSetup",LogSetup); +} diff --git a/macosx_def b/macosx_def new file mode 100644 index 00000000..0085c42a --- /dev/null +++ b/macosx_def @@ -0,0 +1,13 @@ +#-------------------------------------------------------------------------- +# general defines used within the PSI makefile hierarchy +# +# Mark Koennecke, July 2003 +#------------------------------------------------------------------------- + +#DFORTIFY= -DFORTIFY +#FORTIFYOBJ= fortify.o strdup.o +#DFORTIFY= -pg + +MFLAGS=-f makefile_linux$(DUMMY) + +HDFROOT=/Users/Shared diff --git a/macro.c b/macro.c index 2fdb4f36..bc3a10e5 100644 --- a/macro.c +++ b/macro.c @@ -2,7 +2,7 @@ All you need to evaluate macros with SICS - The implmentation for the macro stuff is complex and non intuitive. + The implementation for the macro stuff is complex and non intuitive. This is the price to pay for adding the extremly powerful and strong Tcl-interpreter to SICS. The problem is that Tcl does not know anything about connections and our error handling. We have @@ -61,16 +61,16 @@ #include #include #include -#include "SCinter.h" -#include "conman.h" -#include "macro.h" +#include #include "status.h" -#include "obdes.h" +#include "macro.h" #include "splitter.h" #include "ifile.h" #include "Dbg.h" #include "servlog.h" #include "stringdict.h" +#include "exeman.h" +#include "nxcopy.h" #define SICSERROR "005567SICS" /*---------------------------------------------------------------------------- @@ -131,7 +131,8 @@ char *lastCommand = NULL, comBuffer[132]; int iRet = 0,i; int iMacro; - + Statistics *old; + /* get the datastructures */ pSics = (struct __SicsUnknown *)pData; assert(pSics); @@ -180,7 +181,9 @@ /* invoke */ iMacro = SCinMacro(pCon); SCsetMacro(pCon,1); + old=StatisticsBegin(pCommand->stat); iRet = pCommand->OFunc(pCon,pSinter,pCommand->pData,margc, myarg); + StatisticsEnd(old); SCsetMacro(pCon,iMacro); /* lastUnkown gets deeply stacked with each SICS command exec'd. @@ -299,7 +302,8 @@ static int ProtectedExec(ClientData clientData, Tcl_Interp *interp, /*-------------------------------------------------------------------------- initialises a Tcl-Interpreter, installs SICS unknown mechanism and kills a few dangerous commands from the normal Tcl command set -*/ +----------------------------------------------------------------------------*/ +extern int Nxinter_SafeInit(Tcl_Interp *pTcl); /* from Swig NeXus Tcl interface */ Tcl_Interp *MacroInit(SicsInterp *pSics) { @@ -343,6 +347,9 @@ static int ProtectedExec(ClientData clientData, Tcl_Interp *interp, */ Tcl_CreateObjCommand(pInter,"exec",ProtectedExec,NULL,KillExec); + Nxinter_SafeInit(pInter); + NXcopy_Init(pInter); + return pInter; } /*--------------------------------------------------------------------------*/ @@ -431,6 +438,21 @@ static int ProtectedExec(ClientData clientData, Tcl_Interp *interp, } /*--------------------------------------------------------------------------*/ + int MacroFileEvalNew(SConnection *pCon, SicsInterp *pInter, void *pData, + int argc, char *argv[]) + { + void *pCom = NULL; + pCom = FindCommandData(pInter,"exe","ExeManager"); + assert(pCom != NULL); + + if(argc < 2){ + SCWrite(pCon,"ERROR: no batch buffer to execute specified", + eError); + return 0; + } + return runExeBatchBuffer(pCom,pCon,pInter,argv[1]); + } +/*----------------------------------------------------------------------*/ int MacroFileEval(SConnection *pCon, SicsInterp *pInter, void *pData, int argc, char *argv[]) { @@ -459,8 +481,8 @@ static int ProtectedExec(ClientData clientData, Tcl_Interp *interp, /* open filename */ if( argc < 2) { - SCWrite(pCon,"ERROR: No filename specified ",eError); - return 0; + SCWrite(pCon,"ERROR: No filename specified ",eError); + return 0; } fp = fopen(argv[1],"r"); if(!fp) @@ -490,7 +512,7 @@ static int ProtectedExec(ClientData clientData, Tcl_Interp *interp, { iChar = fgetc(fp); if(iChar == EOF) - { + { iChar = (int)'\n'; iRun = 0; } @@ -505,11 +527,11 @@ static int ProtectedExec(ClientData clientData, Tcl_Interp *interp, SetStatus(eEager); FirstWord(pCom,pBueffel); if(FindCommand(pInter,pBueffel) != NULL) - { + { sprintf(pBueffel,"%s:%d>> %s",pFile,iLine,pCom); SCWrite(pCon,pBueffel,eValue); if(pWhere != NULL) - { + { free(pWhere); } pWhere = strdup(pBueffel); @@ -524,16 +546,16 @@ static int ProtectedExec(ClientData clientData, Tcl_Interp *interp, { /* Tcl error */ if(strlen(pTcl->result) > 2) { - /* - local copy in order to resolve a valgrind error - */ - strncpy(pBueffel,pTcl->result,511); + /* + local copy in order to resolve a valgrind error + */ + strncpy(pBueffel,pTcl->result,511); SCWrite(pCon,pBueffel,eError); } pCom = Tcl_DStringValue(&command); - SCWrite(pCon,"ERROR: in Tcl block:",eError); + SCWrite(pCon,"ERROR: in Tcl block:",eError); SCWrite(pCon,pCom,eError); - SCWrite(pCon,"ERROR: end of Tcl error block",eError); + SCWrite(pCon,"ERROR: end of Tcl error block",eError); } else /* SICS error */ { @@ -546,7 +568,7 @@ static int ProtectedExec(ClientData clientData, Tcl_Interp *interp, fclose(fp); Tcl_DStringFree(&command); SCWrite(pCon,"ERROR: batch processing interrupted",eError); - SetStatus(eEager); + SetStatus(eEager); return 0; } else @@ -753,6 +775,127 @@ static int ProtectedExec(ClientData clientData, Tcl_Interp *interp, } return 1; } +/*-----------------------------------------------------------------------*/ + int GumPut(SConnection *pCon, SicsInterp *pInter, void *pData, + int argc, char *argv[]) + { + OutCode eOut = eWarning; + int i = 0, iCode, iLen; + int iMacro; + char *ppCode; + char *pMessage = NULL; + commandContext cc; + + assert(pCon); + assert(pInter); + + if(argc < 2) + { + SCWrite(pCon,"Insufficient arguments to ClientPut",eError); + return 0; + } + + /* handle optional I/O codes */ + if(argc > 2) + { + /* the last one must be the code */ + iCode = argc - 1; + ppCode = strdup(argv[iCode]); + strtolower(ppCode); + while(pCode[i] != NULL) + { + if(strcmp(pCode[i],ppCode) == 0) + { + break; + } + i++; + } + if(ppCode) + { + free(ppCode); + } + } + else + { + i = 10; + iCode = argc; + } + + switch(i) + { + case 0: + eOut = eInternal; + break; + case 1: + eOut = eCommand; + break; + case 2: + eOut = eHWError; + break; + case 3: + eOut = eInError; + break; + case 4: + eOut = eStatus; + break; + case 5: + eOut = eValue; + break; + case 6: + eOut = eWarning; + break; + case 7: + eOut = eFinish; + break; + case 8: + eOut = eEvent; + break; + case 9: + eOut = eWarning; + break; + case 10: + eOut = eError; + break; + default: + eOut = eWarning; + iCode = argc; + break; + } + + /* recombine the message */ + /* find length */ + iLen = 0; + for(i = 1; i < iCode; i++) + { + iLen += strlen(argv[i]); + } + pMessage = (char *)malloc((iLen+100)*sizeof(char)); + if(!pMessage) + { + SCWrite(pCon,"ERROR: out of memory in clientput",eError); + return 0; + } + memset(pMessage,0,(iLen+100)*sizeof(char)); + Arg2Text(iCode-1,&argv[1],pMessage,(iLen+100)*sizeof(char)); + + /* now write, thereby tunneling macro flag in order to get proper + write to client and not into interpreter. We also make sure that the device + is gumput + */ + iMacro = SCinMacro(pCon); + SCsetMacro(pCon,0); + cc = SCGetContext(pCon); + strcpy(cc.deviceID,"gumput"); + SCPushContext2(pCon,cc); + SCWrite(pCon,pMessage,eOut); + SCPopContext(pCon); + SCsetMacro(pCon,iMacro); + if(pMessage) + { + free(pMessage); + } + return 1; + } /*----------------------------------------------------------------------*/ int Broadcast(SConnection *pCon, SicsInterp *pInter, void *pData, int argc, char *argv[]) @@ -865,7 +1008,7 @@ static int ProtectedExec(ClientData clientData, Tcl_Interp *interp, } /* make a string */ - pCommand = Arg2Tcl(argc,argv,pBueffel,sizeof(pBueffel)); + pCommand = Arg2Tcl0(argc-1,argv+1,pBueffel,sizeof(pBueffel),self->command); if (!pCommand) { SCWrite(pCon, "ERROR: no more memory", eError); return 0; @@ -874,14 +1017,9 @@ static int ProtectedExec(ClientData clientData, Tcl_Interp *interp, if (pCommand != pBueffel) free(pCommand); if(iRet == TCL_OK) { - /* we do not now why, but at some time it was found that - we need a copy, and can not use pTcl->result directly - - SCWrite(pCon,pTcl->result,eStatus); - - let us use SCPrintf, which maked always a copy - */ - SCPrintf(pCon, eStatus, "%s", pTcl->result); + if(strlen(pTcl->result) > 0){ + SCPrintf(pCon, eStatus, "%s", pTcl->result); + } return 1; } else @@ -895,8 +1033,6 @@ static int ProtectedExec(ClientData clientData, Tcl_Interp *interp, } return 1; /* not reached */ } - -#include "access.c" /*--------------------------------------------------------------------------*/ int TclPublish(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) @@ -904,7 +1040,7 @@ static int ProtectedExec(ClientData clientData, Tcl_Interp *interp, pPubTcl pNew = NULL; char pBueffel[132]; int iUser, i, iRet; - + /* check no of args */ if(argc < 3) { @@ -923,18 +1059,10 @@ static int ProtectedExec(ClientData clientData, Tcl_Interp *interp, return 0; } + /* try convert last parameter to user code */ - iUser = 0; - strtolower(argv[2]); - while(aCode[iUser] != NULL) - { - if(strcmp(aCode[iUser],argv[2]) == 0) - { - break; - } - iUser++; - } - if(iUser > iCodes) + iUser = decodeSICSPriv(argv[2]); + if(iUser < 0) { sprintf(pBueffel,"ERROR: cannot identify %s as a valid user code", argv[2]); @@ -947,7 +1075,7 @@ static int ProtectedExec(ClientData clientData, Tcl_Interp *interp, if (pNew) { /* yes -> overwrite access code */ pNew->iUser = iUser; - return 0; + return 1; } /* do a job !*/ pNew = CreatePublish(argv[1],iUser); diff --git a/macro.h b/macro.h index a3b1d535..2bd8fa78 100644 --- a/macro.h +++ b/macro.h @@ -14,6 +14,7 @@ #ifndef SICSMACRO #define SICSMACRO #include +#include "sics.h" #include "SCinter.h" Tcl_Interp *MacroInit(SicsInterp *pInter); @@ -30,6 +31,8 @@ int argc, char *argv[]); int ClientPut(SConnection *pCon, SicsInterp *pInter, void *pData, int argc, char *argv[]); + int GumPut(SConnection *pCon, SicsInterp *pInter, void *pData, + int argc, char *argv[]); int Broadcast(SConnection *pCon, SicsInterp *pInter, void *pData, int argc, char *argv[]); int TransactAction(SConnection *pCon, SicsInterp *pSics, void *pData, diff --git a/make_gen b/make_gen index 865d8930..4d986c81 100644 --- a/make_gen +++ b/make_gen @@ -8,10 +8,10 @@ COBJ = Sclient.o network.o ifile.o intcli.o $(FORTIFYOBJ) SOBJ = network.o ifile.o conman.o SCinter.o splitter.o passwd.o \ servlog.o sicvar.o nserver.o SICSmain.o motorlist.o\ - sicsexit.o costa.o task.o $(FORTIFYOBJ)\ + sicsexit.o costa.o task.o $(FORTIFYOBJ) access.o\ macro.o ofac.o obpar.o obdes.o drive.o status.o intserv.o \ devexec.o mumo.o mumoconf.o selector.o selvar.o fupa.o lld.o \ - lld_blob.o strrepl.o lin2ang.o fomerge.o napi4.o napi5.o \ + lld_blob.o strrepl.o lin2ang.o fomerge.o napi5.o napi4.o\ script.o o2t.o alias.o napi.o nxdata.o stringdict.o sdynar.o \ histmem.o histdriv.o histsim.o interface.o callback.o nxio.o \ event.o emon.o evcontroller.o evdriver.o simev.o perfmon.o \ @@ -30,14 +30,27 @@ SOBJ = network.o ifile.o conman.o SCinter.o splitter.o passwd.o \ s_rnge.o sig_die.o gpibcontroller.o $(NIOBJ) mcreader.o mccontrol.o\ hmdata.o nxscript.o tclintimpl.o sicsdata.o mcstascounter.o \ mcstashm.o initializer.o remob.o tclmotdriv.o protocol.o \ - sinfox.o sicslist.o cone.o + sinfox.o sicslist.o cone.o hipadaba.o sicshipadaba.o statistics.o \ + ascon.o errormsg.o scriptcontext.o logger.o logreader.o logsetup.o \ + savehdb.o statusfile.o sicshdbfactory.o proxy.o devser.o \ + moregress.o multicounter.o regresscter.o histregress.o \ + sicshdbadapter.o polldriv.o sicspoll.o statemon.o hmslave.o \ + nwatch.o asyncqueue.o asyncprotocol.o sicsobj.o \ + nxcopy.o nxinterhelper.o nxinter_wrap.o genericcontroller.o nxstack.o \ + sctdriveadapter.o sctdriveobj.o MOTOROBJ = motor.o simdriv.o COUNTEROBJ = countdriv.o simcter.o counter.o VELOOBJ = velo.o velosim.o .SUFFIXES: -.SUFFIXES: .tcl .htm .c .o +.SUFFIXES: .tcl .htm .c .o .tc + +.tc.c: + tjxp $*.tc $*.c + +#--- This .SECONDARY. target is necessary to preserve generated .c files for debugging +.SECONDARY.: sicspoll.c polldriv.c all: libmat libhlib libtecsl libpsi SICServer @@ -47,7 +60,7 @@ full: purge all SICServer: $(SOBJ) $(MOTOROBJ) $(COUNTEROBJ) \ $(VELOOBJ) $(DIFIL) $(EXTRA) \ $(SUBLIBS) - $(CC) -g -o SICServer \ + $(CC) -g -pg -o SICServer \ $(SOBJ) $(MOTOROBJ) $(COUNTEROBJ) \ $(VELOOBJ) $(DIFOBJ) $(EXTRA) $(LIBS) diff --git a/makefile_linux b/makefile_linux index 660eef3a..33944cd7 100644 --- a/makefile_linux +++ b/makefile_linux @@ -6,11 +6,11 @@ # Markus Zolliker, March 2003 #========================================================================== # assign if the National Instrument GPIB driver is available -SINQDIR=/afs/psi.ch/project/sinq -NI= -#NI= -DHAVENI -#NIOBJ= nigpib.o -#NILIB=$(SINQDIR)/linux/lib/cib.o +SINQDIR=/usr/local +#NI= +NI= -DHAVENI +NIOBJ= nigpib.o +NILIB=$(SINQDIR)/lib/cib.o include linux_def @@ -26,7 +26,7 @@ SUBLIBS = psi/libpsi.a psi/hardsup/libhlib.a matrix/libmatrix.a \ LIBS = -L$(HDFROOT)/lib $(SUBLIBS) $(NILIB)\ -ltcl8.4 -lmxml $(HDFROOT)/lib/libhdf5.a \ $(HDFROOT)/lib/libmfhdf.a $(HDFROOT)/lib/libdf.a \ - -ljpeg -ldl -lz -lm -lc + -lmxml -lghttp -ljpeg -ljson -ldl -lz -lsz -lm -lc include make_gen diff --git a/makefile_macosx b/makefile_macosx new file mode 100644 index 00000000..2dc98c99 --- /dev/null +++ b/makefile_macosx @@ -0,0 +1,42 @@ +#--------------------------------------------------------------------------- +# Makefile for SICS +# machine-dependent part for Mac OS X +# +# Mark Koennecke 1996-2001 +# Markus Zolliker, March 2003 +# Mark Koennecke, July 2008 +#========================================================================== +# assign if the National Instrument GPIB driver is available +SINQDIR=/Users/Shared +#NI= -DHAVENI +#NIOBJ= nigpib.o +#NILIB=$(SINQDIR)/sl5/lib/cib.o + +include macosx_def + +CC = gcc +CFLAGS = -I$(HDFROOT)/include -I/sw/include -DNXXML -DHDF5 -DHDF4 $(NI) -DMACOSX \ + -Ipsi/hardsup -I. \ + -Werror -DNONINTF -g $(DFORTIFY) \ + -Wall -Wno-unused -Wno-comment -Wno-switch + +BINTARGET = bin +EXTRA=nintf.o +SUBLIBS = psi/libpsi.a psi/hardsup/libhlib.a matrix/libmatrix.a \ + psi/tecs/libtecsl.a +LIBS = -L$(HDFROOT)/lib -L/sw/lib $(SUBLIBS) $(NILIB)\ + -ltcl $(HDFROOT)/lib/libhdf5.a -lmfhdf -ldf \ + $(HDFROOT)/lib/libsz.a \ + $(HDFROOT)/lib/libjson.a -ljpeg \ + -ldl -lz -lmxml $(HDFROOT)/lib/libghttp.a -lm -lc + +include make_gen + + + + + + + + + diff --git a/makefile_slinux b/makefile_slinux index 5b813277..f5955672 100644 --- a/makefile_slinux +++ b/makefile_slinux @@ -9,13 +9,13 @@ SINQDIR=/afs/psi.ch/project/sinq NI= -DHAVENI NIOBJ= nigpib.o -NILIB=$(SINQDIR)/sl-linux/lib/cib.o +NILIB=$(SINQDIR)/sl5/lib/cib.o include sllinux_def CC = gcc -CFLAGS = -I$(HDFROOT)/include -DHDF4 -DHDF5 -DNXXML $(NI) \ - -Ipsi/hardsup -I. \ +CFLAGS = -I$(HDFROOT)/include -DNXXML -DHDF4 -DHDF5 $(NI) \ + -Ipsi/hardsup -I. \ -Werror -DCYGNUS -DNONINTF -g $(DFORTIFY) \ -Wall -Wno-unused -Wno-comment -Wno-switch @@ -23,10 +23,12 @@ BINTARGET = bin EXTRA=nintf.o SUBLIBS = psi/libpsi.a psi/hardsup/libhlib.a matrix/libmatrix.a \ psi/tecs/libtecsl.a -LIBS = -L$(HDFROOT)/lib $(SUBLIBS) $(NILIB)\ - -ltcl8.3 $(HDFROOT)/lib/libhdf5.a \ +LIBS = -L$(HDFROOT)/lib $(SUBLIBS) $(NILIB)\ + -ltcl $(HDFROOT)/lib/libhdf5.a \ $(HDFROOT)/lib/libmfhdf.a $(HDFROOT)/lib/libdf.a \ - $(HDFROOT)/lib/libjpeg.a -lsz -ldl -lz -lmxml -lghttp -lm -lc + $(HDFROOT)/lib/libjpeg.a $(HDFROOT)/lib/libsz.a \ + $(HDFROOT)/lib/libjson.a \ + -ldl -lz -lmxml $(HDFROOT)/lib/libghttp.a -lm -lc include make_gen diff --git a/matrix/matcreat.c b/matrix/matcreat.c index 50812345..f2a539f7 100644 --- a/matrix/matcreat.c +++ b/matrix/matcreat.c @@ -20,7 +20,7 @@ #ifdef __TURBOC__ #include #else -#include +#include #endif #include "matrix.h" diff --git a/matrix/materr.c b/matrix/materr.c index 827b73ac..26bc3174 100644 --- a/matrix/materr.c +++ b/matrix/materr.c @@ -19,7 +19,7 @@ #ifdef __TURBOC__ #include #else -#include +#include #endif #include "matrix.h" diff --git a/matrix/matinv.c b/matrix/matinv.c index 645d3798..ca344186 100644 --- a/matrix/matinv.c +++ b/matrix/matinv.c @@ -20,7 +20,7 @@ #ifdef __TURBOC__ #include #else -#include +#include #endif #include "matrix.h" diff --git a/matrix/matsolve.c b/matrix/matsolve.c index 9a2f61e8..9972ff2a 100644 --- a/matrix/matsolve.c +++ b/matrix/matsolve.c @@ -20,7 +20,7 @@ #ifdef __TURBOC__ #include #else -#include +#include #endif #include "matrix.h" diff --git a/maximize.c b/maximize.c index 4857e45f..a8b8affa 100644 --- a/maximize.c +++ b/maximize.c @@ -47,6 +47,7 @@ #include "counter.h" #include "drive.h" #include "maximize.h" +#include "motor.h" #define MAXPTS 100 #define DEBUG 1 @@ -55,6 +56,7 @@ pObjectDescriptor pDes; pCounter pCount; int i360; + int maxpts; }Maxxii; /*----------------------------------------------------------------------- @@ -134,7 +136,18 @@ } return 1; } - + /*----------------------------------------------------------------------*/ + static float readMPDrivable(void *pVar, SConnection *pCon) + { + float value = -999.99; + pIDrivable pDriv = NULL; + pDummy pDum = (pDummy)pVar; + + pDriv = GetDrivableInterface(pVar); + assert(pDriv != NULL); + value = pDriv->GetValue(pVar,pCon); + return value; + } /*-----------------------------------------------------------------------*/ int MaximizePeak(pMax self, void *pVar, char *pVarName, float fStep, CounterMode eMode, @@ -159,27 +172,27 @@ start: lMax = 0; lMin = 0x7fffffff; - fStart = pDriv->GetValue(pVar,pCon); + fStart = readMPDrivable(pVar,pCon); if(fStart < -999999.) - { + { return 0; } /* search to the left until out of space or lCts < lMax/2. */ SCWrite(pCon,"Searching for low angle boundary..",eWarning); - for(i = MAXPTS/2; i >= 0; i--) + for(i = self->maxpts/2; i >= 0; i--) { /* drive motor */ - fPos = fStart - (MAXPTS/2 - i)*fStep; + fPos = fStart - (self->maxpts/2 - i)*fStep; fPos = in360(self,fPos); if(maxDrive(pVar,pVarName,fPos,pCon) != 1) - { + { return 0; } - x[i] = pDriv->GetValue(pVar,pCon); + x[i] = readMPDrivable(pVar,pCon); /* count */ if(maxCount(self->pCount,eMode,fPreset, &lCts,pCon) != 1) - { + { return 0; } /* print a message */ @@ -218,7 +231,7 @@ goto start; } /* no peak found or normal peak: continue at other side */ - if( (i < 1) || (y[MAXPTS/2] > lMax/2) ) + if( (i < 1) || (y[self->maxpts/2] > lMax/2) ) { iSkip = 0; } @@ -227,7 +240,7 @@ /* next case: all of the peak in measured half: find max value and skip the right half */ - for(i = MAXPTS/2; i > 0; i--) + for(i = self->maxpts/2; i > 0; i--) { if(y[i] > lMax/2) { @@ -246,16 +259,16 @@ lMin = 100000; lMax = -100000; SCWrite(pCon,"Searching for high angle boundary..",eWarning); - for(i = MAXPTS/2; i < MAXPTS; i++) + for(i = self->maxpts/2; i < self->maxpts; i++) { /* drive motor */ - fPos = fStart + (i - MAXPTS/2) * fStep; + fPos = fStart + (i - self->maxpts/2) * fStep; fPos = in360(self,fPos); if(maxDrive(pVar,pVarName,fPos,pCon) != 1) { return 0; } - x[i] = pDriv->GetValue(pVar,pCon); + x[i] = readMPDrivable(pVar,pCon); /* count */ if(maxCount(self->pCount,eMode,fPreset, &lCts,pCon) != 1) { @@ -292,18 +305,18 @@ iTop = i; iTop++; /* first case: peak is at high angle side */ - if( (i > MAXPTS-2) && (lMax*0.5 > lMin) ) + if( (i > self->maxpts-2) && (lMax*0.5 > lMin) ) { goto start; } /* second case: no peak */ - if( (iTop > MAXPTS-2) ) + if( (iTop > self->maxpts-2) ) { SCWrite(pCon,"ERROR: no peak found!",eError); return 0; } /* third case: normal peak */ - if(y[MAXPTS/2] >= 0.5*lMax) + if(y[self->maxpts/2] >= 0.5*lMax) { iTop--; } @@ -313,7 +326,7 @@ */ else { - for(i = MAXPTS/2; i < MAXPTS; i++) + for(i = self->maxpts/2; i < self->maxpts; i++) { if(y[i] > lMax/2) { @@ -325,7 +338,7 @@ } } /* end of iSkip */ - if( (iBot < 2) || (iTop > MAXPTS-2) || (lMax < lMin*2) ) + if( (iBot < 2) || (iTop > self->maxpts-2) || (lMax < lMin*2) ) { SCWrite(pCon,"ERROR: no peak found!",eError); return 0; @@ -420,11 +433,14 @@ pNew->pDes = CreateDescriptor("Maximizer"); pNew->pCount = pCom->pData; pNew->i360 = 0; + pNew->maxpts = 100; AddCommand(pSics,"max",MaximizeAction,MaxKill,pNew); return 1; } -/*------------------------------------------------------------------*/ +/*------------------------------------------------------------------ + * max motor step preset mode + * ---------------------------------------------------------------------*/ int MaximizeAction(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) { @@ -434,7 +450,7 @@ double dVal; float fStep, fPreset; CounterMode eCount; - int iRet; + int iRet, iVal; self = (pMax)pData; assert(self); @@ -446,9 +462,56 @@ return 1; } + /* enough arguments ?*/ if(argc < 5) { + if(argc > 1) + { + strtolower(argv[1]); + if(strcmp(argv[1],"in360") == 0) + { + if(argc > 2) + { + iVal = atoi(argv[2]); + if(iVal != 0 && iVal != 1) { + SCWrite(pCon,"ERROR: only 0, 1 allowed for in360",eError); + return 0; + } + self->i360 = iVal; + SCSendOK(pCon); + return 1; + } + else + { + snprintf(pBueffel,255,"max.in360 = %d", self->i360); + SCWrite(pCon,pBueffel,eValue); + return 1; + } + } + if(strcmp(argv[1],"maxpts") == 0) + { + if(argc > 2) + { + iVal = atoi(argv[2]); + if(iVal < 10 || iVal > 100) { + SCWrite(pCon,"ERROR: maxpst must be between 10 and 100", + eError); + return 0; + } + self->maxpts = iVal; + SCSendOK(pCon); + return 1; + } + else + { + snprintf(pBueffel,255,"max.maxpts = %d", self->maxpts); + SCWrite(pCon,pBueffel,eValue); + return 1; + } + } + + } SCWrite(pCon,"ERROR: Insufficient number of arguments to max", eError); return 0; diff --git a/mclist.c b/mclist.c new file mode 100644 index 00000000..f67badf0 --- /dev/null +++ b/mclist.c @@ -0,0 +1,116 @@ +#ifndef MC_List_TYPE +#define MC_List_TYPE MC_NAME(List) +#define MC_First_FUN MC_NAME(First) +#define MC_This_FUN MC_NAME(This) +#define MC_Next_FUN MC_NAME(Next) +#define MC_End_FUN MC_NAME(End) +#define MC_Insert_FUN MC_NAME(Insert) +#define MC_Add_FUN MC_NAME(Add) +#define MC_Take_FUN MC_NAME(Take) +#endif + +#ifdef MC_IMPLEMENTATION + +#undef MC_IMPLEMENTATION +#ifndef MC_TYPE +#define MC_TYPE MC_NAME()* +#endif + +#else + +#define MC_DO_NOT_UNDEF +#include "mclist.h" +#undef MC_DO_NOT_UNDEF + +#endif + +#ifndef MC_NEXT +#define MC_NEXT next +#endif + +MC_TYPE MC_First_FUN(MC_List_TYPE *list) { + list->ptr = &list->head; + return list->head; +} + +MC_TYPE MC_This_FUN(MC_List_TYPE *list) { + if (list->head == NULL) { + list->ptr = &list->head; + return NULL; + } + return *list->ptr; + } + +MC_TYPE MC_Next_FUN(MC_List_TYPE *list) { + MC_TYPE node; + if (list->head == NULL) { + list->ptr = &list->head; + return NULL; + } + node = *list->ptr; + if (node) { + list->ptr = &node->MC_NEXT; + } + return *list->ptr; +} + +void MC_End_FUN(MC_List_TYPE *list) { + MC_TYPE node; + if (list->head == NULL) { + list->ptr = &list->head; + } + node = *list->ptr; + if (node) { + while (node->MC_NEXT != NULL) { + node = node->MC_NEXT; + } + list->ptr = &node->MC_NEXT; + } +} + +void MC_Insert_FUN(MC_List_TYPE *list, MC_TYPE node) { + if (list->head == NULL) { + list->ptr = &list->head; + } + node->MC_NEXT = *list->ptr; + *list->ptr = node; +} + +void MC_Add_FUN(MC_List_TYPE *list, MC_TYPE node) { + node->MC_NEXT = NULL; + if (list->head == NULL) { + list->head = node; + list->ptr = &list->head; + } else { + if (*list->ptr != NULL) { + MC_End_FUN(list); + } + *list->ptr = node; + } +} + +MC_TYPE MC_Take_FUN(MC_List_TYPE *list) { + MC_TYPE node; + node = *list->ptr; + if (node != NULL) { + *list->ptr = node->MC_NEXT; + } + return node; +} + +void MC_Delete_FUN(MC_List_TYPE *list, void (*deleteFunc)(MC_TYPE n)) { + MC_TYPE node; + MC_TYPE victim; + node = list->head; + while (node != NULL) { + victim = node; + node = node->next; + deleteFunc(victim); + } + list->head = NULL; + list->ptr = &list->head; +} + +#undef MC_NAME +#undef MC_TYPE +#undef MC_NEXT diff --git a/mclist.h b/mclist.h new file mode 100644 index 00000000..889a6737 --- /dev/null +++ b/mclist.h @@ -0,0 +1,177 @@ +/** \file + * \brief Type safe list handling + * + * The definition and implementation make use of macros extensively to create + * a list type and related functions for any node type. + * However, accessing the list does not use macros. + * The list is implemented as a singly linked list. Sequential appending + * to the tail is fast, because the list structure contains + * a pointer to the anchor of the last accessed node. + * + * For a local list, mclist.c must be included after + * the declaration of the node type and after defining the macro MC_NAME + * and optionally MC_TYPE and MC_NEXT. + * + * For a public list, in the header file mclist.h must be included after + * the declaration of the node type and after defining the macro MC_NAME + * and optionally MC_TYPE. In the implementation mclist.c + * must be included after defining the macro MC_NAME and MC_IMPLEMENTATION + * and optionally MC_TYPE and MC_NEXT. + * + * MC_NAME has one parameter and describes how to combine the list name + * with the function names. + * + * MC_TYPE defines the node type. If undeclared it defaults to a pointer + * to the name. + * + * MC_NEXT indicates the name of the link. It defaults to 'next'. + * + * MC_IMPLEMENTATION has no value and must be defined when the list type + * was already declared. Typically this is done in a header file including mclist.h. + * + * The macros MC_NAME, MC_TYPE, MC_NEXT and MC_IMPLEMENTATION are undefined + * within mclist.c and mclist.h and must be redefined for every list. + * + * \par Usage example + * \code + * // declare the Node type + * typedef struct Node { + * struct Node *next; + * char *name; + * } Node; + * + * // this declaration leads to a list type 'NodeList' and fucntions names 'Node' + * #define MC_NAME(T) Node##T + * // the following line is not needed as 'next' is the default for the link + * #define MC_NEXT next + * // the following line is not needed as 'Node *' is the default for the type in this case + * #define MC_TYPE Node * + * // inside mclist.c, the list type is declared and the related functions are implemented + * #include "mclist.c" + * + * int main(void) { + * // declare and init the list + * NodeList list={NULL}; + * + * // create a node + * Node *node; + * node = malloc(sizeof(*node)); + * node->name = "First"; + * + * // add node at the end of the list + * NodeAdd(&list, node); + * + * // print the names of all list nodes + * for (node = NodeFirst(&list); node != NULL; node = NodeNext(&list)) { + * printf("%s\n", node->name); + * } + * + * // alternative form not touching the list position + * // only for the case, where no insert or take function is used inside the loop + * for (node = list.head; node != NULL; node = node->next) { + * printf("%s\n", node->name); + * } + * + * // remove the node with the name "First" + * for (node = NodeFirst(&list); node != NULL; node = NodeNext(&list)) { + * if (strcmp(node->name, "First") == 0) { + * free(NodeTake(&list)); + * } + * } + * } + * \endcode + */ +#ifndef MC_TYPE +/** \brief default node type + */ +#define MC_TYPE MC_NAME()* +#endif + +#ifndef MC_List_TYPE +#define MC_List_TYPE MC_NAME(List) +#define MC_First_FUN MC_NAME(First) +#define MC_This_FUN MC_NAME(This) +#define MC_Next_FUN MC_NAME(Next) +#define MC_End_FUN MC_NAME(End) +#define MC_Insert_FUN MC_NAME(Insert) +#define MC_Add_FUN MC_NAME(Add) +#define MC_Take_FUN MC_NAME(Take) +#define MC_Delete_FUN MC_NAME(Delete) +#endif + +typedef struct MC_List_TYPE { + MC_TYPE head; + MC_TYPE *ptr; +} MC_List_TYPE; + +/** \brief move to first node and get it + * \param list the list + * \return the node or NULL when the list is empty + * + * Actual position on return: at the first node + */ +MC_TYPE MC_First_FUN(MC_List_TYPE *list); + +/** \brief get the node at the current position + * \param list the list + * \return the node or NULL when the list is empty or the position is at end + * + * Actual position on return: not changed (= at the returned node) + */ +MC_TYPE MC_This_FUN(MC_List_TYPE *list); + +/** \brief get the node after the current node + * \param list the list + * \return the node or NULL when the list is empty or the position is at end + * + * Actual position on return: incremented (= at the returned node or at end) + */ +MC_TYPE MC_Next_FUN(MC_List_TYPE *list); + +/** \brief move the position to the end + * \param list the list + * + * Actual position on return: at end + */ +void MC_End_FUN(MC_List_TYPE *list); + +/** \brief insert at the current position, i.e. before the current node + * \param list the list + * \param node the node to be inserted + * + * Actual position on return: at the inserted node + */ +void MC_Insert_FUN(MC_List_TYPE *list, MC_TYPE node); + +/** \brief add at the end of the list + * \param list the list + * \param node the node to be added + * + * Actual position on return: at the inserted node (before the last node, not at end!) + */ +void MC_Add_FUN(MC_List_TYPE *list, MC_TYPE node); + +/** \brief remove the node at the current position + * \param list the list + * \return the removed node or NULL when the list is empty or the position is at end + * + * Actual position on return: after the taken node + * + * Note: it is the responsibility of the caller to free the node if it is not used + * anymore + */ +MC_TYPE MC_Take_FUN(MC_List_TYPE *list); + +/** \brief remove and delete all nodes + * \param list the list + * \param deleteFunc the kill function of the node + * + * Calls the kill function for every node. The list is + * empty on return. + */ +void MC_Delete_FUN(MC_List_TYPE *list, void (*deleteFunc)(MC_TYPE node)); + +#ifndef MC_DO_NOT_UNDEF +#undef MC_NAME +#undef MC_TYPE +#endif diff --git a/mcreader.c b/mcreader.c index 4470ac59..d2d6c48d 100644 --- a/mcreader.c +++ b/mcreader.c @@ -178,6 +178,7 @@ static int insertMonitor(pMcStasReader self, SConnection *pCon, return 0; } + memset(pBueffel,0,512); status = NXgetdata(self->handle,pBueffel); if(status != NX_OK){ snprintf(pBueffel,511,"ERROR: Nexus error %s while reading %s", diff --git a/mcstas/dmc/DataNumber b/mcstas/dmc/DataNumber index 985a914b..b4495070 100644 --- a/mcstas/dmc/DataNumber +++ b/mcstas/dmc/DataNumber @@ -1,3 +1,3 @@ - 120 + 230 NEVER, EVER modify or delete this file You'll risk eternal damnation and a reincarnation as a cockroach!|n \ No newline at end of file diff --git a/mcstas/dmc/README b/mcstas/dmc/README new file mode 100644 index 00000000..8f60f950 --- /dev/null +++ b/mcstas/dmc/README @@ -0,0 +1,36 @@ + + VIRTUAL DMC + + This is the SICS-McStas virtual instrument DMC modelled after the real + powder diffcractometer DMC at SINQ: + + http://sinq.web.psi.ch/sinq/instr/dmc/dmc.html + + The McStas simulation used for this system has been verified against + the real instrument. The basic usage is to start the SICServer with: + + ./SICServer vdmc.tcl + + in this directory. Then you can connect to SICS at localhost/2911 + with any SICS client and start issuing commands. + + In order to save simulation time, the simulation works with + neutron data files presimulated for four wavelength up to the sample. + This is why you get your wavelength coerced to the nearest + precalculated value. + + If you desire to "measure" your own sample on virtual DMC, you first + have to create a LazyPulverix output file. You then can proceed to + load the sample into SICS with the SICS command: + + sample load path-to-lazy-pulverix-outputfile + + The command: + + sample list + + tells you which samples virtual DMC already knows about. + + Questions and comments to: Mark.Koennecke@psi.ch + + diff --git a/mcstas/dmc/dmcafter.c b/mcstas/dmc/dmcafter.c new file mode 100644 index 00000000..9579fd0b --- /dev/null +++ b/mcstas/dmc/dmcafter.c @@ -0,0 +1,8436 @@ +/* Automatically generated file. Do not edit. + * Format: ANSI C source code + * Creator: McStas + * Instrument: dmcafter.instr (DMC_diff) + * Date: Tue Aug 2 16:09:43 2005 + */ + + +#define MCSTAS_VERSION "1.8 - Mar. 05, 2004" +#define MC_USE_DEFAULT_MAIN +#define MC_EMBEDDED_RUNTIME + +#line 1 "mcstas-r.h" +/******************************************************************************* +* +* McStas, neutron ray-tracing package +* Copyright 1997-2002, All rights reserved +* Risoe National Laboratory, Roskilde, Denmark +* Institut Laue Langevin, Grenoble, France +* +* Runtime: share/mcstas-r.h +* +* %Identification +* Written by: KN +* Date: Aug 29, 1997 +* Release: McStas 1.6 +* Version: 1.5 +* +* Runtime system header for McStas. +* +* Usage: Automatically embbeded in the c code. +* +* $Id$ +* +* $Log$ +* Revision 1.1 2007/01/30 03:19:43 koennecke +* - Fixed state monitor eclipse commit problems. Siiiiiiiggggghhhhhh! +* +* Revision 1.55 2003/10/21 14:08:12 pkwi +* Rectangular focusing improved: Renamed randvec_target_rect to randvec_target_rect_angular. Wrote new randvec_target_rect routine, w/h in metres. Both routines use use component orientation (ROT_A_CURRENT_COMP) as input. +* +* Modifications to Res_sample and V_sample to match new features of the runtime. +* +* Revision 1.54 2003/09/05 08:59:18 farhi +* added INSTRUMENT parameter default value grammar +* mcinputtable now has also default values +* mcreadpar now uses default values if parameter not given +* extended instr_formal parameter struct +* extended mcinputtable structure type +* +* Revision 1.53 2003/04/07 11:50:51 farhi +* Extended the way mcplot:plotter is assigned. Set --portable ok +* Handle Scilab:Tk and ~GTk menu (shifted) +* Updated help in mcrun and mcstas-r.c +* +* Revision 1.52 2003/04/04 18:20:21 farhi +* remove some warnings (duplicated decl) for --no-runtime on Dec OSF +* +* Revision 1.51 2003/04/04 14:27:19 farhi +* Moved format definitions to mcstas-r.c for --no-runtime to work +* +* Revision 1.50 2003/02/11 12:28:46 farhi +* Variouxs bug fixes after tests in the lib directory +* mcstas_r : disable output with --no-out.. flag. Fix 1D McStas output +* read_table:corrected MC_SYS_DIR -> MCSTAS define +* monitor_nd-lib: fix Log(signal) log(coord) +* HOPG.trm: reduce 4000 points -> 400 which is enough and faster to resample +* Progress_bar: precent -> percent parameter +* CS: ---------------------------------------------------------------------- +* +* Revision 1.5 2002/10/19 22:46:21 ef +* gravitation for all with -g. Various output formats. +* +* Revision 1.4 2002/09/17 12:01:21 ef +* removed unused macros (PROP_Y0, X0), changed randvec_target_sphere to circle +* added randvec_target_rect +* +* Revision 1.3 2002/08/28 11:36:37 ef +* Changed to lib/share/c code +* +* Revision 1.2 2001/10/10 11:36:37 ef +* added signal handler +* +* Revision 1.1 1998/08/29 11:36:37 kn +* Initial revision +* +*******************************************************************************/ + +#ifndef MCSTAS_R_H +#define MCSTAS_R_H "$Revision$" + +#include +#include +#include +#include + +/* If the runtime is embedded in the simulation program, some definitions can + be made static. */ + +#ifdef MC_EMBEDDED_RUNTIME +#define mcstatic static +#else +#define mcstatic +#endif + +#ifdef __dest_os +#if (__dest_os == __mac_os) +#define MAC +#endif +#endif + +#ifdef WIN32 +#define MC_PATHSEP_C '\\' +#define MC_PATHSEP_S "\\" +#else /* !WIN32 */ +#ifdef MAC +#define MC_PATHSEP_C ':' +#define MC_PATHSEP_S ":" +#else /* !MAC */ +#define MC_PATHSEP_C '/' +#define MC_PATHSEP_S "/" +#endif /* !MAC */ +#endif /* !WIN32 */ + +#ifndef MC_PORTABLE +#ifndef MAC +#ifndef WIN32 +#include +#endif /* !MAC */ +#endif /* !WIN32 */ +#endif /* MC_PORTABLE */ + +typedef double MCNUM; +typedef struct {MCNUM x, y, z;} Coords; +typedef MCNUM Rotation[3][3]; + +/* Note: the enum instr_formal_types definition MUST be kept + synchronized with the one in mcstas.h and with the + instr_formal_type_names array in cogen.c. */ +enum instr_formal_types + { + instr_type_double, instr_type_int, instr_type_string + }; +struct mcinputtable_struct { + char *name; + void *par; + enum instr_formal_types type; + char *val; +}; +extern struct mcinputtable_struct mcinputtable[]; +extern int mcnumipar; +extern char mcinstrument_name[], mcinstrument_source[]; +extern int mctraceenabled, mcdefaultmain; +#ifndef MC_EMBEDDED_RUNTIME +extern FILE *mcsiminfo_file; +extern char mcsig_message[]; +extern int mcgravitation; +extern int mcdotrace; +extern struct mcformats_struct mcformats[]; +extern struct mcformats_struct mcformat; +#endif +void mcinit(void); +void mcraytrace(void); +void mcsave(FILE *); +void mcfinally(void); +void mcdisplay(void); + +/* MOD: E. Farhi, Sep 25th 2001 set Scattered flag (for groups) */ +#define SCATTER do {mcDEBUG_SCATTER(mcnlx, mcnly, mcnlz, mcnlvx, mcnlvy, mcnlvz, \ + mcnlt,mcnlsx,mcnlsy, mcnlp); mcScattered++;} while(0) +#define ABSORB do {mcDEBUG_STATE(mcnlx, mcnly, mcnlz, mcnlvx, mcnlvy, mcnlvz, \ + mcnlt,mcnlsx,mcnlsy, mcnlp); mcDEBUG_ABSORB(); goto mcabsorb;} while(0) +/* Note: The two-stage approach to MC_GETPAR is NOT redundant; without it, +* after #define C sample, MC_GETPAR(C,x) would refer to component C, not to +* component sample. Such are the joys of ANSI C. + +* Anyway the usage of MCGETPAR requires that we use sometimes bare names... +*/ +#define MC_GETPAR2(comp, par) (mcc ## comp ## _ ## par) +#define MC_GETPAR(comp, par) MC_GETPAR2(comp,par) +#define DETECTOR_OUT(p0,p1,p2) mcdetector_out(NAME_CURRENT_COMP,p0,p1,p2,NULL) +#define DETECTOR_OUT_0D(t,p0,p1,p2) mcdetector_out_0D(t,p0,p1,p2,NAME_CURRENT_COMP) +#define DETECTOR_OUT_1D(t,xl,yl,xvar,x1,x2,n,p0,p1,p2,f) \ + mcdetector_out_1D(t,xl,yl,xvar,x1,x2,n,p0,p1,p2,f,NAME_CURRENT_COMP) +#define DETECTOR_OUT_2D(t,xl,yl,x1,x2,y1,y2,m,n,p0,p1,p2,f) \ + mcdetector_out_2D(t,xl,yl,x1,x2,y1,y2,m,n,p0,p1,p2,f,NAME_CURRENT_COMP) +#define DETECTOR_OUT_3D(t,xl,yl,zl,xv,yv,zv,x1,x2,y1,y2,z1,z2,m,n,p,p0,p1,p2,f) \ + mcdetector_out_3D(t,xl,yl,zl,xv,yv,zv,x1,x2,y1,y2,z1,z2,m,n,p,p0,p1,p2,f,NAME_CURRENT_COMP) +/* ADD: E. Farhi, Sep 20th 2001 save neutron state (in local coords) */ +#define STORE_NEUTRON(index, x, y, z, vx, vy, vz, t, sx, sy, sz, p) \ + mcstore_neutron(mccomp_storein,index, x, y, z, vx, vy, vz, t, sx, sy, sz, p); +/* ADD: E. Farhi, Sep 20th 2001 restore neutron state (in local coords) */ +#define RESTORE_NEUTRON(index, x, y, z, vx, vy, vz, t, sx, sy, sz, p) \ + mcrestore_neutron(mccomp_storein,index, &x, &y, &z, &vx, &vy, &vz, &t, &sx, &sy, &sz, &p); +#define POS_A_COMP_INDEX(index) \ + (mccomp_posa[index]) +#define POS_R_COMP_INDEX(index) \ + (mccomp_posr[index]) \ + +#ifdef MC_TRACE_ENABLED +#define DEBUG +#endif + +#ifdef DEBUG +#define mcDEBUG_INSTR() if(!mcdotrace); else printf("INSTRUMENT:\n"); +#define mcDEBUG_COMPONENT(name,c,t) if(!mcdotrace); else \ + printf("COMPONENT: \"%s\"\n" \ + "POS: %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g, %g\n", \ + name, c.x, c.y, c.z, t[0][0], t[0][1], t[0][2], \ + t[1][0], t[1][1], t[1][2], t[2][0], t[2][1], t[2][2]); +#define mcDEBUG_INSTR_END() if(!mcdotrace); else printf("INSTRUMENT END:\n"); +#define mcDEBUG_ENTER() if(!mcdotrace); else printf("ENTER:\n"); +#define mcDEBUG_COMP(c) if(!mcdotrace); else printf("COMP: \"%s\"\n", c); +#define mcDEBUG_STATE(x,y,z,vx,vy,vz,t,s1,s2,p) if(!mcdotrace); else \ + printf("STATE: %g, %g, %g, %g, %g, %g, %g, %g, %g, %g\n", \ + x,y,z,vx,vy,vz,t,s1,s2,p); +#define mcDEBUG_SCATTER(x,y,z,vx,vy,vz,t,s1,s2,p) if(!mcdotrace); else \ + printf("SCATTER: %g, %g, %g, %g, %g, %g, %g, %g, %g, %g\n", \ + x,y,z,vx,vy,vz,t,s1,s2,p); +#define mcDEBUG_LEAVE() if(!mcdotrace); else printf("LEAVE:\n"); +#define mcDEBUG_ABSORB() if(!mcdotrace); else printf("ABSORB:\n"); +#else +#define mcDEBUG_INSTR() +#define mcDEBUG_COMPONENT(name,c,t) +#define mcDEBUG_INSTR_END() +#define mcDEBUG_ENTER() +#define mcDEBUG_COMP(c) +#define mcDEBUG_STATE(x,y,z,vx,vy,vz,t,s1,s2,p) +#define mcDEBUG_SCATTER(x,y,z,vx,vy,vz,t,s1,s2,p) +#define mcDEBUG_LEAVE() +#define mcDEBUG_ABSORB() +#endif + +#ifdef TEST +#define test_printf printf +#else +#define test_printf while(0) printf +#endif + +void mcdis_magnify(char *); +void mcdis_line(double, double, double, double, double, double); +void mcdis_multiline(int, ...); +void mcdis_circle(char *, double, double, double, double); + +#define RAD2MIN ((180*60)/PI) +#define MIN2RAD (PI/(180*60)) +#define DEG2RAD (PI/180) +#define RAD2DEG (180/PI) +#define AA2MS 629.719 /* Convert k[1/AA] to v[m/s] */ +#define MS2AA 1.58801E-3 /* Convert v[m/s] to k[1/AA] */ +#define K2V AA2MS +#define V2K MS2AA +#define Q2V AA2MS +#define V2Q MS2AA +#define SE2V 437.3949 /* Convert sqrt(E)[meV] to v[m/s] */ +#define VS2E 5.227e-6 /* Convert (v[m/s])**2 to E[meV] */ +#define FWHM2RMS 0.424660900144 /* Convert between full-width-half-max and */ +#define RMS2FWHM 2.35482004503 /* root-mean-square (standard deviation) */ +#define HBAR 1.05459E-34 +#define MNEUTRON 1.67492E-27 + +#ifndef PI +# ifdef M_PI +# define PI M_PI +# else +# define PI 3.14159265358979323846 +# endif +#endif + +typedef int mc_int32_t; +mc_int32_t mc_random(void); +void mc_srandom (unsigned int x); +unsigned long mt_random(void); +void mt_srandom (unsigned long x); + +#ifndef MC_RAND_ALG +#define MC_RAND_ALG 1 +#endif + +#if MC_RAND_ALG == 0 + /* Use system random() (not recommended). */ +# define MC_RAND_MAX RAND_MAX +#elif MC_RAND_ALG == 1 + /* "Mersenne Twister", by Makoto Matsumoto and Takuji Nishimura. */ +# define MC_RAND_MAX ((unsigned long)0xffffffff) +# define random mt_random +# define srandom mt_srandom +#elif MC_RAND_ALG == 2 + /* Algorithm used in McStas 1.1 and earlier (not recommended). */ +# define MC_RAND_MAX 0x7fffffff +# define random mc_random +# define srandom mc_srandom +#else +# error "Bad value for random number generator choice." +#endif + +#define rand01() ( ((double)random())/((double)MC_RAND_MAX+1) ) +#define randpm1() ( ((double)random()) / (((double)MC_RAND_MAX+1)/2) - 1 ) +#define rand0max(max) ( ((double)random()) / (((double)MC_RAND_MAX+1)/(max)) ) +#define randminmax(min,max) ( rand0max((max)-(min)) + (min) ) + +#define mcPROP_DT(dt) \ + do { \ + mcnlx += mcnlvx*(dt); \ + mcnly += mcnlvy*(dt); \ + mcnlz += mcnlvz*(dt); \ + mcnlt += (dt); \ + } while(0) + +/* ADD: E. Farhi, Aug 6th, 2001 PROP_GRAV_DT propagation with gravitation */ +#define PROP_GRAV_DT(dt, Ax, Ay, Az) \ + do { \ + mcnlx += mcnlvx*dt + Ax*dt*dt/2; \ + mcnly += mcnlvy*dt + Ay*dt*dt/2; \ + mcnlz += mcnlvz*dt + Az*dt*dt/2; \ + mcnlvx += Ax*dt; \ + mcnlvy += Ay*dt; \ + mcnlvz += Az*dt; \ + mcnlt += dt; \ + } while(0) + +#define PROP_DT(dt) \ + do { \ + if(dt < 0) ABSORB; \ + if (mcgravitation) { Coords mcLocG; double mc_gx, mc_gy, mc_gz; \ + mcLocG = rot_apply(ROT_A_CURRENT_COMP, coords_set(0,-9.8,0)); \ + coords_get(mcLocG, &mc_gx, &mc_gy, &mc_gz); \ + PROP_GRAV_DT(dt, mc_gx, mc_gy, mc_gz); } \ + else mcPROP_DT(dt); \ + } while(0) + +#define PROP_Z0 \ + do { \ + if (mcgravitation) { Coords mcLocG; int mc_ret; \ + double mc_dt, mc_gx, mc_gy, mc_gz; \ + mcLocG = rot_apply(ROT_A_CURRENT_COMP, coords_set(0,-9.8,0)); \ + coords_get(mcLocG, &mc_gx, &mc_gy, &mc_gz); \ + mc_ret = plane_intersect_Gfast(&mc_dt, -mc_gz/2, -mcnlvz, -mcnlz); \ + if (mc_ret && mc_dt>0) PROP_GRAV_DT(mc_dt, mc_gx, mc_gy, mc_gz); \ + else ABSORB; }\ + else mcPROP_Z0; \ + } while(0) + + +#define mcPROP_Z0 \ + do { \ + double mc_dt; \ + if(mcnlvz == 0) ABSORB; \ + mc_dt = -mcnlz/mcnlvz; \ + if(mc_dt < 0) ABSORB; \ + mcnlx += mcnlvx*mc_dt; \ + mcnly += mcnlvy*mc_dt; \ + mcnlt += mc_dt; \ + mcnlz = 0; \ + } while(0) + +#define vec_prod(x, y, z, x1, y1, z1, x2, y2, z2) \ + do { \ + double mcvp_tmpx, mcvp_tmpy, mcvp_tmpz; \ + mcvp_tmpx = (y1)*(z2) - (y2)*(z1); \ + mcvp_tmpy = (z1)*(x2) - (z2)*(x1); \ + mcvp_tmpz = (x1)*(y2) - (x2)*(y1); \ + (x) = mcvp_tmpx; (y) = mcvp_tmpy; (z) = mcvp_tmpz; \ + } while(0) + +#define scalar_prod(x1, y1, z1, x2, y2, z2) \ + ((x1)*(x2) + (y1)*(y2) + (z1)*(z2)) + +#define NORM(x,y,z) \ + do { \ + double mcnm_tmp = sqrt((x)*(x) + (y)*(y) + (z)*(z)); \ + if(mcnm_tmp != 0.0) \ + { \ + (x) /= mcnm_tmp; \ + (y) /= mcnm_tmp; \ + (z) /= mcnm_tmp; \ + } \ + } while(0) + +#define rotate(x, y, z, vx, vy, vz, phi, ax, ay, az) \ + do { \ + double mcrt_tmpx = (ax), mcrt_tmpy = (ay), mcrt_tmpz = (az); \ + double mcrt_vp, mcrt_vpx, mcrt_vpy, mcrt_vpz; \ + double mcrt_vnx, mcrt_vny, mcrt_vnz, mcrt_vn1x, mcrt_vn1y, mcrt_vn1z; \ + double mcrt_bx, mcrt_by, mcrt_bz; \ + double mcrt_cos, mcrt_sin; \ + NORM(mcrt_tmpx, mcrt_tmpy, mcrt_tmpz); \ + mcrt_vp = scalar_prod((vx), (vy), (vz), mcrt_tmpx, mcrt_tmpy, mcrt_tmpz); \ + mcrt_vpx = mcrt_vp*mcrt_tmpx; \ + mcrt_vpy = mcrt_vp*mcrt_tmpy; \ + mcrt_vpz = mcrt_vp*mcrt_tmpz; \ + mcrt_vnx = (vx) - mcrt_vpx; \ + mcrt_vny = (vy) - mcrt_vpy; \ + mcrt_vnz = (vz) - mcrt_vpz; \ + vec_prod(mcrt_bx, mcrt_by, mcrt_bz, \ + mcrt_tmpx, mcrt_tmpy, mcrt_tmpz, mcrt_vnx, mcrt_vny, mcrt_vnz); \ + mcrt_cos = cos((phi)); mcrt_sin = sin((phi)); \ + mcrt_vn1x = mcrt_vnx*mcrt_cos + mcrt_bx*mcrt_sin; \ + mcrt_vn1y = mcrt_vny*mcrt_cos + mcrt_by*mcrt_sin; \ + mcrt_vn1z = mcrt_vnz*mcrt_cos + mcrt_bz*mcrt_sin; \ + (x) = mcrt_vpx + mcrt_vn1x; \ + (y) = mcrt_vpy + mcrt_vn1y; \ + (z) = mcrt_vpz + mcrt_vn1z; \ + } while(0) + +Coords coords_set(MCNUM x, MCNUM y, MCNUM z); +Coords coords_get(Coords a, MCNUM *x, MCNUM *y, MCNUM *z); +Coords coords_add(Coords a, Coords b); +Coords coords_sub(Coords a, Coords b); +Coords coords_neg(Coords a); + +void rot_set_rotation(Rotation t, double phx, double phy, double phz); +void rot_mul(Rotation t1, Rotation t2, Rotation t3); +void rot_copy(Rotation dest, Rotation src); +void rot_transpose(Rotation src, Rotation dst); +Coords rot_apply(Rotation t, Coords a); +void mccoordschange(Coords a, Rotation t, double *x, double *y, double *z, + double *vx, double *vy, double *vz, double *time, + double *s1, double *s2); +void mccoordschange_polarisation(Rotation t, + double *sx, double *sy, double *sz); +double mcestimate_error(double N, double p1, double p2); +void mcreadparams(void); + +void mcsetstate(double x, double y, double z, double vx, double vy, double vz, + double t, double sx, double sy, double sz, double p); +void mcgenstate(void); +double randnorm(void); +void normal_vec(double *nx, double *ny, double *nz, + double x, double y, double z); +int box_intersect(double *dt_in, double *dt_out, double x, double y, double z, + double vx, double vy, double vz, double dx, double dy, double dz); +int cylinder_intersect(double *t0, double *t1, double x, double y, double z, + double vx, double vy, double vz, double r, double h); +int sphere_intersect(double *t0, double *t1, double x, double y, double z, + double vx, double vy, double vz, double r); +/* ADD: E. Farhi, Aug 6th, 2001 plane_intersect_Gfast */ +int plane_intersect_Gfast(double *Idt, + double A, double B, double C); +void randvec_target_circle(double *xo, double *yo, double *zo, + double *solid_angle, double xi, double yi, double zi, double radius); +#define randvec_target_sphere randvec_target_circle +void randvec_target_rect_angular(double *xo, double *yo, double *zo, + double *solid_angle, + double xi, double yi, double zi, double height, double width, Rotation A); +void randvec_target_rect(double *xo, double *yo, double *zo, + double *solid_angle, + double xi, double yi, double zi, double height, double width, Rotation A); +void extend_list(int count, void **list, int *size, size_t elemsize); + +void mcset_ncount(double count); +double mcget_ncount(void); +double mcget_run_num(void); +int mcstas_main(int argc, char *argv[]); + +/* file i/o definitions and function prototypes */ + +struct mcformats_struct { + char *Name; /* may also specify: append, partial(hidden), binary */ + char *Extension; + char *Header; + char *Footer; + char *BeginSection; + char *EndSection; + char *AssignTag; + char *BeginData; + char *BeginErrors; + char *BeginNcount; + char *EndData; + char *EndErrors; + char *EndNcount; + }; + +/* in order to be fully portable, the format specifiers must mention each + * fprintf parameters. In case we do not want to use some of them, we must + * set the precision to 0. + * ex: fprintf(f, "printed:%1$s %3$s not printed: %2$.0s\n", "1", "2", "3"); + * such are the joys of ANSI C99 and Single Unix Specification ! + * This 0-precision for unused data is automatically checked in mccheck_format + * Maximum number of positional arguments is NL_RGMAX, which is 9 on System V + * machines (Dec/Compaq/HP). Some more enjoyable stuff !! -> we use pfprintf + */ +/* The mcformat.Name may contain additional keywords: + * partial: will not show the monitor in mcstas.sim, omit the format footer + * (usually the end data), and not print the monitor sum in stdout + */ + +#ifndef MCSTAS_VERSION +#define MCSTAS_VERSION "External Run-time" +#endif + +/* function prototypes */ +void mcuse_format(char *format); +double mcdetector_out(char *cname, double p0, double p1, double p2, char *filename); +double mcdetector_out_0D(char *t, double p0, double p1, double p2, char *c); +double mcdetector_out_1D(char *t, char *xl, char *yl, + char *xvar, double x1, double x2, int n, + double *p0, double *p1, double *p2, char *f, char *c); +double mcdetector_out_2D(char *t, char *xl, char *yl, + double x1, double x2, double y1, double y2, int m, + int n, double *p0, double *p1, double *p2, char *f, char *c); +double mcdetector_out_3D(char *t, char *xl, char *yl, char *zl, + char *xvar, char *yvar, char *zvar, + double x1, double x2, double y1, double y2, double z1, double z2, int m, + int n, int p, double *p0, double *p1, double *p2, char *f, char *c); +void mcheader_out(FILE *f,char *parent, + int m, int n, int p, + char *xlabel, char *ylabel, char *zlabel, char *title, + char *xvar, char *yvar, char *zvar, + double x1, double x2, double y1, double y2, double z1, double z2, + char *filename); /* output header for user data file */ +void mcinfo_simulation(FILE *f, struct mcformats_struct format, + char *pre, char *name); /* used to add sim parameters (e.g. in Res_monitor) */ +void mcsiminfo_init(FILE *f); +void mcsiminfo_close(void); + + +#ifndef FLT_MAX +#define FLT_MAX 3.40282347E+38F /* max decimal value of a "float" */ +#endif + +/* Retrieve component information from the kernel */ +/* Name, position and orientation (both absolute and relative) */ +/* Any component: For "redundancy", see comment by KN */ +#define tmp_name_comp(comp) #comp +#define NAME_COMP(comp) tmp_name_comp(comp) +#define tmp_pos_a_comp(comp) (mcposa ## comp) +#define POS_A_COMP(comp) tmp_pos_a_comp(comp) +#define tmp_pos_r_comp(comp) (mcposr ## comp) +#define POS_R_COMP(comp) tmp_pos_r_comp(comp) +#define tmp_rot_a_comp(comp) (mcrota ## comp) +#define ROT_A_COMP(comp) tmp_rot_a_comp(comp) +#define tmp_rot_r_comp(comp) (mcrotr ## comp) +#define ROT_R_COMP(comp) tmp_rot_r_comp(comp) + +/* Current component */ +#define NAME_CURRENT_COMP NAME_COMP(mccompcurname) +#define INDEX_CURRENT_COMP mccompcurindex +#define POS_A_CURRENT_COMP POS_A_COMP(mccompcurname) +#define POS_R_CURRENT_COMP POS_R_COMP(mccompcurname) +#define ROT_A_CURRENT_COMP ROT_A_COMP(mccompcurname) +#define ROT_R_CURRENT_COMP ROT_R_COMP(mccompcurname) + +#define SCATTERED mcScattered + +#endif /* MCSTAS_R_H */ +/* End of file "mcstas-r.h". */ + +#line 546 "dmcafter.c" + +#line 1 "mcstas-r.c" +/******************************************************************************* +* +* McStas, neutron ray-tracing package +* Copyright 1997-2002, All rights reserved +* Risoe National Laboratory, Roskilde, Denmark +* Institut Laue Langevin, Grenoble, France +* +* Runtime: share/mcstas-r.c +* +* %Identification +* Written by: KN +* Date: Aug 29, 1997 +* Release: McStas 1.6 +* Version: 1.7 +* +* Runtime system for McStas. +* Embedded within instrument in runtime mode. +* +* Usage: Automatically embbeded in the c code whenever required. +* +* $Id$ +* +* $Log$ +* Revision 1.1 2007/01/30 03:19:43 koennecke +* - Fixed state monitor eclipse commit problems. Siiiiiiiggggghhhhhh! +* +* Revision 1.85 2004/03/05 17:43:47 farhi +* Default instr parameters are now correctly handled in all instrument usage cases. +* +* Revision 1.84 2004/03/03 13:41:23 pkwi +* Corrected error in relation to instrument default values: 0's were used in all cases. +* +* Revision 1.83 2004/02/26 12:53:27 farhi +* Scilab format now enables more than one monitor file for a single component +* (e.g. Monitor_nD with multiple detectors). +* +* Revision 1.82 2004/02/23 12:48:42 farhi +* Additional check for default value and unset parameters +* +* Revision 1.81 2004/02/19 14:42:52 farhi +* Experimental Octave/OpenGENIE output format (for ISIS) +* +* Revision 1.80 2004/01/23 16:14:12 pkwi +* Updated version of Mersenne Twister algorithm. make test'ed ok on my machine. +* +* Revision 1.79 2003/11/28 18:08:32 farhi +* Corrected error for IDL import +* +* Revision 1.77 2003/10/22 15:51:26 farhi +* -i also displays default parameter values (if any), which may be +* read by mcgui for init of Run Simulation dialog +* +* Revision 1.76 2003/10/22 09:18:00 farhi +* Solved name conflict problem for Matlab/Scilab by adding 'mc_' prefix +* to all component/file field names. Works ok for both, and also in binary. +* +* Revision 1.75 2003/10/21 14:08:12 pkwi +* Rectangular focusing improved: Renamed randvec_target_rect to randvec_target_rect_angular. Wrote new randvec_target_rect routine, w/h in metres. Both routines use use component orientation (ROT_A_CURRENT_COMP) as input. +* +* Modifications to Res_sample and V_sample to match new features of the runtime. +* +* Revision 1.74 2003/10/21 11:54:48 farhi +* instrument default parameter value handling now works better +* either from args or from mcreadparam (prompt) +* +* Revision 1.73 2003/09/05 08:59:17 farhi +* added INSTRUMENT parameter default value grammar +* mcinputtable now has also default values +* mcreadpar now uses default values if parameter not given +* extended instr_formal parameter struct +* extended mcinputtable structure type +* +* Revision 1.72 2003/08/26 12:32:43 farhi +* Corrected 4PI random vector generation to retain initial vector length +* +* Revision 1.71 2003/08/20 09:25:00 farhi +* Add the instrument Source tag in scan files (origin of data !) +* +* Revision 1.70 2003/08/12 13:35:52 farhi +* displays known signals list in instrument help (-h) +* +* Revision 1.68 2003/06/17 14:21:54 farhi +* removed 'clear %4$s' in Scilab/Matlab 'end of section' format which +* caused pb when comp_name == file_name +* +* Revision 1.67 2003/06/12 10:22:00 farhi +* -i show info as McStas format, --info use MCSTAS_FORMAT or --format setting +* +* Revision 1.66 2003/06/10 11:29:58 pkwi +* Corrected multiple parse errors: Added two missing sets of curly brackets { } in parameter parsing function. +* +* Revision 1.65 2003/06/05 09:25:59 farhi +* restore header support in data files when --format option found +* +* Revision 1.64 2003/05/26 10:21:00 farhi +* Correct core dump for instrument STRING parameters in 'string printer' +* +* Revision 1.63 2003/05/20 11:54:38 farhi +* make sighandler not restart SAVE when already saving (USR2) +* +* Revision 1.62 2003/05/16 12:13:03 farhi +* added path rehash for Matlab mcload_inline +* +* Revision 1.61 2003/04/25 16:24:44 farhi +* corrected 4PI scattering from randvec_* functions causing mcdisplay to crash +* when using (0,0,0) vector for coordinate transformations +* +* Revision 1.60 2003/04/16 14:55:47 farhi +* Major change in saving data so that it's shown just like PGPLOT +* and axes+labels should follow data orientation (if transposed) +* when in binary mode, sets -a as default. Use +a to force text header +* +* Revision 1.59 2003/04/09 15:51:33 farhi +* Moved MCSTAS_FORMAT define +* +* Revision 1.58 2003/04/08 18:55:56 farhi +* Made XML format more NeXus compliant +* +* Revision 1.57 2003/04/07 11:50:50 farhi +* Extended the way mcplot:plotter is assigned. Set --portable ok +* Handle Scilab:Tk and ~GTk menu (shifted) +* Updated help in mcrun and mcstas-r.c +* +* Revision 1.56 2003/04/04 18:36:12 farhi +* Corrected $ and % chars for IDL format, conflicting with pfprintf (Dec/SGI) +* +* Revision 1.55 2003/04/04 15:11:08 farhi +* Use MCSTAS_FORMAT env var for default plotter, or use mcstas_config +* Corrected strlen(NULL pointer) for getenv(MCSTAS_FORMAT)==NULL +* +* Revision 1.54 2003/04/04 14:26:25 farhi +* Managed --no-runtime to work. Use MCSTAS_FORMAT env/define for default format +* Make --no-output-files still print out the integrated counts +* +* Revision 1.53 2003/02/18 09:10:52 farhi +* Just changed a message (warning for -a flag binary) +* +* Revision 1.51 2003/02/11 12:28:46 farhi +* Variouxs bug fixes after tests in the lib directory +* mcstas_r : disable output with --no-out.. flag. Fix 1D McStas output +* read_table:corrected MC_SYS_DIR -> MCSTAS define +* monitor_nd-lib: fix Log(signal) log(coord) +* HOPG.trm: reduce 4000 points -> 400 which is enough and faster to resample +* Progress_bar: precent -> percent parameter +* CS: ---------------------------------------------------------------------- +* +* Revision 1.50 2003/02/06 14:25:05 farhi +* Made --no-output-files work again and 1D McStas data 4 columns again +* +* : ---------------------------------------------------------------------- +* +* Revision 1.7 2002/10/19 22:46:21 ef +* gravitation for all with -g. Various output formats. +* +* Revision 1.6 2002/09/17 12:01:21 ef +* changed randvec_target_sphere to circle +* added randvec_target_rect +* +* Revision 1.5 2002/09/03 19:48:01 ef +* corrected randvec_target_sphere. created target_rect. +* +* Revision 1.4 2002/09/02 18:59:05 ef +* moved adapt_tree functions to independent lib. Updated sighandler. +* +* Revision 1.3 2002/08/28 11:36:37 ef +* Changed to lib/share/c code +* +* Revision 1.2 2001/10/10 11:36:37 ef +* added signal handler +* +* Revision 1.1 1998/08/29 11:36:37 kn +* Initial revision +* +*******************************************************************************/ + +#include +#include +#include +#include + +#ifndef MCSTAS_R_H +#include "mcstas-r.h" +#endif + + +#ifdef MC_ANCIENT_COMPATIBILITY +int mctraceenabled = 0; +int mcdefaultmain = 0; +#endif + +static long mcseed = 0; +mcstatic int mcdotrace = 0; +static int mcascii_only = 0; +static int mcdisable_output_files = 0; +static int mcsingle_file= 0; +mcstatic int mcgravitation = 0; +static long mcstartdate = 0; + +mcstatic FILE *mcsiminfo_file = NULL; +static char *mcdirname = NULL; +static char *mcsiminfo_name= "mcstas"; +mcstatic char mcsig_message[256]; /* ADD: E. Farhi, Sep 20th 2001 */ + +/* Multiple output format support. ========================================== */ + +#define mcNUMFORMATS 8 +#ifndef MCSTAS_FORMAT +#define MCSTAS_FORMAT "McStas" /* default format */ +#endif + +mcstatic struct mcformats_struct mcformat; + +mcstatic struct mcformats_struct mcformats[mcNUMFORMATS] = { + { "McStas", "sim", + "%1$sFormat: %4$s file\n" + "%1$sURL: http://neutron.risoe.dk/\n" + "%1$sEditor: %6$s\n" + "%1$sCreator:%2$s simulation (McStas " MCSTAS_VERSION ")\n" + "%1$sDate: Simulation started (%8$li) %5$s\n" + "%1$sFile: %3$s\n", + "%1$sEndDate:%5$s\n", + "%1$sbegin %2$s\n", + "%1$send %2$s\n", + "%1$s%3$s: %4$s\n", + "", + "%1$sErrors [%2$s/%4$s]: \n", + "%1$sEvents [%2$s/%4$s]: \n", + "", "", "" }, + { "Scilab", "sci", + "function mc_%7$s = get_%7$s(p)\n" + "// %4$s function issued from McStas on %5$s\n" + "// McStas simulation %2$s: %3$s" MC_PATHSEP_S "%4$s\n" + "// import data using exec('%7$s.sci',-1); s=get_%7$s('plot');\nmode(-1); //silent execution\n" + "if argn(2) > 0, p=1; else p=0; end\n" + "mc_%7$s = struct();\n" + "mc_%7$s.Format ='%4$s';\n" + "mc_%7$s.URL ='http://neutron.risoe.dk';\n" + "mc_%7$s.Editor ='%6$s';\n" + "mc_%7$s.Creator='%2$s McStas " MCSTAS_VERSION " simulation';\n" + "mc_%7$s.Date =%8$li; // for getdate\n" + "mc_%7$s.File ='%3$s';\n", + "mc_%7$s.EndDate=%8$li; // for getdate\nendfunction\n" + "function d=mcload_inline(d)\n" + "// local inline func to load data\n" + "execstr(['S=['+part(d.type,10:(length(d.type)-1))+'];']);\n" + "if ~length(d.data)\n" + " if ~length(strindex(d.format, 'binary'))\n" + " exec(d.filename,-1);p=d.parent;\n" + " if ~execstr('d2='+d.func+'();','errcatch'),d=d2; d.parent=p;end\n" + " else\n" + " if length(strindex(d.format, 'float')), t='f';\n" + " elseif length(strindex(d.format, 'double')), t='d';\n" + " else return; end\n" + " fid=mopen(d.filename, 'rb');\n" + " pS = prod(S);\n" + " x = mget(3*pS, t, fid);\n" + " d.data =matrix(x(1:pS), S);\n" + " if length(x) >= 3*pS,\n" + " d.errors=matrix(x((pS+1):(2*pS)), S);\n" + " d.events=matrix(x((2*pS+1):(3*pS)), S);end\n" + " mclose(fid);\n" + " return\n" + " end\n" + "end\n" + "endfunction\n" + "function d=mcplot_inline(d,p)\n" + "// local inline func to plot data\n" + "if ~length(strindex(d.type,'0d')), d=mcload_inline(d); end\n" + "if ~p, return; end;\n" + "execstr(['l=[',d.xylimits,'];']); S=size(d.data);\n" + "t1=['['+d.parent+'] '+d.filename+': '+d.title];t = [t1;[' '+d.variables+'=['+d.values+']'];[' '+d.signal];[' '+d.statistics]];\n" + "mprintf('%%s\\n',t(:));\n" + "if length(strindex(d.type,'0d')),return;\n" + "else\nw=winsid();if length(w),w=w($)+1; else w=0; end\n" + "xbasr(w); xset('window',w);\n" + "if length(strindex(d.type,'2d'))\n" + " d.x=linspace(l(1),l(2),S(2)); d.y=linspace(l(3),l(4),S(1)); z=d.data;\n" + " xlab=d.xlabel; ylab=d.ylabel; x=d.x; y=d.y;\n" + " fz=max(abs(z));fx=max(abs(d.x));fy=max(abs(d.y));\n" + " if fx>0,fx=round(log10(fx)); x=x/10^fx; xlab=xlab+' [*10^'+string(fx)+']'; end\n" + " if fy>0,fy=round(log10(fy)); y=y/10^fy; ylab=ylab+' [*10^'+string(fy)+']'; end\n" + " if fz>0,fz=round(log10(fz)); z=z/10^fz; t1=t1+' [*10^'+string(fz)+']'; end\n" + " xset('colormap',hotcolormap(64));\n" + " plot3d1(x,y,z',90,0,xlab+'@'+ylab+'@'+d.zlabel); xtitle(t);\n" + "else\nd.x=linspace(l(1),l(2),max(S));\n" + " plot2d(d.x,d.data);xtitle(t,d.xlabel,d.ylabel);end\nend\n" + "xname(t1);\nendfunction\n" + "mc_%7$s=get_%7$s();\n", + "// Section %2$s [%3$s] (level %7$d)\n" + "%1$st=[]; execstr('t=mc_%4$s.class','errcatch'); if ~length(t), mc_%4$s = struct(); end; mc_%4$s.class = '%2$s';", + "%1$smc_%6$s.mc_%4$s = 0; mc_%6$s.mc_%4$s = mc_%4$s;\n", + "%1$smc_%2$s.%3$s = '%4$s';\n", + "%1$smc_%2$s.func='get_%2$s';\n%1$smc_%2$s.data = [ ", + "%1$serrors = [ ", + "%1$sevents = [ ", + " ]; // end of data\n%1$sif length(mc_%2$s.data) == 0, single_file=0; else single_file=1; end\n%1$smc_%2$s=mcplot_inline(mc_%2$s,p);\n", + " ]; // end of errors\n%1$sif single_file == 1, mc_%2$s.errors=errors; end\n", + " ]; // end of events\n%1$sif single_file == 1, mc_%2$s.events=events; end\n"}, + { "Matlab", "m", + "function mc_%7$s = get_%7$s(p)\n" + "%% %4$s function issued from McStas on %5$s\n" + "%% McStas simulation %2$s: %3$s\n" + "%% import data using s=%7$s('plot');\n" + "if nargout == 0 | nargin > 0, p=1; else p=0; end\n" + "mc_%7$s.Format ='%4$s';\n" + "mc_%7$s.URL ='http://neutron.risoe.dk';\n" + "mc_%7$s.Editor ='%6$s';\n" + "mc_%7$s.Creator='%2$s McStas " MCSTAS_VERSION " simulation';\n" + "mc_%7$s.Date =%8$li; %% for datestr\n" + "mc_%7$s.File ='%3$s';\n", + "mc_%7$s.EndDate=%8$li; %% for datestr\n" + "function d=mcload_inline(d)\n" + "%% local inline function to load data\n" + "S=d.type; eval(['S=[ ' S(10:(length(S)-1)) ' ];']);\n" + "if isempty(d.data)\n" + " if ~length(findstr(d.format, 'binary'))\n" + " copyfile(d.filename,[d.func,'.m']);p=d.parent;path(path);\n" + " eval(['d=',d.func,';']);d.parent=p;delete([d.func,'.m']);\n" + " else\n" + " if length(findstr(d.format, 'float')), t='single';\n" + " elseif length(findstr(d.format, 'double')), t='double';\n" + " else return; end\n" + " if length(S) == 1, S=[S 1]; end\n" + " fid=fopen(d.filename, 'r');\n" + " pS = prod(S);\n" + " x = fread(fid, 3*pS, t);\n" + " d.data =reshape(x(1:pS), S);\n" + " if prod(size(x)) >= 3*pS,\n" + " d.errors=reshape(x((pS+1):(2*pS)), S);\n" + " d.events=reshape(x((2*pS+1):(3*pS)), S);end\n" + " fclose(fid);\n" + " return\n" + " end\n" + "end\n" + "return;\n" + "function d=mcplot_inline(d,p)\n" + "%% local inline function to plot data\n" + "if isempty(findstr(d.type,'0d')), d=mcload_inline(d); end\nif ~p, return; end;\n" + "eval(['l=[',d.xylimits,'];']); S=size(d.data);\n" + "t1=['[',d.parent,'] ',d.filename,': ',d.title];t = strvcat(t1,[' ',d.variables,'=[',d.values,']'],[' ',d.signal],[' ',d.statistics]);\n" + "disp(t);\n" + "if ~isempty(findstr(d.type,'0d')), return; end\n" + "figure; if ~isempty(findstr(d.type,'2d'))\n" + "d.x=linspace(l(1),l(2),S(2)); d.y=linspace(l(3),l(4),S(1));\n" + "surface(d.x,d.y,d.data);\n" + "else\nd.x=linspace(l(1),l(2),max(S));\nplot(d.x,d.data);end\n" + "xlabel(d.xlabel); ylabel(d.ylabel); title(t); axis tight;" + "set(gca,'position',[.18,.18,.7,.65]); set(gcf,'name',t1);grid on;\n" + "if ~isempty(findstr(d.type,'2d')), colorbar; end\n", + "%% Section %2$s [%3$s] (level %7$d)\n" + "mc_%4$s.class = '%2$s';", + "mc_%6$s.mc_%4$s = mc_%4$s;\n", + "%1$smc_%2$s.%3$s = '%4$s';\n", + "%1$smc_%2$s.func='%2$s';\n%1$smc_%2$s.data = [ ", + "%1$serrors = [ ", + "%1$sevents = [ ", + " ]; %% end of data\nif length(mc_%2$s.data) == 0, single_file=0; else single_file=1; end\nmc_%2$s=mcplot_inline(mc_%2$s,p);\n", + " ]; %% end of errors\nif single_file, mc_%2$s.errors=errors; end\n", + " ]; %% end of events\nif single_file, mc_%2$s.events=events; end\n"}, + { "IDL", "pro", + "function mcload_inline,d\n" + "; local inline function to load external data\n" + "S=d.type & a=execute('S=long(['+strmid(S,9,strlen(S)-10)+'])')\n" + "if strpos(d.format, 'binary') lt 0 then begin\n" + " p=d.parent\n" + " x=read_binary(d.filename)\n" + " get_lun, lun\n" + " openw,lun,d.func+'.pro'\n" + " writeu, lun,x\n" + " free_lun,lun\n" + " resolve_routine, d.func, /is_func, /no\n" + " d=call_function(d.func)\n" + "endif else begin\n" + " if strpos(d.format, 'float') ge 0 then t=4 $\n" + " else if strpos(d.format, 'double') ge 0 then t=5 $\n" + " else return,d\n" + " x=read_binary(d.filename, data_type=t)\n" + " pS=n_elements(S)\nif pS eq 1 then pS=long(S) $\n" + " else if pS eq 2 then pS=long(S(0)*S(1)) $\n" + " else pS=long(S(0)*S(1)*S(2))\n" + " pS=pS(0)\nstv,d,'data',reform(x(0:(pS-1)),S)\n" + " d.data=transpose(d.data)\n" + " if n_elements(x) ge long(3*pS) then begin\n" + " stv,d,'errors',reform(x(pS:(2*pS-1)),S)\n" + " stv,d,'events',reform(x((2*pS):(3*pS-1)),S)\n" + " d.errors=transpose(d.errors)\n" + " d.events=transpose(d.events)\n" + " endif\n" + "endelse\n" + "return,d\nend ; FUN load\n" + "function mcplot_inline,d,p\n" + "; local inline function to plot data\n" + "if size(d.data,/typ) eq 7 and strpos(d.type,'0d') lt 0 then d=mcload_inline(d)\n" + "if p eq 0 or strpos(d.type,'0d') ge 0 then return, d\n" + "S=d.type & a=execute('S=long(['+strmid(S,9,strlen(S)-10)+'])')\n" + "stv,d,'data',reform(d.data,S,/over)\n" + "if total(strpos(tag_names(d),'ERRORS')+1) gt 0 then begin\n" + " stv,d,'errors',reform(d.errors,S,/over)\n" + " stv,d,'events',reform(d.events,S,/over)\n" + "endif\n" + "d.xylimits=strjoin(strsplit(d.xylimits,' ',/extract),',') & a=execute('l=['+d.xylimits+']')\n" + "t1='['+d.parent+'] '+d.filename+': '+d.title\n" + "t=[t1,' '+d.variables+'=['+d.values+']',' '+d.signal,' '+d.statistics]\n" + "print,t\n" + "if strpos(d.type,'0d') ge 0 then return,d\n" + "d.xlabel=strjoin(strsplit(d.xlabel,'`!\"£^&*()-+=|\\,.<>/?@''~#{[}]',/extract),'_')\n" + "d.ylabel=strjoin(strsplit(d.ylabel,'`!\"£^&*()-+=|\\,.<>/?@''~#{[}]',/extract),'_')\n" + "stv,d,'x',l(0)+indgen(S(0))*(l(1)-l(0))/S(0)\n" + "if strpos(d.type,'2d') ge 0 then begin\n" + " name={DATA:d.func,IX:d.xlabel,IY:d.ylabel}\n" + " stv,d,'y',l(2)+indgen(S(1))*(l(3)-l(2))/S(1)\n" + " live_surface,d.data,xindependent=d.x,yindependent=d.y,name=name,reference_out=Win\n" + "endif else begin\n" + " name={DATA:d.func,I:d.xlabel}\n" + " live_plot,d.data,independent=d.x,name=name,reference_out=Win\n" + "endelse\n" + "live_text,t,Window_In=Win.Win,location=[0.3,0.9]\n" + "return,d\nend ; FUN plot\n" + "pro stv,S,T,V\n" + "; procedure set-tag-value that does S.T=V\n" + "sv=size(V)\n" + "T=strupcase(T)\n" + "TL=strupcase(tag_names(S))\n" + "id=where(TL eq T)\n" + "sz=[0,0,0]\n" + "vd=n_elements(sv)-2\n" + "type=sv[vd]\n" + "if id(0) ge 0 then d=execute('sz=SIZE(S.'+T+')')\n" + "if (sz(sz(0)+1) ne sv(sv(0)+1)) or (sz(0) ne sv(0)) $\n" + " or (sz(sz(0)+2) ne sv(sv(0)+2)) $\n" + " or type eq 8 then begin\n" + " ES = ''\n" + " for k=0,n_elements(TL)-1 do begin\n" + " case TL(k) of\n" + " T:\n" + " else: ES=ES+','+TL(k)+':S.'+TL(k)\n" + " endcase\n" + " endfor\n" + " d=execute('S={'+T+':V'+ES+'}')\n" + "endif else d=execute('S.'+T+'=V')\n" + "end ; PRO stv\n" + "function %7$s,plot=plot\n" + "; %4$s function issued from McStas on %5$s\n" + "; McStas simulation %2$s: %3$s\n" + "; import using s=%7$s(/plot)\n" + "if keyword_set(plot) then p=1 else p=0\n" + "%7$s={Format:'%4$s',URL:'http://neutron.risoe.dk'," + "Editor:'%6$s',$\n" + "Creator:'%2$s McStas " MCSTAS_VERSION " simulation',$\n" + "Date:%8$li," + "File:'%3$s'}\n", + "stv,%7$s,'EndDate',%8$li ; for systime\nreturn, %7$s\nend\n", + "; Section %2$s [%3$s] (level %7$d)\n" + "%1$s%4$s={class:'%2$s'}\n", + "%1$sstv,%6$s,'%4$s',%4$s\n", + "%1$sstv,%2$s,'%3$s','%4$s'\n", + "%1$sstv,%2$s,'func','%2$s' & data=[ ", + "%1$sif single_file ne 0 then begin errors=[ ", + "%1$sif single_file ne 0 then begin events=[ ", + " ]\n%1$sif size(data,/type) eq 7 then single_file=0 else single_file=1\n" + "%1$sstv,%2$s,'data',data & data=0 & %2$s=mcplot_inline(%2$s,p)\n", + " ]\n%1$sstv,%2$s,'errors',reform(errors,%14$d,%15$d,/over) & errors=0\n%1$sendif\n", + " ]\n%1$sstv,%2$s,'events',reform(events,%14$d,%15$d,/over) & events=0\n%1$sendif\n\n"}, + { "XML", "xml", + "\n\n" + "\n" + "%5$s\n", + "%5$s\n\n", + "%1$s\n", + "%1$s\n", + "%1$s<%3$s>%4$s\n", + "%1$s<%6$s long_name=\"%5$s\" axis=\"1\" primary=\"1\" min=\"%17$g\"" + " max=\"%18$g\" dims=\"%14$d\" range=\"1\">\n" + "%1$s<%8$s long_name=\"%7$s\" axis=\"2\" primary=\"1\" min=\"%19$g\"" + " max=\"%20$g\" dims=\"%15$d\" range=\"1\">\n" + "%1$s<%10$s long_name=\"%9$s\" axis=\"3\" primary=\"1\" min=\"%21$g\"" + " max=\"%22$g\" dims=\"%16$d\" range=\"1\">\n" + "%1$s", + "%1$s", "%1$s", + "%1$s\n", "%1$s\n", "%1$s\n"}, + { "HTML", "html", + "\n" + "\n" + "\n" + "\n" + "[McStas %2$s]%3$s\n" + "

" + "McStas simulation %2$s: %3$s


\n" + "This simulation report was automatically created by" + " McStas " MCSTAS_VERSION "
\n" + "
User:   %6$s
\n" + "%1$sCreator: %2$s McStas simulation
\n" + "%1$sDate: (%8$li) %5$s
\n", + "EndDate: (%8$li) %5$s
\n", + "%1$s%2$s %3$s " + "[child of %5$s]
\n" + "%1$sAssociated data file %3$s
\n" + "%1$sAssociated %2$s image %3$s.png
(when available)\n" + "%1$s\"%2$s

\n", + "[end of %2$s %3$s]
\n", + "%1$s%3$s: %4$s
\n", + "%1$sDATA
\n", + "%1$sERRORS
\n","%1$sEVENTS
\n", + "%1$sEnd of DATA
\n", "%1$sEnd of ERRORS
\n", "%1$sEnd of EVENTS
\n"}, + { "OpenGENIE", "gcl", + "PROCEDURE get_%7$s\n" + "RESULT %7$s\n" + "# %4$s procedure issued from McStas on %5$s\n" + "# McStas simulation %2$s: %3$s" MC_PATHSEP_S "%4$s\n" + "# import data using s=get_%7$s();\n" + "%7$s = fields();\n" + "%7$s.Format =\"%4$s\";\n" + "%7$s.URL =\"http://neutron.risoe.dk\";\n" + "%7$s.Editor =\"%6$s\";\n" + "%7$s.Creator=\"%2$s McStas " MCSTAS_VERSION " simulation\";\n" + "%7$s.Date =%8$li;\n" + "%7$s.File =\"%3$s\";\n", + "%7$s.EndDate=%8$li;\nENDPROCEDURE\n", + "# Section %2$s [%3$s] (level %7$d)\n" + "%1$s%4$s = fields(); %4$s.class = \"%2$s\";", + "%1$s%6$s.%4$s = %4$s; free \"%4$s\";\n", + "%1$s%2$s.%3$s = \"%4$s\";\n", + "%1$s%2$s.func=\"get_%2$s\";\n%1$s%2$s.data = [ ", + "%1$sIF (single_file = 1); %2$s.errors = [ ", + "%1$sIF (single_file = 1); %2$s.ncount = [ ", + " ] array(%14$d,%15$d); # end of data\nIF (length(%2$s.data) = 0); single_file=0; ELSE single_file=1; ENDIF\n%2$s=mcplot_inline(%2$s,p);\n", + " ] array(%14$d,%15$d); # end of errors\nENDIF\n", + " ] array(%14$d,%15$d); # end of ncount\nENDIF\n"}, + { "Octave", "m", + "function mc_%7$s = get_%7$s(p)\n" + "%% %4$s function issued from McStas on %5$s\n" + "%% McStas simulation %2$s: %3$s\n" + "%% import data using s=%7$s('plot');\n" + "if nargin > 0, p=1; else p=0; end\n" + "mc_%7$s.Format ='%4$s';\n" + "mc_%7$s.URL ='http://neutron.risoe.dk';\n" + "mc_%7$s.Editor ='%6$s';\n" + "mc_%7$s.Creator='%2$s McStas " MCSTAS_VERSION " simulation';\n" + "mc_%7$s.Date =%8$li; %% for datestr\n" + "mc_%7$s.File ='%3$s';\n", + "mc_%7$s.EndDate=%8$li; %% for datestr\nendfunction\n" + "if exist('mcload_inline'), return; end\n" + "function d=mcload_inline(d)\n" + "%% local inline function to load data\n" + "S=d.type; eval(['S=[ ' S(10:(length(S)-1)) ' ];']);\n" + "if isempty(d.data)\n" + " if ~length(findstr(d.format, 'binary'))\n" + " source(d.filename);p=d.parent;\n" + " eval(['d=get_',d.func,';']);d.parent=p;\n" + " else\n" + " if length(findstr(d.format, 'float')), t='float';\n" + " elseif length(findstr(d.format, 'double')), t='double';\n" + " else return; end\n" + " if length(S) == 1, S=[S 1]; end\n" + " fid=fopen(d.filename, 'r');\n" + " pS = prod(S);\n" + " x = fread(fid, 3*pS, t);\n" + " d.data =reshape(x(1:pS), S);\n" + " if prod(size(x)) >= 3*pS,\n" + " d.errors=reshape(x((pS+1):(2*pS)), S);\n" + " d.events=reshape(x((2*pS+1):(3*pS)), S);end\n" + " fclose(fid);\n" + " return\n" + " end\n" + "end\n" + "return;\nendfunction\n\n" + "function d=mcplot_inline(d,p)\n" + "%% local inline function to plot data\n" + "if isempty(findstr(d.type,'0d')), d=mcload_inline(d); end\nif ~p, return; end;\n" + "eval(['l=[',d.xylimits,'];']); S=size(d.data);\n" + "t1=['[',d.parent,'] ',d.filename,': ',d.title];t = strcat(t1,[' ',d.variables,'=[',d.values,']'],[' ',d.signal],[' ',d.statistics]);\n" + "disp(t);\n" + "if ~isempty(findstr(d.type,'0d')), return; end\n" + "xlabel(d.xlabel); ylabel(d.ylabel); title(t);" + "figure; if ~isempty(findstr(d.type,'2d'))\n" + "d.x=linspace(l(1),l(2),S(2)); d.y=linspace(l(3),l(4),S(1));\n" + "mesh(d.x,d.y,d.data);\n" + "else\nd.x=linspace(l(1),l(2),max(S));\nplot(d.x,d.data);end\nendfunction\n", + "%% Section %2$s [%3$s] (level %7$d)\n" + "mc_%4$s.class = '%2$s';", + "mc_%6$s.mc_%4$s = mc_%4$s;\n", + "%1$smc_%2$s.%3$s = '%4$s';\n", + "%1$smc_%2$s.func='%2$s';\n%1$smc_%2$s.data = [ ", + "%1$serrors = [ ", + "%1$sevents = [ ", + " ]; %% end of data\nif length(mc_%2$s.data) == 0, single_file=0; else single_file=1; end\nmc_%2$s=mcplot_inline(mc_%2$s,p);\n", + " ]; %% end of errors\nif single_file, mc_%2$s.errors=errors; end\n", + " ]; %% end of events\nif single_file, mc_%2$s.events=events; end\n"} + }; + +/* MCDISPLAY support. ======================================================= */ + +void mcdis_magnify(char *what){ + printf("MCDISPLAY: magnify('%s')\n", what); +} + +void mcdis_line(double x1, double y1, double z1, + double x2, double y2, double z2){ + printf("MCDISPLAY: multiline(2,%g,%g,%g,%g,%g,%g)\n", + x1,y1,z1,x2,y2,z2); +} + +void mcdis_multiline(int count, ...){ + va_list ap; + double x,y,z; + + printf("MCDISPLAY: multiline(%d", count); + va_start(ap, count); + while(count--) + { + x = va_arg(ap, double); + y = va_arg(ap, double); + z = va_arg(ap, double); + printf(",%g,%g,%g", x, y, z); + } + va_end(ap); + printf(")\n"); +} + +void mcdis_circle(char *plane, double x, double y, double z, double r){ + printf("MCDISPLAY: circle('%s',%g,%g,%g,%g)\n", plane, x, y, z, r); +} + +/* coordinates handling ===================================================== */ + +/******************************************************************************* +* Since we use a lot of geometric calculations using Cartesian coordinates, +* we collect some useful routines here. However, it is also permissible to +* work directly on the underlying struct coords whenever that is most +* convenient (that is, the type Coords is not abstract). +* +* Coordinates are also used to store rotation angles around x/y/z axis. +* +* Since coordinates are used much like a basic type (such as double), the +* structure itself is passed and returned, rather than a pointer. +* +* At compile-time, the values of the coordinates may be unknown (for example +* a motor position). Hence coordinates are general expressions and not simple +* numbers. For this we used the type Coords_exp which has three CExp +* fields. For runtime (or calculations possible at compile time), we use +* Coords which contains three double fields. +*******************************************************************************/ + +/* Assign coordinates. */ +Coords +coords_set(MCNUM x, MCNUM y, MCNUM z) +{ + Coords a; + + a.x = x; + a.y = y; + a.z = z; + return a; +} + +Coords +coords_get(Coords a, MCNUM *x, MCNUM *y, MCNUM *z) +{ + *x = a.x; + *y = a.y; + *z = a.z; + return a; +} + +/* Add two coordinates. */ +Coords +coords_add(Coords a, Coords b) +{ + Coords c; + + c.x = a.x + b.x; + c.y = a.y + b.y; + c.z = a.z + b.z; + return c; +} + +/* Subtract two coordinates. */ +Coords +coords_sub(Coords a, Coords b) +{ + Coords c; + + c.x = a.x - b.x; + c.y = a.y - b.y; + c.z = a.z - b.z; + return c; +} + +/* Negate coordinates. */ +Coords +coords_neg(Coords a) +{ + Coords b; + + b.x = -a.x; + b.y = -a.y; + b.z = -a.z; + return b; +} + +/******************************************************************************* +* The Rotation type implements a rotation transformation of a coordinate +* system in the form of a double[3][3] matrix. +* +* Contrary to the Coords type in coords.c, rotations are passed by +* reference. Functions that yield new rotations do so by writing to an +* explicit result parameter; rotations are not returned from functions. The +* reason for this is that arrays cannot by returned from functions (though +* structures can; thus an alternative would have been to wrap the +* double[3][3] array up in a struct). Such are the ways of C programming. +* +* A rotation represents the tranformation of the coordinates of a vector when +* changing between coordinate systems that are rotated with respect to each +* other. For example, suppose that coordinate system Q is rotated 45 degrees +* around the Z axis with respect to coordinate system P. Let T be the +* rotation transformation representing a 45 degree rotation around Z. Then to +* get the coordinates of a vector r in system Q, apply T to the coordinates +* of r in P. If r=(1,0,0) in P, it will be (sqrt(1/2),-sqrt(1/2),0) in +* Q. Thus we should be careful when interpreting the sign of rotation angles: +* they represent the rotation of the coordinate systems, not of the +* coordinates (which has opposite sign). +*******************************************************************************/ + +/******************************************************************************* +* Get transformation for rotation first phx around x axis, then phy around y, +* then phz around z. +*******************************************************************************/ +void +rot_set_rotation(Rotation t, double phx, double phy, double phz) +{ + double cx = cos(phx); + double sx = sin(phx); + double cy = cos(phy); + double sy = sin(phy); + double cz = cos(phz); + double sz = sin(phz); + + t[0][0] = cy*cz; + t[0][1] = sx*sy*cz + cx*sz; + t[0][2] = sx*sz - cx*sy*cz; + t[1][0] = -cy*sz; + t[1][1] = cx*cz - sx*sy*sz; + t[1][2] = sx*cz + cx*sy*sz; + t[2][0] = sy; + t[2][1] = -sx*cy; + t[2][2] = cx*cy; +} + +/******************************************************************************* +* Matrix multiplication of transformations (this corresponds to combining +* transformations). After rot_mul(T1, T2, T3), doing T3 is equal to doing +* first T2, then T1. +* Note that T3 must not alias (use the same array as) T1 or T2. +*******************************************************************************/ +void +rot_mul(Rotation t1, Rotation t2, Rotation t3) +{ + int i,j; + + for(i = 0; i < 3; i++) + for(j = 0; j < 3; j++) + t3[i][j] = t1[i][0]*t2[0][j] + t1[i][1]*t2[1][j] + t1[i][2]*t2[2][j]; +} + +/******************************************************************************* +* Copy a rotation transformation (needed since arrays cannot be assigned in C). +*******************************************************************************/ +void +rot_copy(Rotation dest, Rotation src) +{ + dest[0][0] = src[0][0]; + dest[0][1] = src[0][1]; + dest[0][2] = src[0][2]; + dest[1][0] = src[1][0]; + dest[1][1] = src[1][1]; + dest[1][2] = src[1][2]; + dest[2][0] = src[2][0]; + dest[2][1] = src[2][1]; + dest[2][2] = src[2][2]; +} + +void +rot_transpose(Rotation src, Rotation dst) +{ + dst[0][0] = src[0][0]; + dst[0][1] = src[1][0]; + dst[0][2] = src[2][0]; + dst[1][0] = src[0][1]; + dst[1][1] = src[1][1]; + dst[1][2] = src[2][1]; + dst[2][0] = src[0][2]; + dst[2][1] = src[1][2]; + dst[2][2] = src[2][2]; +} + +Coords +rot_apply(Rotation t, Coords a) +{ + Coords b; + + b.x = t[0][0]*a.x + t[0][1]*a.y + t[0][2]*a.z; + b.y = t[1][0]*a.x + t[1][1]*a.y + t[1][2]*a.z; + b.z = t[2][0]*a.x + t[2][1]*a.y + t[2][2]*a.z; + return b; +} + +void +mccoordschange(Coords a, Rotation t, double *x, double *y, double *z, + double *vx, double *vy, double *vz, double *time, + double *s1, double *s2) +{ + Coords b, c; + + b.x = *x; + b.y = *y; + b.z = *z; + c = rot_apply(t, b); + b = coords_add(c, a); + *x = b.x; + *y = b.y; + *z = b.z; + + b.x = *vx; + b.y = *vy; + b.z = *vz; + c = rot_apply(t, b); + *vx = c.x; + *vy = c.y; + *vz = c.z; + /* ToDo: What to do about the spin? */ +} + + +void +mccoordschange_polarisation(Rotation t, double *sx, double *sy, double *sz) +{ + Coords b, c; + + b.x = *sx; + b.y = *sy; + b.z = *sz; + c = rot_apply(t, b); + *sx = c.x; + *sy = c.y; + *sz = c.z; +} + +void +mcstore_neutron(MCNUM *s, int index, double x, double y, double z, + double vx, double vy, double vz, double t, + double sx, double sy, double sz, double p) +{ + s[11*index+1] = x ; + s[11*index+2] = y ; + s[11*index+3] = z ; + s[11*index+4] = vx; + s[11*index+5] = vy; + s[11*index+6] = vz; + s[11*index+7] = t ; + s[11*index+8] = sx; + s[11*index+9] = sy; + s[11*index+10] = sz; + s[11*index+0] = p ; +} + +void +mcrestore_neutron(MCNUM *s, int index, double *x, double *y, double *z, + double *vx, double *vy, double *vz, double *t, + double *sx, double *sy, double *sz, double *p) +{ + *x = s[11*index+1] ; + *y = s[11*index+2] ; + *z = s[11*index+3] ; + *vx = s[11*index+4] ; + *vy = s[11*index+5] ; + *vz = s[11*index+6] ; + *t = s[11*index+7] ; + *sx = s[11*index+8] ; + *sy = s[11*index+9] ; + *sz = s[11*index+10] ; + *p = s[11*index+0]; +} + + +double +mcestimate_error(double N, double p1, double p2) +{ + double pmean, n1; + if(N <= 1) + return p1; + pmean = p1 / N; + n1 = N - 1; + /* Note: underflow may cause p2 to become zero; the fabs() below guards + against this. */ + return sqrt((N/n1)*fabs(p2 - pmean*pmean)); +} + +/* parameters handling ====================================================== */ + +/* Instrument input parameter type handling. */ +static int +mcparm_double(char *s, void *vptr) +{ + char *p; + double *v = (double *)vptr; + + if (!s) { *v = 0; return(1); } + *v = strtod(s, &p); + if(*s == '\0' || (p != NULL && *p != '\0') || errno == ERANGE) + return 0; /* Failed */ + else + return 1; /* Success */ +} + + +static char * +mcparminfo_double(char *parmname) +{ + return "double"; +} + + +static void +mcparmerror_double(char *parm, char *val) +{ + fprintf(stderr, "Error: Invalid value '%s' for floating point parameter %s\n", + val, parm); +} + + +static void +mcparmprinter_double(char *f, void *vptr) +{ + double *v = (double *)vptr; + sprintf(f, "%g", *v); +} + + +static int +mcparm_int(char *s, void *vptr) +{ + char *p; + int *v = (int *)vptr; + long x; + + if (!s) { *v = 0; return(1); } + *v = 0; + x = strtol(s, &p, 10); + if(x < INT_MIN || x > INT_MAX) + return 0; /* Under/overflow */ + *v = x; + if(*s == '\0' || (p != NULL && *p != '\0') || errno == ERANGE) + return 0; /* Failed */ + else + return 1; /* Success */ +} + + +static char * +mcparminfo_int(char *parmname) +{ + return "int"; +} + + +static void +mcparmerror_int(char *parm, char *val) +{ + fprintf(stderr, "Error: Invalid value '%s' for integer parameter %s\n", + val, parm); +} + + +static void +mcparmprinter_int(char *f, void *vptr) +{ + int *v = (int *)vptr; + sprintf(f, "%d", *v); +} + + +static int +mcparm_string(char *s, void *vptr) +{ + char **v = (char **)vptr; + if (!s) { *v = NULL; return(1); } + *v = (char *)malloc(strlen(s) + 1); + if(*v == NULL) + { + fprintf(stderr, "Error: Out of memory (mcparm_string).\n"); + exit(1); + } + strcpy(*v, s); + return 1; /* Success */ +} + + +static char * +mcparminfo_string(char *parmname) +{ + return "string"; +} + + +static void +mcparmerror_string(char *parm, char *val) +{ + fprintf(stderr, "Error: Invalid value '%s' for string parameter %s\n", + val, parm); +} + + +static void +mcparmprinter_string(char *f, void *vptr) +{ + char **v = (char **)vptr; + char *p; + + if (!*v) { *f='\0'; return; } + strcpy(f, ""); + for(p = *v; *p != '\0'; p++) + { + switch(*p) + { + case '\n': + strcat(f, "\\n"); + break; + case '\r': + strcat(f, "\\r"); + break; + case '"': + strcat(f, "\\\""); + break; + case '\\': + strcat(f, "\\\\"); + break; + default: + strncat(f, p, 1); + } + } + /* strcat(f, "\""); */ +} + + +static struct + { + int (*getparm)(char *, void *); + char * (*parminfo)(char *); + void (*error)(char *, char *); + void (*printer)(char *, void *); + } mcinputtypes[] = + { + mcparm_double, mcparminfo_double, mcparmerror_double, + mcparmprinter_double, + mcparm_int, mcparminfo_int, mcparmerror_int, + mcparmprinter_int, + mcparm_string, mcparminfo_string, mcparmerror_string, + mcparmprinter_string + }; + +/* init/run/rand handling =================================================== */ + +void +mcreadparams(void) +{ + int i,j,status; + char buf[1024]; + char *p; + int len; + + printf("Instrument parameters for %s (%s)\n", mcinstrument_name, mcinstrument_source); + for(i = 0; mcinputtable[i].name != 0; i++) + { + do + { + if (mcinputtable[i].val && strlen(mcinputtable[i].val)) + printf("Set value of instrument parameter %s (%s) [default='%s']:\n", + mcinputtable[i].name, + (*mcinputtypes[mcinputtable[i].type].parminfo) + (mcinputtable[i].name), mcinputtable[i].val); + else + printf("Set value of instrument parameter %s (%s):\n", + mcinputtable[i].name, + (*mcinputtypes[mcinputtable[i].type].parminfo) + (mcinputtable[i].name)); + + fflush(stdout); + p = fgets(buf, 1024, stdin); + if(p == NULL) + { + fprintf(stderr, "Error: empty input for paramater %s\n", mcinputtable[i].name); + exit(1); + } + len = strlen(buf); + if (!len || (len == 1 && (buf[0] == '\n' || buf[0] == '\r'))) + { + if (mcinputtable[i].val && strlen(mcinputtable[i].val)) { + strncpy(buf, mcinputtable[i].val, 1024); /* use default value */ + len = strlen(buf); + } + } + for(j = 0; j < 2; j++) + { + if(len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r')) + { + len--; + buf[len] = '\0'; + } + } + + status = (*mcinputtypes[mcinputtable[i].type].getparm) + (buf, mcinputtable[i].par); + if(!status) + { + (*mcinputtypes[mcinputtable[i].type].error)(mcinputtable[i].name, buf); + if (!mcinputtable[i].val || strlen(mcinputtable[i].val)) { + fprintf(stderr, " Change %s default value in instrument definition.\n", mcinputtable[i].name); + exit(1); + } + } + } while(!status); + } +} + + + +void +mcsetstate(double x, double y, double z, double vx, double vy, double vz, + double t, double sx, double sy, double sz, double p) +{ + extern double mcnx, mcny, mcnz, mcnvx, mcnvy, mcnvz; + extern double mcnt, mcnsx, mcnsy, mcnsz, mcnp; + + mcnx = x; + mcny = y; + mcnz = z; + mcnvx = vx; + mcnvy = vy; + mcnvz = vz; + mcnt = t; + mcnsx = sx; + mcnsy = sy; + mcnsz = sz; + mcnp = p; +} + +void +mcgenstate(void) +{ + mcsetstate(0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1); +} + +/* McStas random number routine. */ + +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * This is derived from the Berkeley source: + * @(#)random.c 5.5 (Berkeley) 7/6/88 + * It was reworked for the GNU C Library by Roland McGrath. + * Rewritten to use reentrant functions by Ulrich Drepper, 1995. + */ + +/******************************************************************************* +* Modified for McStas from glibc 2.0.7pre1 stdlib/random.c and +* stdlib/random_r.c. +* +* This way random() is more than four times faster compared to calling +* standard glibc random() on ix86 Linux, probably due to multithread support, +* ELF shared library overhead, etc. It also makes McStas generated +* simulations more portable (more likely to behave identically across +* platforms, important for parrallel computations). +*******************************************************************************/ + + +#define TYPE_3 3 +#define BREAK_3 128 +#define DEG_3 31 +#define SEP_3 3 + +static mc_int32_t randtbl[DEG_3 + 1] = + { + TYPE_3, + + -1726662223, 379960547, 1735697613, 1040273694, 1313901226, + 1627687941, -179304937, -2073333483, 1780058412, -1989503057, + -615974602, 344556628, 939512070, -1249116260, 1507946756, + -812545463, 154635395, 1388815473, -1926676823, 525320961, + -1009028674, 968117788, -123449607, 1284210865, 435012392, + -2017506339, -911064859, -370259173, 1132637927, 1398500161, + -205601318, + }; + +static mc_int32_t *fptr = &randtbl[SEP_3 + 1]; +static mc_int32_t *rptr = &randtbl[1]; +static mc_int32_t *state = &randtbl[1]; +#define rand_deg DEG_3 +#define rand_sep SEP_3 +static mc_int32_t *end_ptr = &randtbl[sizeof (randtbl) / sizeof (randtbl[0])]; + +mc_int32_t +mc_random (void) +{ + mc_int32_t result; + + *fptr += *rptr; + /* Chucking least random bit. */ + result = (*fptr >> 1) & 0x7fffffff; + ++fptr; + if (fptr >= end_ptr) + { + fptr = state; + ++rptr; + } + else + { + ++rptr; + if (rptr >= end_ptr) + rptr = state; + } + return result; +} + +void +mc_srandom (unsigned int x) +{ + /* We must make sure the seed is not 0. Take arbitrarily 1 in this case. */ + state[0] = x ? x : 1; + { + long int i; + for (i = 1; i < rand_deg; ++i) + { + /* This does: + state[i] = (16807 * state[i - 1]) % 2147483647; + but avoids overflowing 31 bits. */ + long int hi = state[i - 1] / 127773; + long int lo = state[i - 1] % 127773; + long int test = 16807 * lo - 2836 * hi; + state[i] = test + (test < 0 ? 2147483647 : 0); + } + fptr = &state[rand_sep]; + rptr = &state[0]; + for (i = 0; i < 10 * rand_deg; ++i) + random (); + } +} + +/* "Mersenne Twister", by Makoto Matsumoto and Takuji Nishimura. */ +/* See http://www.math.keio.ac.jp/~matumoto/emt.html for original source. */ + + +/* + A C-program for MT19937, with initialization improved 2002/1/26. + Coded by Takuji Nishimura and Makoto Matsumoto. + + Before using, initialize the state by using mt_srandom(seed) + or init_by_array(init_key, key_length). + + Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Any feedback is very welcome. + http://www.math.keio.ac.jp/matumoto/emt.html + email: matumoto@math.keio.ac.jp +*/ + +#include + +/* Period parameters */ +#define N 624 +#define M 397 +#define MATRIX_A 0x9908b0dfUL /* constant vector a */ +#define UPPER_MASK 0x80000000UL /* most significant w-r bits */ +#define LOWER_MASK 0x7fffffffUL /* least significant r bits */ + +static unsigned long mt[N]; /* the array for the state vector */ +static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */ + +/* initializes mt[N] with a seed */ +void mt_srandom(unsigned long s) +{ + mt[0]= s & 0xffffffffUL; + for (mti=1; mti> 30)) + mti); + /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ + /* In the previous versions, MSBs of the seed affect */ + /* only MSBs of the array mt[]. */ + /* 2002/01/09 modified by Makoto Matsumoto */ + mt[mti] &= 0xffffffffUL; + /* for >32 bit machines */ + } +} + +/* initialize by an array with array-length */ +/* init_key is the array for initializing keys */ +/* key_length is its length */ +void init_by_array(init_key, key_length) +unsigned long init_key[], key_length; +{ + int i, j, k; + mt_srandom(19650218UL); + i=1; j=0; + k = (N>key_length ? N : key_length); + for (; k; k--) { + mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL)) + + init_key[j] + j; /* non linear */ + mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ + i++; j++; + if (i>=N) { mt[0] = mt[N-1]; i=1; } + if (j>=key_length) j=0; + } + for (k=N-1; k; k--) { + mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL)) + - i; /* non linear */ + mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */ + i++; + if (i>=N) { mt[0] = mt[N-1]; i=1; } + } + + mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ +} + +/* generates a random number on [0,0xffffffff]-interval */ +unsigned long mt_random(void) +{ + unsigned long y; + static unsigned long mag01[2]={0x0UL, MATRIX_A}; + /* mag01[x] = x * MATRIX_A for x=0,1 */ + + if (mti >= N) { /* generate N words at one time */ + int kk; + + if (mti == N+1) /* if mt_srandom() has not been called, */ + mt_srandom(5489UL); /* a default initial seed is used */ + + for (kk=0;kk> 1) ^ mag01[y & 0x1UL]; + } + for (;kk> 1) ^ mag01[y & 0x1UL]; + } + y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); + mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; + + mti = 0; + } + + y = mt[mti++]; + + /* Tempering */ + y ^= (y >> 11); + y ^= (y << 7) & 0x9d2c5680UL; + y ^= (y << 15) & 0xefc60000UL; + y ^= (y >> 18); + + return y; +} + +#undef N +#undef M +#undef MATRIX_A +#undef UPPER_MASK +#undef LOWER_MASK + +/* End of "Mersenne Twister". */ + +/* End of McStas random number routine. */ + +double +randnorm(void) +{ + static double v1, v2, s; + static int phase = 0; + double X, u1, u2; + + if(phase == 0) + { + do + { + u1 = rand01(); + u2 = rand01(); + v1 = 2*u1 - 1; + v2 = 2*u2 - 1; + s = v1*v1 + v2*v2; + } while(s >= 1 || s == 0); + + X = v1*sqrt(-2*log(s)/s); + } + else + { + X = v2*sqrt(-2*log(s)/s); + } + + phase = 1 - phase; + return X; +} + +/* intersect handling ======================================================= */ + +/* Compute normal vector to (x,y,z). */ +void normal_vec(double *nx, double *ny, double *nz, + double x, double y, double z) +{ + double ax = fabs(x); + double ay = fabs(y); + double az = fabs(z); + double l; + if(x == 0 && y == 0 && z == 0) + { + *nx = 0; + *ny = 0; + *nz = 0; + return; + } + if(ax < ay) + { + if(ax < az) + { /* Use X axis */ + l = sqrt(z*z + y*y); + *nx = 0; + *ny = z/l; + *nz = -y/l; + return; + } + } + else + { + if(ay < az) + { /* Use Y axis */ + l = sqrt(z*z + x*x); + *nx = z/l; + *ny = 0; + *nz = -x/l; + return; + } + } + /* Use Z axis */ + l = sqrt(y*y + x*x); + *nx = y/l; + *ny = -x/l; + *nz = 0; +} + +/* If intersection with box dt_in and dt_out is returned */ +/* This function written by Stine Nyborg, 1999. */ +int box_intersect(double *dt_in, double *dt_out, + double x, double y, double z, + double vx, double vy, double vz, + double dx, double dy, double dz) +{ + double x_in, y_in, z_in, tt, t[6], a, b; + int i, count, s; + + /* Calculate intersection time for each of the six box surface planes + * If the box surface plane is not hit, the result is zero.*/ + + if(vx != 0) + { + tt = -(dx/2 + x)/vx; + y_in = y + tt*vy; + z_in = z + tt*vz; + if( y_in > -dy/2 && y_in < dy/2 && z_in > -dz/2 && z_in < dz/2) + t[0] = tt; + else + t[0] = 0; + + tt = (dx/2 - x)/vx; + y_in = y + tt*vy; + z_in = z + tt*vz; + if( y_in > -dy/2 && y_in < dy/2 && z_in > -dz/2 && z_in < dz/2) + t[1] = tt; + else + t[1] = 0; + } + else + t[0] = t[1] = 0; + + if(vy != 0) + { + tt = -(dy/2 + y)/vy; + x_in = x + tt*vx; + z_in = z + tt*vz; + if( x_in > -dx/2 && x_in < dx/2 && z_in > -dz/2 && z_in < dz/2) + t[2] = tt; + else + t[2] = 0; + + tt = (dy/2 - y)/vy; + x_in = x + tt*vx; + z_in = z + tt*vz; + if( x_in > -dx/2 && x_in < dx/2 && z_in > -dz/2 && z_in < dz/2) + t[3] = tt; + else + t[3] = 0; + } + else + t[2] = t[3] = 0; + + if(vz != 0) + { + tt = -(dz/2 + z)/vz; + x_in = x + tt*vx; + y_in = y + tt*vy; + if( x_in > -dx/2 && x_in < dx/2 && y_in > -dy/2 && y_in < dy/2) + t[4] = tt; + else + t[4] = 0; + + tt = (dz/2 - z)/vz; + x_in = x + tt*vx; + y_in = y + tt*vy; + if( x_in > -dx/2 && x_in < dx/2 && y_in > -dy/2 && y_in < dy/2) + t[5] = tt; + else + t[5] = 0; + } + else + t[4] = t[5] = 0; + + /* The intersection is evaluated and *dt_in and *dt_out are assigned */ + + a = b = s = 0; + count = 0; + + for( i = 0; i < 6; i = i + 1 ) + if( t[i] == 0 ) + s = s+1; + else if( count == 0 ) + { + a = t[i]; + count = 1; + } + else + { + b = t[i]; + count = 2; + } + + if ( a == 0 && b == 0 ) + return 0; + else if( a < b ) + { + *dt_in = a; + *dt_out = b; + return 1; + } + else + { + *dt_in = b; + *dt_out = a; + return 1; + } + +} + +/* Written by: EM,NB,ABA 4.2.98 */ +int +cylinder_intersect(double *t0, double *t1, double x, double y, double z, + double vx, double vy, double vz, double r, double h) +{ + double D, t_in, t_out, y_in, y_out; + int ret=1; + + D = (2*vx*x + 2*vz*z)*(2*vx*x + 2*vz*z) + - 4*(vx*vx + vz*vz)*(x*x + z*z - r*r); + + if (D>=0) + { + t_in = (-(2*vz*z + 2*vx*x) - sqrt(D))/(2*(vz*vz + vx*vx)); + t_out = (-(2*vz*z + 2*vx*x) + sqrt(D))/(2*(vz*vz + vx*vx)); + y_in = vy*t_in + y; + y_out =vy*t_out + y; + + if ( (y_in > h/2 && y_out > h/2) || (y_in < -h/2 && y_out < -h/2) ) + return 0; + else + { + if (y_in > h/2) + { t_in = ((h/2)-y)/vy; ret += 2; } + else if (y_in < -h/2) + { t_in = ((-h/2)-y)/vy; ret += 4; } + if (y_out > h/2) + { t_out = ((h/2)-y)/vy; ret += 8; } + else if (y_out < -h/2) + { t_out = ((-h/2)-y)/vy; ret += 16; } + } + *t0 = t_in; + *t1 = t_out; + return ret; + } + else + { + *t0 = *t1 = 0; + return 0; + } +} + + +/* Calculate intersection between line and sphere. */ +int +sphere_intersect(double *t0, double *t1, double x, double y, double z, + double vx, double vy, double vz, double r) +{ + double A, B, C, D, v; + + v = sqrt(vx*vx + vy*vy + vz*vz); + A = v*v; + B = 2*(x*vx + y*vy + z*vz); + C = x*x + y*y + z*z - r*r; + D = B*B - 4*A*C; + if(D < 0) + return 0; + D = sqrt(D); + *t0 = (-B - D) / (2*A); + *t1 = (-B + D) / (2*A); + return 1; +} + + +/* ADD: E. Farhi, Aug 6th, 2001 plane_intersect_Gfast + * intersection of a plane and a trajectory with gravitation + * this function calculates the intersection between a neutron trajectory + * and a plane with acceleration gx,gy,gz. The neutron starts at point x,y,z + * with velocity vx, vy, vz. The plane has a normal vector nx,ny,nz and + * contains the point wx,wy,wz + * The function returns 0 if no intersection occured after the neutron started + * and non 0 if there is an intersection. Then *Idt is the time until + * the neutron hits the roof. + * Let n=(nx,ny,nz) be the normal plane vector (one of the six sides) + * Let W=(wx,wy,wz) be Any point on this plane (for instance at z=0) + * The problem consists in solving the 2nd order equation: + * 1/2.n.g.t^2 + n.v.t + n.(r-W) = 0 (1) + * Without acceleration, t=-n.(r-W)/n.v + */ + +int plane_intersect_Gfast(double *Idt, + double A, double B, double C) +{ + /* plane_intersect_Gfast(&dt, A, B, C) + * A = 0.5 n.g; B = n.v; C = n.(r-W); + * no acceleration when A=0 + */ + int ret=0; + double dt0; + + *Idt = 0; + + if (B) dt0 = -C/B; + if (fabs(A) < 1E-10) /* this plane is parallel to the acceleration */ + { + if (B) + { *Idt = dt0; ret=3; } + /* else the speed is parallel to the plane, no intersection */ + } + else + { + double D, sD, dt1, dt2; + D = B*B - 4*A*C; + if (D >= 0) /* Delta > 0: neutron trajectory hits the mirror */ + { + sD = sqrt(D); + dt1 = (-B + sD)/2/A; + dt2 = (-B - sD)/2/A; + if (B) + { + if (fabs(dt0-dt1) < fabs(dt0-dt2)) ret=1; else ret=2; + } + else + { + if (dt1 <= dt2) ret=1; else ret=2; + } + if (ret==1) *Idt = dt1; + else if (ret==2) *Idt = dt2; + } /* else Delta <0: no intersection */ + } + return(ret); +} + + +/* Choose random direction towards target at (x,y,z) with given radius. */ +/* If radius is zero, choose random direction in full 4PI, no target. */ +void +randvec_target_circle(double *xo, double *yo, double *zo, double *solid_angle, + double xi, double yi, double zi, double radius) +{ + double l2, phi, theta, nx, ny, nz, xt, yt, zt, xu, yu, zu; + + if(radius == 0.0) + { + /* No target, choose uniformly a direction in full 4PI solid angle. */ + theta = acos (1 - rand0max(2)); + phi = rand0max(2 * PI); + if(solid_angle) + *solid_angle = 4*PI; + nx = 1; + ny = 0; + nz = 0; + yi = sqrt(xi*xi+yi*yi+zi*zi); + zi = 0; + xi = 0; + } + else + { + double costheta0; + l2 = xi*xi + yi*yi + zi*zi; /* sqr Distance to target. */ + costheta0 = sqrt(l2/(radius*radius+l2)); + if (radius < 0) costheta0 *= -1; + if(solid_angle) + { + /* Compute solid angle of target as seen from origin. */ + *solid_angle = 2*PI*(1 - costheta0); + } + + /* Now choose point uniformly on sphere surface within angle theta0 */ + theta = acos (1 - rand0max(1 - costheta0)); /* radius on circle */ + phi = rand0max(2 * PI); /* rotation on circle at given radius */ + /* Now, to obtain the desired vector rotate (xi,yi,zi) angle theta around a + perpendicular axis u=i x n and then angle phi around i. */ + if(xi == 0 && zi == 0) + { + nx = 1; + ny = 0; + nz = 0; + } + else + { + nx = -zi; + nz = xi; + ny = 0; + } + } + + /* [xyz]u = [xyz]i x n[xyz] (usually vertical) */ + vec_prod(xu, yu, zu, xi, yi, zi, nx, ny, nz); + /* [xyz]t = [xyz]i rotated theta around [xyz]u */ + rotate (xt, yt, zt, xi, yi, zi, theta, xu, yu, zu); + /* [xyz]o = [xyz]t rotated phi around n[xyz] */ + rotate (*xo, *yo, *zo, xt, yt, zt, phi, xi, yi, zi); +} + + +/* Choose random direction towards target at (xi,yi,zi) with given */ +/* ANGULAR dimension height x width. height=phi_x, width=phi_y (radians)*/ +/* If height or width is zero, choose random direction in full 4PI, no target. */ +void +randvec_target_rect_angular(double *xo, double *yo, double *zo, double *solid_angle, + double xi, double yi, double zi, double width, double height, Rotation A) +{ + double theta, phi, nx, ny, nz, xt, yt, zt, xu, yu, zu; + Coords tmp; + Rotation Ainverse; + + rot_transpose(A, Ainverse); + + if(height == 0.0 || width == 0.0) + { + randvec_target_circle(xo, yo, zo, solid_angle, + xi, yi, zi, 0); + } + else + { + if(solid_angle) + { + /* Compute solid angle of target as seen from origin. */ + *solid_angle = 2*fabs(width*sin(height/2)); + } + + /* Go to global coordinate system */ + + tmp = coords_set(xi, yi, zi); + tmp = rot_apply(Ainverse, tmp); + coords_get(tmp, &xi, &yi, &zi); + + /* Now choose point uniformly on quadrant within angle theta0/phi0 */ + theta = width*randpm1()/2.0; + phi = height*randpm1()/2.0; + /* Now, to obtain the desired vector rotate (xi,yi,zi) angle phi around + n, and then theta around u. */ + if(xi == 0 && zi == 0) + { + nx = 1; + ny = 0; + nz = 0; + } + else + { + nx = -zi; + nz = xi; + ny = 0; + } + } + + /* [xyz]u = [xyz]i x n[xyz] (usually vertical) */ + vec_prod(xu, yu, zu, xi, yi, zi, nx, ny, nz); + /* [xyz]t = [xyz]i rotated theta around [xyz]u */ + rotate (xt, yt, zt, xi, yi, zi, phi, nx, ny, nz); + /* [xyz]o = [xyz]t rotated phi around n[xyz] */ + rotate (*xo, *yo, *zo, xt, yt, zt, theta, xu, yu, zu); + + /* Go back to local coordinate system */ + tmp = coords_set(*xo, *yo, *zo); + tmp = rot_apply(A, tmp); + coords_get(tmp, &*xo, &*yo, &*zo); + +} + +/* Choose random direction towards target at (xi,yi,zi) with given */ +/* dimension height x width (in meters!). */ +/* If height or width is zero, choose random direction in full 4PI, no target. */ +void +randvec_target_rect(double *xo, double *yo, double *zo, double *solid_angle, + double xi, double yi, double zi, double width, double height, Rotation A) +{ + double dx, dy, dist, dist_p, nx, ny, nz, mx, my, mz, xt, yt, zt, xu, yu, zu, theta, phi, n_norm, m_norm; + Coords tmp; + Rotation Ainverse; + + rot_transpose(A, Ainverse); + + if(height == 0.0 || width == 0.0) + { + randvec_target_circle(xo, yo, zo, solid_angle, + xi, yi, zi, 0); + } + else + { + + /* Now choose point uniformly on quadrant within width x height */ + dx = width*randpm1()/2.0; + dy = height*randpm1()/2.0; + + /* Determine distance to target */ + dist = sqrt(xi*xi + yi*yi + zi*zi); + /* Go to global coordinate system */ + + tmp = coords_set(xi, yi, zi); + tmp = rot_apply(Ainverse, tmp); + coords_get(tmp, &xi, &yi, &zi); + + /* Determine vector normal to neutron axis (z) and gravity [0 1 0] */ + vec_prod(nx, ny, nz, xi, yi, zi, 0, 1, 0); + + /* This now defines the x-axis, normalize: */ + n_norm=sqrt(nx*nx + ny*ny + nz*nz); + nx = nx/n_norm; + ny = ny/n_norm; + nz = nz/n_norm; + + /* Now, determine our y-axis (vertical in many cases...) */ + vec_prod(mx, my, mz, xi, yi, zi, nx, ny, nz); + m_norm=sqrt(mx*mx + my*my + mz*mz); + mx = mx/m_norm; + my = my/m_norm; + mz = mz/m_norm; + + /* Our output, random vector can now be defined by linear combination: */ + + *xo = xi + dx * nx + dy * mx; + *yo = yi + dx * ny + dy * my; + *zo = zi + dx * nz + dy * mz; + + /* Go back to local coordinate system */ + tmp = coords_set(*xo, *yo, *zo); + tmp = rot_apply(A, tmp); + coords_get(tmp, &*xo, &*yo, &*zo); + + /* Determine distance to random point */ + dist_p = sqrt(dx*dx + dy*dy + dist*dist); + + /* Adjust the 'solid angle' (here more thought of as a normalization constant) */ + /* Works since we are in the relative coordinate system, origin is where we are at */ + *solid_angle = (width*height*dist)/(dist_p*dist_p*dist_p); + + } +} + + +/* Make sure a list is big enough to hold element COUNT. +* +* The list is an array, and the argument 'list' is a pointer to a pointer to +* the array start. The argument 'size' is a pointer to the number of elements +* in the array. The argument 'elemsize' is the sizeof() an element. The +* argument 'count' is the minimum number of elements needed in the list. +* +* If the old array is to small (or if *list is NULL or *size is 0), a +* sufficuently big new array is allocated, and *list and *size are updated. +*/ +void extend_list(int count, void **list, int *size, size_t elemsize) +{ + if(count >= *size) + { + void *oldlist = *list; + if(*size > 0) + *size *= 2; + else + *size = 32; + *list = malloc(*size*elemsize); + if(!*list) + { + fprintf(stderr, "\nError: Out of memory (extend_list).\n"); + exit(1); + } + if(oldlist) + { + memcpy(*list, oldlist, count*elemsize); + free(oldlist); + } + } +} + +/* Number of neutron histories to simulate. */ +static double mcncount = 1e6; +double mcrun_num = 0; + +void +mcset_ncount(double count) +{ + mcncount = count; +} + +double +mcget_ncount(void) +{ + return mcncount; +} + +double +mcget_run_num(void) +{ + return mcrun_num; +} + +static void +mcsetn_arg(char *arg) +{ + mcset_ncount(strtod(arg, NULL)); +} + +static void +mcsetseed(char *arg) +{ + mcseed = atol(arg); + if(mcseed) + srandom(mcseed); + else + { + fprintf(stderr, "Error: seed most not be zero.\n"); + exit(1); + } +} + +static void +mchelp(char *pgmname) +{ + int i; + + fprintf(stderr, "Usage: %s [options] [parm=value ...]\n", pgmname); + fprintf(stderr, +"Options are:\n" +" -s SEED --seed=SEED Set random seed (must be != 0)\n" +" -n COUNT --ncount=COUNT Set number of neutrons to simulate.\n" +" -d DIR --dir=DIR Put all data files in directory DIR.\n" +" -f FILE --file=FILE Put all data in a single file.\n" +" -t --trace Enable trace of neutron through instrument.\n" +" -g --gravitation Enable gravitation for all trajectories.\n" +" -a --data-only Do not put any headers in the data files.\n" +" --no-output-files Do not write any data files.\n" +" -h --help Show this help message.\n" +" -i --info Detailed instrument information.\n" +" --format=FORMAT Output data files using format FORMAT\n" +" (use option +a to include text header in files\n" +); + if(mcnumipar > 0) + { + fprintf(stderr, "Instrument parameters are:\n"); + for(i = 0; i < mcnumipar; i++) + if (mcinputtable[i].val && strlen(mcinputtable[i].val)) + fprintf(stderr, " %-16s(%s) [default='%s']\n", mcinputtable[i].name, + (*mcinputtypes[mcinputtable[i].type].parminfo)(mcinputtable[i].name), + mcinputtable[i].val); + else + fprintf(stderr, " %-16s(%s)\n", mcinputtable[i].name, + (*mcinputtypes[mcinputtable[i].type].parminfo)(mcinputtable[i].name)); + } + fprintf(stderr, "Available output formats are (default is %s):\n ", mcformat.Name); + for (i=0; i < mcNUMFORMATS; fprintf(stderr,"\"%s\" " , mcformats[i++].Name) ); + fprintf(stderr, "\n Format modifiers: FORMAT may be followed by 'binary float' or \n"); + fprintf(stderr, " 'binary double' to save data blocks as binary. This removes text headers.\n"); + fprintf(stderr, " The MCSTAS_FORMAT environment variable may set the default FORMAT to use.\n"); +#ifndef MC_PORTABLE +#ifndef MAC +#ifndef WIN32 + fprintf(stderr, "Known signals are: USR1 (status) USR2(save) TERM (save and exit)\n"); +#endif /* !MAC */ +#endif /* !WIN32 */ +#endif /* !MC_PORTABLE */ +} + +static void +mcshowhelp(char *pgmname) +{ + mchelp(pgmname); + exit(0); +} + +static void +mcusage(char *pgmname) +{ + fprintf(stderr, "Error: incorrect command line arguments\n"); + mchelp(pgmname); + exit(1); +} + +static void +mcenabletrace(void) +{ + if(mctraceenabled) + mcdotrace = 1; + else + { + fprintf(stderr, + "Error: trace not enabled.\n" + "Please re-run the McStas compiler " + "with the --trace option, or rerun the\n" + "C compiler with the MC_TRACE_ENABLED macro defined.\n"); + exit(1); + } +} + +/* file i/o handling ======================================================== */ +/* opens a new file within mcdirname if non NULL */ +/* if mode is non 0, then mode is used, else mode is 'w' */ + +FILE * +mcnew_file(char *name, char *mode) +{ + int dirlen; + char *mem; + FILE *file; + + if (!name || strlen(name) == 0) return(NULL); + + dirlen = mcdirname ? strlen(mcdirname) : 0; + mem = malloc(dirlen + 1 + strlen(name) + 1); + if(!mem) + { + fprintf(stderr, "Error: Out of memory (mcnew_file)\n"); + exit(1); + } + strcpy(mem, ""); + if(dirlen) + { + strcat(mem, mcdirname); + if(mcdirname[dirlen - 1] != MC_PATHSEP_C && + name[0] != MC_PATHSEP_C) + strcat(mem, MC_PATHSEP_S); + } + strcat(mem, name); + file = fopen(mem, (mode ? mode : "w")); + if(!file) + fprintf(stderr, "Warning: could not open output file '%s'\n", mem); + free(mem); + return file; +} /* mcnew_file */ + +/* mcvalid_name: makes a valid string for variable names. + * copy 'original' into 'valid', replacing invalid characters by '_' + * char arrays must be pre-allocated. n can be 0, or the maximum number of + * chars to be copied/checked + */ +static char *mcvalid_name(char *valid, char *original, int n) +{ + long i; + + + if (original == NULL || strlen(original) == 0) + { strcpy(valid, "noname"); return(valid); } + if (n <= 0) n = strlen(valid); + + if (n > strlen(original)) n = strlen(original); + strncpy(valid, original, n); + + for (i=0; i < n; i++) + { + if ( (valid[i] > 122) + || (valid[i] < 32) + || (strchr("!\"#$%&'()*+,-.:;<=>?@[\\]^`/ ", valid[i]) != NULL) ) + { + if (i) valid[i] = '_'; else valid[i] = 'm'; + } + } + valid[i] = '\0'; + + return(valid); +} /* mcvalid_name */ + +#if defined(NL_ARGMAX) || defined(WIN32) +static int pfprintf(FILE *f, char *fmt, char *fmt_args, ...) +{ +/* this function +1- look for the maximum %d$ field in fmt +2- looks for all %d$ fields up to max in fmt and set their type (next alpha) +3- retrieve va_arg up to max, and save pointer to arg in local arg array +4- use strchr to split around '%' chars, until all pieces are written + +usage: just as fprintf, but with (char *)fmt_args being the list of arg type + */ + + #define MyNL_ARGMAX 50 + char *fmt_pos; + + char *arg_char[MyNL_ARGMAX]; + int arg_int[MyNL_ARGMAX]; + long arg_long[MyNL_ARGMAX]; + double arg_double[MyNL_ARGMAX]; + + char *arg_posB[MyNL_ARGMAX]; /* position of '%' */ + char *arg_posE[MyNL_ARGMAX]; /* position of '$' */ + char *arg_posT[MyNL_ARGMAX]; /* position of type */ + + int arg_num[MyNL_ARGMAX]; /* number of argument (between % and $) */ + int this_arg=0; + int arg_max=0; + va_list ap; + + if (!f || !fmt_args || !fmt) return(-1); + for (this_arg=0; this_arg= MyNL_ARGMAX) + return(-fprintf(stderr,"pfprintf: invalid positional argument number (<=0 or >=%i) %s.\n", MyNL_ARGMAX, arg_posB[this_arg])); + /* get type of positional argument: follows '%' -> arg_posE[this_arg]+1 */ + fmt_pos = arg_posE[this_arg]+1; + if (!strchr(printf_formats, fmt_pos[0])) + return(-fprintf(stderr,"pfprintf: invalid positional argument type (%c != expected %c).\n", fmt_pos[0], fmt_args[arg_num[this_arg]-1])); + if (fmt_pos[0] == 'l' && fmt_pos[1] == 'i') fmt_pos++; + arg_posT[this_arg] = fmt_pos; + /* get next argument... */ + this_arg++; + } + else + { + if (tmp[1] != '%') + return(-fprintf(stderr,"pfprintf: must use only positional arguments (%s).\n", arg_posB[this_arg])); + else fmt_pos = arg_posB[this_arg]+2; /* found %% */ + } + } else + break; /* no more % argument */ + } + arg_max = this_arg; + /* get arguments from va_arg list, according to their type */ + va_start(ap, fmt_args); + for (this_arg=0; this_arg0) + { + fmt_bit = (char*)malloc(arg_posB[this_arg]-fmt_pos+10); + if (!fmt_bit) return(-fprintf(stderr,"pfprintf: not enough memory.\n")); + strncpy(fmt_bit, fmt_pos, arg_posB[this_arg]-fmt_pos); + fmt_bit[arg_posB[this_arg]-fmt_pos] = '\0'; + fprintf(f, fmt_bit); /* fmt part without argument */ + } else + { + fmt_bit = (char*)malloc(10); + if (!fmt_bit) return(-fprintf(stderr,"pfprintf: not enough memory.\n")); + } + arg_n = arg_num[this_arg]-1; /* must be >= 0 */ + strcpy(fmt_bit, "%"); + strncat(fmt_bit, arg_posE[this_arg]+1, arg_posT[this_arg]-arg_posE[this_arg]); + fmt_bit[arg_posT[this_arg]-arg_posE[this_arg]+1] = '\0'; + + switch(fmt_args[arg_n]) + { + case 's': fprintf(f, fmt_bit, arg_char[arg_n]); + break; + case 'd': + case 'i': + case 'c': /* int */ + fprintf(f, fmt_bit, arg_int[arg_n]); + break; + case 'l': /* long */ + fprintf(f, fmt_bit, arg_long[arg_n]); + break; + case 'f': + case 'g': + case 'G': /* double */ + fprintf(f, fmt_bit, arg_double[arg_n]); + break; + } + fmt_pos = arg_posT[this_arg]+1; + if (this_arg == arg_max-1) + { /* add eventual leading characters for last parameter */ + if (fmt_pos < fmt+strlen(fmt)) + fprintf(f, "%s", fmt_pos); + } + if (fmt_bit) free(fmt_bit); + + } + return(this_arg); +} +#else +static int pfprintf(FILE *f, char *fmt, char *fmt_args, ...) +{ /* wrapper to standard fprintf */ + va_list ap; + int tmp; + + va_start(ap, fmt_args); + tmp=vfprintf(f, fmt, ap); + va_end(ap); + return(tmp); +} +#endif + +/* mcfile_header: output header/footer using specific file format. + * outputs, in file 'name' having preallocated 'f' handle, the format Header + * 'part' may be 'header' or 'footer' depending on part to write + * if name == NULL, ignore function (no header/footer output) + */ +static int mcfile_header(FILE *f, struct mcformats_struct format, char *part, char *pre, char *name, char *parent) +{ + char user[64]; + char date[64]; + char *HeadFoot; + long date_l; /* date as a long number */ + time_t t; + char valid_parent[256]; + char instrname[256]; + char file[256]; + + if(!f) + return (-1); + + time(&t); + + if (part && !strcmp(part,"footer")) + { + HeadFoot = format.Footer; + date_l = (long)t;; + } + else + { + HeadFoot = format.Header; + date_l = mcstartdate; + } + t = (time_t)date_l; + + if (!strlen(HeadFoot) || (!name)) return (-1); + + sprintf(file,"%s",name); + sprintf(user,"%s on %s", getenv("USER"), getenv("HOST")); + sprintf(instrname,"%s (%s)", mcinstrument_name, mcinstrument_source); + strncpy(date, ctime(&t), 64); + if (strlen(date)) date[strlen(date)-1] = '\0'; + + if (parent && strlen(parent)) mcvalid_name(valid_parent, parent, 256); + else strcpy(valid_parent, "root"); + + return(pfprintf(f, HeadFoot, "sssssssl", + pre, /* %1$s */ + instrname, /* %2$s */ + file, /* %3$s */ + format.Name, /* %4$s */ + date, /* %5$s */ + user, /* %6$s */ + valid_parent, /* %7$s*/ + date_l)); /* %8$li */ +} /* mcfile_header */ + +/* mcfile_tag: output tag/value using specific file format. + * outputs, in file with 'f' handle, a tag/value pair. + * if name == NULL, ignore function (no section definition) + */ +static int mcfile_tag(FILE *f, struct mcformats_struct format, char *pre, char *section, char *name, char *value) +{ + char valid_section[256]; + int i; + + if (!strlen(format.AssignTag) || (!name) || (!f)) return(-1); + + mcvalid_name(valid_section, section, 256); + + /* remove quote chars in values */ + if (strstr(format.Name, "Scilab") || strstr(format.Name, "Matlab") || strstr(format.Name, "IDL")) + for(i = 0; i < strlen(value); i++) + if (value[i] == '"' || value[i] == '\'') value[i] = ' '; + + return(pfprintf(f, format.AssignTag, "ssss", + pre, /* %1$s */ + valid_section,/* %2$s */ + name, /* %3$s */ + value)); /* %4$s */ +} /* mcfile_tag */ + +/* mcfile_section: output section start/end using specific file format. + * outputs, in file 'name' having preallocated 'f' handle, the format Section. + * 'part' may be 'begin' or 'end' depending on section part to write + * 'type' may be e.g. 'instrument','simulation','component','data' + * if name == NULL, ignore function (no section definition) + * the prefix 'pre' is automatically idented/un-indented (pre-allocated !) + */ + +static int mcfile_section(FILE *f, struct mcformats_struct format, char *part, char *pre, char *name, char *type, char *parent, int level) +{ + char *Section; + char valid_name[256]; + char valid_parent[256]; + int ret; + + if(!f) + return (-1); + + if (part && !strcmp(part,"end")) Section = format.EndSection; + else Section = format.BeginSection; + + if (!strlen(Section) || (!name)) return (-1); + + mcvalid_name(valid_name, name, 256); + if (parent && strlen(parent)) mcvalid_name(valid_parent, parent, 256); + else strcpy(valid_parent, "root"); + + if (!strcmp(part,"end") && pre) + { + if (strlen(pre) <= 2) strcpy(pre,""); + else pre[strlen(pre)-2]='\0'; + } + + ret = pfprintf(f, Section, "ssssssl", + pre, /* %1$s */ + type, /* %2$s */ + name, /* %3$s */ + valid_name, /* %4$s */ + parent, /* %5$s */ + valid_parent, /* %6$s */ + level); /* %7$li */ + + if (!strcmp(part,"begin")) + { + strcat(pre," "); + if (name && strlen(name)) + mcfile_tag(f, format, pre, name, "name", name); + if (parent && strlen(parent)) + mcfile_tag(f, format, pre, name, "parent", parent); + } + + + return(ret); +} /* mcfile_section */ + +static void mcinfo_instrument(FILE *f, struct mcformats_struct format, + char *pre, char *name) +{ + char Value[1300] = ""; + int i; + + if (!f) return; + + for(i = 0; i < mcnumipar; i++) + { + char ThisParam[256]; + if (strlen(mcinputtable[i].name) > 200) break; + sprintf(ThisParam, " %s(%s)", mcinputtable[i].name, + (*mcinputtypes[mcinputtable[i].type].parminfo) + (mcinputtable[i].name)); + strcat(Value, ThisParam); + if (strlen(Value) > 1024) break; + } + mcfile_tag(f, format, pre, name, "Parameters", Value); + mcfile_tag(f, format, pre, name, "Source", mcinstrument_source); + mcfile_tag(f, format, pre, name, "Trace_enabled", mctraceenabled ? "yes" : "no"); + mcfile_tag(f, format, pre, name, "Default_main", mcdefaultmain ? "yes" : "no"); + mcfile_tag(f, format, pre, name, "Embedded_runtime", +#ifdef MC_EMBEDDED_RUNTIME + "yes" +#else + "no" +#endif + ); +} /* mcinfo_instrument */ + +void mcinfo_simulation(FILE *f, struct mcformats_struct format, + char *pre, char *name) +{ + int i; + double run_num, ncount; + time_t t; + char Value[256]; + + if (!f) return; + + run_num = mcget_run_num(); + ncount = mcget_ncount(); + time(&t); + strncpy(Value, ctime(&t), 256); if (strlen(Value)) Value[strlen(Value)-1] = '\0'; + mcfile_tag(f, format, pre, name, "Date", Value); + if (run_num == 0 || run_num == ncount) sprintf(Value, "%g", ncount); + else sprintf(Value, "%g/%g", run_num, ncount); + mcfile_tag(f, format, pre, name, "Ncount", Value); + mcfile_tag(f, format, pre, name, "Trace", mcdotrace ? "yes" : "no"); + mcfile_tag(f, format, pre, name, "Gravitation", mcgravitation ? "yes" : "no"); + if(mcseed) + { + sprintf(Value, "%ld", mcseed); + mcfile_tag(f, format, pre, name, "Seed", Value); + } + if (strstr(format.Name, "McStas")) + { + for(i = 0; i < mcnumipar; i++) + { + if (mcrun_num || (mcinputtable[i].val && strlen(mcinputtable[i].val))) { + (*mcinputtypes[mcinputtable[i].type].printer)(Value, mcinputtable[i].par); + fprintf(f, "%sParam: %s=%s", pre, mcinputtable[i].name, Value); + fprintf(f, "\n"); + } + } + } + else + { + mcfile_section(f, format, "begin", pre, "parameters", "parameters", name, 3); + for(i = 0; i < mcnumipar; i++) + { + (*mcinputtypes[mcinputtable[i].type].printer)(Value, mcinputtable[i].par); + mcfile_tag(f, format, pre, "parameters", mcinputtable[i].name, Value); + } + mcfile_section(f, format, "end", pre, "parameters", "parameters", name, 3); + } +} /* mcinfo_simulation */ + +static void mcinfo_data(FILE *f, struct mcformats_struct format, + char *pre, char *parent, char *title, + int m, int n, int p, + char *xlabel, char *ylabel, char *zlabel, + char *xvar, char *yvar, char *zvar, + double x1, double x2, double y1, double y2, double z1, double z2, + char *filename, + double *p0, double *p1, double *p2, char istransposed) +{ + char type[256]; + char stats[256]; + char vars[256]; + char signal[256]; + char values[256]; + char limits[256]; + char lim_field[10]; + char c[32]; + double run_num, ncount; + char ratio[256]; + + double sum_xz = 0; + double sum_yz = 0; + double sum_z = 0; + double sum_y = 0; + double sum_x = 0; + double sum_x2z = 0; + double sum_y2z = 0; + double min_z = 0; + double max_z = 0; + double fmon_x=0, smon_x=0, fmon_y=0, smon_y=0, mean_z=0; + double Nsum=0; + double P2sum=0; + + int i,j; + + if (!f || m*n*p == 0) return; + + if (p1) + { + min_z = p1[0]; + max_z = min_z; + for(j = 0; j < n*p; j++) + { + for(i = 0; i < m; i++) + { + double x,y,z; + double N, E; + long index; + + if (!istransposed) index = i*n*p + j; + else index = i+j*m; + if (p0) N = p0[index]; + if (p2) E = p2[index]; + + if (m) x = x1 + (i + 0.5)/m*(x2 - x1); else x = 0; + if (n) y = y1 + (j + 0.5)/n/p*(y2 - y1); else y = 0; + z = p1[index]; + sum_xz += x*z; + sum_yz += y*z; + sum_x += x; + sum_y += y; + sum_z += z; + sum_x2z += x*x*z; + sum_y2z += y*y*z; + if (z > max_z) max_z = z; + if (z < min_z) min_z = z; + + Nsum += p0 ? N : 1; + P2sum += p2 ? E : z*z; + } + } + if (sum_z && n*m*p) + { + fmon_x = sum_xz/sum_z; + fmon_y = sum_yz/sum_z; + smon_x = sqrt(sum_x2z/sum_z-fmon_x*fmon_x); + smon_y = sqrt(sum_y2z/sum_z-fmon_y*fmon_y); + mean_z = sum_z/n/m/p; + } + } + + if (m*n*p == 1) + { strcpy(type, "array_0d"); strcpy(stats, ""); } + else if (n == 1 || m == 1) + { if (m == 1) {m = n; n = 1; } + sprintf(type, "array_1d(%d)", m); + sprintf(stats, "X0=%g; dX=%g;", fmon_x, smon_x); } + else + { if (p == 1) sprintf(type, "array_2d(%d, %d)", m, n); + else sprintf(type, "array_3d(%d, %d, %d)", m, n, p); + sprintf(stats, "X0=%g; dX=%g; Y0=%g; dY=%g;", fmon_x, smon_x, fmon_y, smon_y); } + strcpy(c, "I "); + if (zvar && strlen(zvar)) strncpy(c, zvar,32); + else if (yvar && strlen(yvar)) strncpy(c, yvar,32); + else if (xvar && strlen(xvar)) strncpy(c, xvar,32); + else strncpy(c, xvar,32); + if (m == 1 || n == 1) sprintf(vars, "%s %s %s_err N", xvar, c, c); + else sprintf(vars, "%s %s_err N", c, c); + + run_num = mcget_run_num(); + ncount = mcget_ncount(); + sprintf(ratio, "%g/%g", run_num, ncount); + + mcfile_tag(f, format, pre, parent, "type", type); + mcfile_tag(f, format, pre, parent, "Source", mcinstrument_source); + if (parent) mcfile_tag(f, format, pre, parent, (strstr(format.Name,"McStas") ? "component" : "parent"), parent); + if (title) mcfile_tag(f, format, pre, parent, "title", title); + mcfile_tag(f, format, pre, parent, "ratio", ratio); + if (filename) { + mcfile_tag(f, format, pre, parent, "filename", filename); + mcfile_tag(f, format, pre, parent, "format", format.Name); + } else mcfile_tag(f, format, pre, parent, "filename", ""); + + if (p1) + { + if (n*m*p > 1) + { + sprintf(signal, "Min=%g; Max=%g; Mean= %g;", min_z, max_z, mean_z); + if (y1 == 0 && y2 == 0) { y1 = min_z; y2 = max_z;} + else if (z1 == 0 && z2 == 0) { z1 = min_z; z2 = max_z;} + } else strcpy(signal, ""); + + mcfile_tag(f, format, pre, parent, "statistics", stats); + mcfile_tag(f, format, pre, parent, "signal", signal); + + sprintf(values, "%g %g %g", sum_z, mcestimate_error(Nsum, sum_z, P2sum), Nsum); + mcfile_tag(f, format, pre, parent, "values", values); + } + strcpy(lim_field, "xylimits"); + if (n*m > 1) + { + mcfile_tag(f, format, pre, parent, "xvar", xvar); + mcfile_tag(f, format, pre, parent, "yvar", yvar); + mcfile_tag(f, format, pre, parent, "xlabel", xlabel); + mcfile_tag(f, format, pre, parent, "ylabel", ylabel); + if ((n == 1 || m == 1) && strstr(format.Name, "McStas")) + { + sprintf(limits, "%g %g", x1, x2); + strcpy(lim_field, "xlimits"); + } + else + { + mcfile_tag(f, format, pre, parent, "zvar", zvar); + mcfile_tag(f, format, pre, parent, "zlabel", zlabel); + sprintf(limits, "%g %g %g %g %g %g", x1, x2, y1, y2, z1, z2); + } + } else strcpy(limits, "0 0 0 0 0 0"); + mcfile_tag(f, format, pre, parent, lim_field, limits); + mcfile_tag(f, format, pre, parent, "variables", vars); +} /* mcinfo_data */ + +/* main output function, works for 0d, 1d, 2d data */ + +void +mcsiminfo_init(FILE *f) +{ + char info_name[256]; + + if (mcdisable_output_files) return; + if (!f && (!mcsiminfo_name || !strlen(mcsiminfo_name))) return; + if (!strchr(mcsiminfo_name,'.')) sprintf(info_name, "%s.%s", mcsiminfo_name, mcformat.Extension); else strcpy(info_name, mcsiminfo_name); + if (!f) mcsiminfo_file = mcnew_file(info_name, "w"); + else mcsiminfo_file = f; + if(!mcsiminfo_file) + fprintf(stderr, + "Warning: could not open simulation description file '%s'\n", + info_name); + else + { + char pre[20]; + int ismcstas; + char simname[1024]; + char root[10]; + + strcpy(pre, ""); + ismcstas = (strstr(mcformat.Name, "McStas") != NULL); + if (strstr(mcformat.Name, "XML") == NULL && strstr(mcformat.Name, "NeXus") == NULL) strcpy(root, "mcstas"); + else strcpy(root, "root"); + if (mcdirname) sprintf(simname, "%s%s%s", mcdirname, MC_PATHSEP_S, mcsiminfo_name); else sprintf(simname, "%s%s%s", ".", MC_PATHSEP_S, mcsiminfo_name); + + mcfile_header(mcsiminfo_file, mcformat, "header", pre, simname, root); + mcfile_section(mcsiminfo_file, mcformat, "begin", pre, mcinstrument_name, "instrument", root, 1); + mcinfo_instrument(mcsiminfo_file, mcformat, pre, mcinstrument_name); + if (ismcstas) mcfile_section(mcsiminfo_file, mcformat, "end", pre, mcinstrument_name, "instrument", root, 1); + mcfile_section(mcsiminfo_file, mcformat, "begin", pre, simname, "simulation", mcinstrument_name, 2); + mcinfo_simulation(mcsiminfo_file, mcformat, pre, simname); + if (ismcstas) mcfile_section(mcsiminfo_file, mcformat, "end", pre, simname, "simulation", mcinstrument_name, 2); + } +} /* mcsiminfo_init */ + +void +mcsiminfo_close(void) +{ + if (mcdisable_output_files) return; + if(mcsiminfo_file) + { + int ismcstas; + char simname[1024]; + char root[10]; + char pre[10]; + + strcpy(pre, " "); + ismcstas = (strstr(mcformat.Name, "McStas") != NULL); + if (mcdirname) sprintf(simname, "%s%s%s", mcdirname, MC_PATHSEP_S, mcsiminfo_name); else sprintf(simname, "%s%s%s", ".", MC_PATHSEP_S, mcsiminfo_name); + if (strstr(mcformat.Name, "XML") == NULL && strstr(mcformat.Name, "NeXus") == NULL) strcpy(root, "mcstas"); else strcpy(root, "root"); + + if (!ismcstas) + { + mcfile_section(mcsiminfo_file, mcformat, "end", pre, simname, "simulation", mcinstrument_name, 2); + mcfile_section(mcsiminfo_file, mcformat, "end", pre, mcinstrument_name, "instrument", root, 1); + } + mcfile_header(mcsiminfo_file, mcformat, "footer", pre, simname, root); + + if (mcsiminfo_file != stdout) fclose(mcsiminfo_file); + mcsiminfo_file = NULL; + } +} /* mcsiminfo_close */ + +/* mcfile_datablock: output a single data block using specific file format. + * 'part' can be 'data','errors','ncount' + * if y1 == y2 == 0 and McStas format, then stores as a 1D array with [I,E,N] + * return value: 0=0d/2d, 1=1d + * when !single_file, create independent data files, with header and data tags + * if one of the dimensions m,n,p is negative, the data matrix will be written + * after transposition of m/x and n/y dimensions + */ + +static int mcfile_datablock(FILE *f, struct mcformats_struct format, + char *pre, char *parent, char *part, + double *p0, double *p1, double *p2, int m, int n, int p, + char *xlabel, char *ylabel, char *zlabel, char *title, + char *xvar, char *yvar, char *zvar, + double x1, double x2, double y1, double y2, double z1, double z2, + char *filename, char istransposed) +{ + char *Begin; + char *End; + char valid_xlabel[64]; + char valid_ylabel[64]; + char valid_zlabel[64]; + char valid_parent[64]; + FILE *datafile= NULL; + int isdata=0; + int just_header=0; + int i,j, is1d; + double Nsum=0, Psum=0, P2sum=0; + char sec[256]; + char isdata_present; + + if (strstr(part,"data")) + { isdata = 1; Begin = format.BeginData; End = format.EndData; } + if (strstr(part,"errors")) + { isdata = 2; Begin = format.BeginErrors; End = format.EndErrors; } + if (strstr(part,"ncount")) + { isdata = 0; Begin = format.BeginNcount; End = format.EndNcount; } + if (strstr(part, "begin")) just_header = 1; + if (strstr(part, "end")) just_header = 2; + + isdata_present=((isdata==1 && p1) || (isdata==2 && p2) || (isdata==0 && p0)); + + is1d = ((m==1 || n==1) && strstr(format.Name,"McStas")); + mcvalid_name(valid_xlabel, xlabel, 64); + mcvalid_name(valid_ylabel, ylabel, 64); + mcvalid_name(valid_zlabel, zlabel, 64); + + if (strstr(format.Name, "McStas") || !filename || strlen(filename) == 0) + mcvalid_name(valid_parent, parent, 64); + else mcvalid_name(valid_parent, filename, 64); + + /* if normal or begin and part == data: output info_data (sim/data_file) */ + if (isdata == 1 && just_header != 2 && f) + { + mcinfo_data(f, format, pre, valid_parent, title, m, n, p, + xlabel, ylabel, zlabel, xvar, yvar, zvar, + x1, x2, y1, y2, z1, z2, filename, p0, p1, p2, istransposed); + } + + /* if normal or begin: begin part (sim/data file) */ + if (strlen(Begin) && just_header != 2 && f) + pfprintf(f, Begin, "ssssssssssssslllgggggg", + pre, /* %1$s */ + valid_parent, /* %2$s */ + title, /* %3$s */ + filename, /* %4$s */ + xlabel, /* %5$s */ + valid_xlabel, /* %6$s*/ + ylabel, /* %7$s */ + valid_ylabel, /* %8$s */ + zlabel, /* %9$s*/ + valid_zlabel, /* %10$s*/ + xvar, /* %11$s */ + yvar, /* %12$s */ + zvar, /* %13$s */ + m, /* %14$li */ + n, /* %15$li */ + p, /* %16$li */ + x1, /* %17$g */ + x2, /* %18$g */ + y1, /* %19$g*/ + y2, /* %20$g */ + z1, /* %21$g */ + z2); /* %22$g */ + + /* if normal, and !single: + * open datafile, + * if !ascii_only + * if data: write file header, + * call datablock part+header(begin) + * else data file = f + */ + if (!mcsingle_file && just_header == 0) + { + /* if data: open new file for data else append for error/ncount */ + if (filename) datafile = mcnew_file(filename, + (isdata != 1 || strstr(format.Name, "append") ? "a" : "w")); + else datafile = NULL; + /* special case of IDL: can not have empty vectors. Init to 'empty' */ + if (strstr(format.Name, "IDL") && f) fprintf(f, "'external'"); + /* if data, start with root header plus tags of parent data */ + if (datafile && !mcascii_only) + { + char mode[32]; + if (isdata == 1) mcfile_header(datafile, format, "header", + (strstr(format.Name, "McStas") ? "# " : ""), + filename, valid_parent); + sprintf(mode, "%s begin", part); + /* write header+data block begin tags into datafile */ + mcfile_datablock(datafile, format, + (strstr(format.Name, "McStas") ? "# " : ""), + valid_parent, mode, + p0, p1, p2, m, n, p, + xlabel, ylabel, zlabel, title, + xvar, yvar, zvar, + x1, x2, y1, y2, z1, z2, filename, istransposed); + + + } + } + else if (just_header == 0) + { + if (strstr(format.Name, "McStas") && m*n*p>1 && f) + { + if (is1d) sprintf(sec,"array_1d(%d)", m); + else if (p==1) sprintf(sec,"array_2d(%d,%d)", m,n); + else sprintf(sec,"array_3d(%d,%d,%d)", m,n,p); + fprintf(f,"%sbegin %s\n", pre, sec); + datafile = f; + } + if (mcsingle_file) datafile = f; + } + + /* if normal: [data] in data file */ + /* do loops: 2 loops on m,n. */ + if (just_header == 0) + { + char eol_char[3]; + int isIDL, isPython; + int isBinary=0; + + if (strstr(format.Name, "binary float")) isBinary=1; + else if (strstr(format.Name, "binary double")) isBinary=2; + isIDL = (strstr(format.Name, "IDL") != NULL); + isPython = (strstr(format.Name, "Python") != NULL); + if (isIDL) strcpy(eol_char,"$\n"); else strcpy(eol_char,"\n"); + + for(j = 0; j < n*p; j++) /* loop on rows(y) */ + { + if(datafile && !isBinary) + fprintf(datafile,"%s", pre); + for(i = 0; i < m; i++) /* write all columns (x) */ + { + double I=0, E=0, N=0; + double value=0; + long index; + + if (!istransposed) index = i*n*p + j; + else index = i+j*m; + if (p0) N = p0[index]; + if (p1) I = p1[index]; + if (p2) E = p2[index]; + + Nsum += p0 ? N : 1; + Psum += I; + P2sum += p2 ? E : I*I; + + if (p0 && p1 && p2) E = mcestimate_error(N,I,E); + if(datafile && !isBinary && isdata_present) + { + if (isdata == 1) value = I; + else if (isdata == 0) value = N; + else if (isdata == 2) value = E; + if (is1d) + { + double x; + + x = x1+(x2-x1)*(index)/(m*n*p); + if (m*n*p > 1) fprintf(datafile, "%g %g %g %g\n", x, I, E, N); + } + else + { + fprintf(datafile, "%g", value); + if ((isIDL || isPython) && ((i+1)*(j+1) < m*n*p)) fprintf(datafile, ","); + else fprintf(datafile, " "); + } + } + } + if (datafile && !isBinary && isdata_present) fprintf(datafile, eol_char); + } /* end 2 loops if not Binary */ + if (datafile && isBinary) + { + double *d=NULL; + if (isdata==1) d=p1; + else if (isdata==2) d=p2; + else if (isdata==0) d=p0; + + if (d && isBinary == 1) /* float */ + { + float *s; + s = (float*)malloc(m*n*p*sizeof(float)); + if (s) + { + long i, count; + for (i=0; i 1) + fprintf(f,"%send %s\n", pre, sec); + } + + /* set return value */ + return(is1d); +} /* mcfile_datablock */ + +/* mcfile_data: output data/errors/ncounts using specific file format. + * if McStas 1D then data is stored + * as a long 1D array [p0, p1, p2] to reorder -> don't output err/ncount again. + * if p1 or p2 is NULL then skip that part. + */ +static int mcfile_data(FILE *f, struct mcformats_struct format, + char *pre, char *parent, + double *p0, double *p1, double *p2, int m, int n, int p, + char *xlabel, char *ylabel, char *zlabel, char *title, + char *xvar, char *yvar, char *zvar, + double x1, double x2, double y1, double y2, double z1, double z2, + char *filename, char istransposed) +{ + int is1d; + + /* return if f,n,m,p1 NULL */ + if ((m*n*p == 0) || !p1) return (-1); + + /* output data block */ + is1d = mcfile_datablock(f, format, pre, parent, "data", + p0, p1, p2, m, n, p, + xlabel, ylabel, zlabel, title, + xvar, yvar, zvar, + x1, x2, y1, y2, z1, z2, filename, istransposed); + /* return if 1D data */ + if (is1d) return(is1d); + /* output error block and p2 non NULL */ + if (p0 && p2) mcfile_datablock(f, format, pre, parent, "errors", + p0, p1, p2, m, n, p, + xlabel, ylabel, zlabel, title, + xvar, yvar, zvar, + x1, x2, y1, y2, z1, z2, filename, istransposed); + /* output ncount block and p0 non NULL */ + if (p0 && p2) mcfile_datablock(f, format, pre, parent, "ncount", + p0, p1, p2, m, n, p, + xlabel, ylabel, zlabel, title, + xvar, yvar, zvar, + x1, x2, y1, y2, z1, z2, filename, istransposed); + + return(is1d); +} /* mcfile_data */ + +double +mcdetector_out(char *cname, double p0, double p1, double p2, char *filename) +{ + printf("Detector: %s_I=%g %s_ERR=%g %s_N=%g", + cname, p1, cname, mcestimate_error(p0,p1,p2), cname, p0); + if(filename && strlen(filename)) + printf(" \"%s\"", filename); + printf("\n"); + return(p0); +} + +/* parent is the component name */ + +static double mcdetector_out_012D(struct mcformats_struct format, + char *pre, char *parent, char *title, + int m, int n, int p, + char *xlabel, char *ylabel, char *zlabel, + char *xvar, char *yvar, char *zvar, + double x1, double x2, double y1, double y2, double z1, double z2, + char *filename, + double *p0, double *p1, double *p2) +{ + char simname[512]; + int i,j; + double Nsum=0, Psum=0, P2sum=0; + FILE *local_f=NULL; + char istransposed=0; + + if (m<0 || n<0 || p<0 || strstr(format.Name, "binary")) /* do the swap once for all */ + { + double tmp1, tmp2; + char *lab; + istransposed = 1; + + i=m; m=abs(n); n=abs(i); p=abs(p); + } + + if (!strstr(format.Name,"partial")) local_f = mcsiminfo_file; + if (mcdirname) sprintf(simname, "%s%s%s", mcdirname, MC_PATHSEP_S, mcsiminfo_name); else sprintf(simname, "%s%s%s", ".", MC_PATHSEP_S, mcsiminfo_name); + + if (!mcdisable_output_files) + { + + mcfile_section(local_f, format, "begin", pre, parent, "component", simname, 3); + mcfile_section(local_f, format, "begin", pre, filename, "data", parent, 4); + mcfile_data(local_f, format, + pre, parent, + p0, p1, p2, m, n, p, + xlabel, ylabel, zlabel, title, + xvar, yvar, zvar, + x1, x2, y1, y2, z1, z2, filename, istransposed); + + mcfile_section(local_f, format, "end", pre, filename, "data", parent, 4); + mcfile_section(local_f, format, "end", pre, parent, "component", simname, 3); + } + + if (local_f || mcdisable_output_files) + { + for(j = 0; j < n*p; j++) + { + for(i = 0; i < m; i++) + { + double N,I,E; + int index; + if (!istransposed) index = i*n*p + j; + else index = i+j*m; + if (p0) N = p0[index]; + if (p1) I = p1[index]; + if (p2) E = p2[index]; + + Nsum += p0 ? N : 1; + Psum += I; + P2sum += p2 ? E : I*I; + } + } + /* give 0D detector output. */ + mcdetector_out(parent, Nsum, Psum, P2sum, filename); + } + return(Psum); +} /* mcdetector_out_012D */ + +void mcheader_out(FILE *f,char *parent, + int m, int n, int p, + char *xlabel, char *ylabel, char *zlabel, char *title, + char *xvar, char *yvar, char *zvar, + double x1, double x2, double y1, double y2, double z1, double z2, + char *filename) +{ + int loc_single_file; + char pre[3]; + char simname[512]; + loc_single_file = mcsingle_file; mcsingle_file = 1; + + if (!strstr(mcformat.Name, "McStas")) strcpy(pre,""); else strcpy(pre,"# "); + + mcfile_header(f, mcformat, "header", pre, mcinstrument_name, "mcstas"); + mcinfo_instrument(f, mcformat, pre, mcinstrument_name); + if (mcdirname) sprintf(simname, "%s%s%s", mcdirname, MC_PATHSEP_S, mcsiminfo_name); else sprintf(simname, "%s%s%s", ".", MC_PATHSEP_S, mcsiminfo_name); + + mcfile_datablock(f, mcformat, + pre, parent, "data", + NULL,NULL,NULL, m, n, p, + xlabel, ylabel, zlabel, title, + xvar, yvar, zvar, x1, x2, y1, y2, z1, z2, + filename, 0); + + mcsingle_file = loc_single_file; + mcfile_header(f, mcformat, "footer", pre, mcinstrument_name, "mcstas"); +} + + +double mcdetector_out_0D(char *t, double p0, double p1, double p2, char *c) +{ + char pre[20]; + + strcpy(pre, ""); + return(mcdetector_out_012D(mcformat, + pre, c, t, + 1, 1, 1, + "I", "", "", + "I", "", "", + 0, 0, 0, 0, 0, 0, NULL, + &p0, &p1, &p2)); +} + +double mcdetector_out_1D(char *t, char *xl, char *yl, + char *xvar, double x1, double x2, int n, + double *p0, double *p1, double *p2, char *f, char *c) +{ + char pre[20]; + + strcpy(pre, ""); + return(mcdetector_out_012D(mcformat, + pre, c, t, + n, 1, 1, + xl, yl, "Intensity", + xvar, "(I,I_err)", "I", + x1, x2, x1, x2, 0, 0, f, + p0, p1, p2)); +} + +double mcdetector_out_2D(char *t, char *xl, char *yl, + double x1, double x2, double y1, double y2, int m, + int n, double *p0, double *p1, double *p2, char *f, char *c) +{ + char xvar[3]; + char yvar[3]; + char pre[20]; + + strcpy(pre, ""); strcpy(xvar, "x "); strcpy(yvar, "y "); + if (xl && strlen(xl)) strncpy(xvar, xl, 2); + if (yl && strlen(yl)) strncpy(yvar, yl, 2); + + return(mcdetector_out_012D(mcformat, + pre, c, t, + m, n, 1, + xl, yl, "Intensity", + xvar, yvar, "I", + x1, x2, y1, y2, 0, 0, f, + p0, p1, p2)); +} + +double mcdetector_out_3D(char *t, char *xl, char *yl, char *zl, + char *xvar, char *yvar, char *zvar, + double x1, double x2, double y1, double y2, double z1, double z2, int m, + int n, int p, double *p0, double *p1, double *p2, char *f, char *c) +{ + char pre[20]; + + strcpy(pre, ""); + return(mcdetector_out_012D(mcformat, + pre, c, t, + m, n, p, + xl, yl, zl, + xvar, yvar, zvar, + x1, x2, y1, y2, z1, z2, f, + p0, p1, p2)); +} + +/* end of file i/o functions */ + + + +static void +mcuse_dir(char *dir) +{ +#ifdef MC_PORTABLE + fprintf(stderr, "Error: " + "Directory output cannot be used with portable simulation.\n"); + exit(1); +#else /* !MC_PORTABLE */ + if(mkdir(dir, 0777)) + { + fprintf(stderr, "Error: unable to create directory '%s'.\n", dir); + fprintf(stderr, "(Maybe the directory already exists?)\n"); + exit(1); + } + mcdirname = dir; +#endif /* !MC_PORTABLE */ +} + +static void +mcuse_file(char *file) +{ + mcsiminfo_name = file; + mcsingle_file = 1; +} + +void mcuse_format(char *format) +{ + int i,j; + int i_format=-1; + char *tmp; + char low_format[256]; + + /* get the format to lower case */ + if (!format) return; + strcpy(low_format, format); + for (i=0; i MCSTAS define +* monitor_nd-lib: fix Log(signal) log(coord) +* HOPG.trm: reduce 4000 points -> 400 which is enough and faster to resample +* Progress_bar: precent -> percent parameter +* CS: ---------------------------------------------------------------------- +* +* Revision 1.1 2002/08/29 11:39:00 ef +* Initial revision extracted from lib/optics/Monochromators... +*******************************************************************************/ + +#ifndef READ_TABLE_LIB_H +#define READ_TABLE_LIB_H "1.1.0" + +#include + + typedef struct struct_table + { + char filename[128]; + char *header; + double *data; /* vector { x[0], y[0], ... x[n-1], y[n-1]... } */ + double min_x; + double max_x; + double step_x; + long rows; + long columns; + long block_number; + } t_Table; + +/* read_table-lib function prototypes */ +/* ========================================================================= */ +void Table_Init(t_Table *Table); +void Table_Free(t_Table *Table); +long Table_Read(t_Table *Table, char *File, long block_number); +long Table_Read_Offset(t_Table *mc_rt_Table, char *mc_rt_File, long mc_rt_block_number, long *offset, long max_lines); +long Table_Read_Offset_Binary(t_Table *mc_rt_Table, char *mc_rt_File, char *mc_rt_type, long *mc_rt_offset, long mc_rt_rows, long mc_rt_columns); +long Table_Read_Handle(t_Table *Table, FILE *fid, long block_number, long max_lines); +long Table_Rebin(t_Table *Table); +double Table_Index(t_Table Table, long i, long j); +double Table_Value(t_Table Table, double X, long j); +void Table_Info(t_Table Table); +static void Table_Stat(t_Table *mc_rt_Table); + +#endif + +/* end of read_table-lib.h */ +/******************************************************************************* +* +* McStas, neutron ray-tracing package +* Copyright 1997-2002, All rights reserved +* Risoe National Laboratory, Roskilde, Denmark +* Institut Laue Langevin, Grenoble, France +* +* Library: share/read_table-lib.c +* +* %Identification +* Written by: EF +* Date: Aug 28, 2002 +* Origin: ILL +* Release: McStas 1.6 +* Version: 1.2 +* +* This file is to be imported by components that may read data from table files +* It handles some shared functions. Embedded within instrument in runtime mode. +* Variable names have prefix 'mc_rt_' for 'McStas Read Table' to avoid conflicts +* +* Usage: within SHARE +* %include "read_table-lib" +* +* $Id$ +* +* $Log$ +* Revision 1.1 2007/01/30 03:19:43 koennecke +* - Fixed state monitor eclipse commit problems. Siiiiiiiggggghhhhhh! +* +* Revision 1.9 2003/05/20 15:12:33 farhi +* malloc size for read table binary now needs less memory +* +* Revision 1.8 2003/02/11 12:28:46 farhi +* Variouxs bug fixes after tests in the lib directory +* mcstas_r : disable output with --no-out.. flag. Fix 1D McStas output +* read_table:corrected MC_SYS_DIR -> MCSTAS define +* monitor_nd-lib: fix Log(signal) log(coord) +* HOPG.trm: reduce 4000 points -> 400 which is enough and faster to resample +* Progress_bar: precent -> percent parameter +* CS: ---------------------------------------------------------------------- +* +* Revision 1.8 2003/02/06 14:14:41 farhi +* Corrected MC_SYS_DIR into MCSTAS definition of default lib location +* +* Revision 1.2 2002/12/19 12:48:07 ef +* Added binary import. Fixed Rebin. Added Stat. +* +* Revision 1.1 2002/08/29 11:39:00 ef +* Initial revision extracted from lib/optics/Monochromators... +*******************************************************************************/ + +#ifndef READ_TABLE_LIB_H +#error McStas : please import this library with %include "read_table-lib" +#endif + +/******************************************************************************* +* long Read_Table(t_Table *Table, char *name, int block_number) +* input Table: pointer to a t_Table structure +* name: file name from which table should be extracted +* block_number: if the file does contain more than one +* data block, then indicates which one to get (from index 1) +* a 0 value means append/catenate all +* return modified Table t_Table structure containing data, header, ... +* number of read elements (-1: error, 0:header only) +* The routine stores any line starting with '#', '%' and ';' into the header +* File is opened, read and closed +* Other lines are interpreted as numerical data, and stored. +* Data block should be a rectangular matrix or vector. +* Data block may be rebined with Table_Rebin (also sort in ascending order) +*******************************************************************************/ + long Table_Read(t_Table *mc_rt_Table, char *mc_rt_File, long mc_rt_block_number) + { /* reads all/a data block in 'file' and returns a Table structure */ + long mc_rt_offset=0; + return(Table_Read_Offset(mc_rt_Table, mc_rt_File, mc_rt_block_number, &mc_rt_offset, 0)); + + } /* end Table_Read */ + +/******************************************************************************* +* long Table_Read_Offset(t_Table *Table, char *name, int block_number, long *mc_rt_offset +* long mc_rt_max_lines) +* Same as Table_Read(..) except: +* input mc_rt_offset: pointer to an mc_rt_offset (*mc_rt_offset should be 0 at start) +* mc_rt_max_lines: max number of data rows to read from file (0 means all) +* return also updated *mc_rt_offset position (where end of reading occured) +*******************************************************************************/ + long Table_Read_Offset(t_Table *mc_rt_Table, char *mc_rt_File, long mc_rt_block_number, long *mc_rt_offset, long mc_rt_max_lines) + { /* reads all/a data block in 'file' and returns a Table structure */ + FILE *mc_rt_hfile; + long mc_rt_nelements; + + if (!mc_rt_File) return (-1); + if (strlen(mc_rt_File) == 0) return (-1); + mc_rt_hfile = fopen(mc_rt_File, "r"); + if(!mc_rt_hfile) + { + char mc_rt_path[256]; + char mc_rt_dir[256]; + + if (!strchr(mc_rt_File, MC_PATHSEP_C)) + { + if (getenv("MCSTAS")) strcpy(mc_rt_dir, getenv("MCSTAS")); + else strcpy(mc_rt_dir, MCSTAS); + sprintf(mc_rt_path, "%s%c%s%c%s", mc_rt_dir, MC_PATHSEP_C, "data", MC_PATHSEP_C, mc_rt_File); + mc_rt_hfile = fopen(mc_rt_path, "r"); + } + if(!mc_rt_hfile) + { + fprintf(stderr, "Error: Could not open input file '%s' (Table_Read)\n", mc_rt_File); + return (-1); + } + } + if (mc_rt_offset && *mc_rt_offset) fseek(mc_rt_hfile, *mc_rt_offset, SEEK_SET); + mc_rt_nelements = Table_Read_Handle(mc_rt_Table, mc_rt_hfile, mc_rt_block_number, mc_rt_max_lines); + strncpy(mc_rt_Table->filename, mc_rt_File, 128); + if (mc_rt_offset) *mc_rt_offset=ftell(mc_rt_hfile); + fclose(mc_rt_hfile); + return(mc_rt_nelements); + + } /* end Table_Read_Offset */ + +/******************************************************************************* +* long Table_Read_Offset_Binary(t_Table *mc_rt_Table, char *mc_rt_File, char *mc_rt_type, +* long +mc_rt_offset, long mc_rt_max_lines) +* Same as Table_Read_Offset(..) except that it handles binary files. +* input mc_rt_type: may be "float" or "double" +* mc_rt_offset: pointer to an mc_rt_offset (*mc_rt_offset should be 0 at start) +* mc_rt_columns: number of columns +* mc_rt_rows : max number of data rows to read from file (0 means all) +* return also updated *mc_rt_offset position (where end of reading occured) +*******************************************************************************/ + long Table_Read_Offset_Binary(t_Table *mc_rt_Table, char *mc_rt_File, char *mc_rt_type, long *mc_rt_offset, long mc_rt_rows, long mc_rt_columns) + { + long mc_rt_nelements, mc_rt_sizeofelement; + long mc_rt_filesize; + FILE *mc_rt_hfile; + struct stat mc_rt_stfile; + double *mc_rt_data; + long mc_rt_i; + + Table_Init(mc_rt_Table); + + stat(mc_rt_File,&mc_rt_stfile); + mc_rt_filesize = mc_rt_stfile.st_size; + mc_rt_hfile = fopen(mc_rt_File, "r"); + if(!mc_rt_hfile) + { + char mc_rt_path[256]; + char mc_rt_dir[256]; + + if (!strchr(mc_rt_File, MC_PATHSEP_C)) + { + if (getenv("MCSTAS")) strcpy(mc_rt_dir, getenv("MCSTAS")); + else strcpy(mc_rt_dir, MCSTAS); + sprintf(mc_rt_path, "%s%c%s%c%s", mc_rt_dir, MC_PATHSEP_C, "data", MC_PATHSEP_C, mc_rt_File); + mc_rt_hfile = fopen(mc_rt_path, "r"); + } + if(!mc_rt_hfile) + { + fprintf(stderr, "Error: Could not open input file '%s' (Table_Read_Binary)\n", mc_rt_File); + return (-1); + } + } + if (mc_rt_type && !strcmp(mc_rt_type,"double")) mc_rt_sizeofelement = sizeof(double); + else mc_rt_sizeofelement = sizeof(float); + if (mc_rt_offset && *mc_rt_offset) fseek(mc_rt_hfile, *mc_rt_offset, SEEK_SET); + if (mc_rt_rows && mc_rt_filesize > mc_rt_sizeofelement*mc_rt_columns*mc_rt_rows) + mc_rt_nelements = mc_rt_columns*mc_rt_rows; + else mc_rt_nelements = (long)(mc_rt_filesize/mc_rt_sizeofelement); + if (!mc_rt_nelements || mc_rt_filesize <= *mc_rt_offset) return(0); + mc_rt_data = (double*)malloc(mc_rt_nelements*mc_rt_sizeofelement); + if (!mc_rt_data) { + fprintf(stderr,"Error: allocating %d elements for %s file '%s'. Too big (Table_Read_Offset_Binary).\n", mc_rt_nelements, mc_rt_type, mc_rt_File); + exit(-1); + } + mc_rt_nelements = fread(mc_rt_data, mc_rt_sizeofelement, mc_rt_nelements, mc_rt_hfile); + + if (!mc_rt_data || !mc_rt_nelements) + { + fprintf(stderr,"Error: reading %d elements from %s file '%s' (Table_Read_Offset_Binary)\n", mc_rt_nelements, mc_rt_type, mc_rt_File); + exit(-1); + } + if (mc_rt_offset) *mc_rt_offset=ftell(mc_rt_hfile); + fclose(mc_rt_hfile); + mc_rt_data = (double*)realloc(mc_rt_data, (double)mc_rt_nelements*mc_rt_sizeofelement); + /* copy file data into Table */ + if (mc_rt_type && !strcmp(mc_rt_type,"double")) mc_rt_Table->data = mc_rt_data; + else { + float *mc_rt_s; + double *mc_rt_dataf; + mc_rt_s = (float*)mc_rt_data; + mc_rt_dataf = (double*)malloc(sizeof(double)*mc_rt_nelements); + for (mc_rt_i=0; mc_rt_idata = mc_rt_dataf; + } + strcpy(mc_rt_Table->filename, mc_rt_File); + mc_rt_Table->rows = mc_rt_nelements/mc_rt_columns; + mc_rt_Table->columns = mc_rt_columns; + Table_Stat(mc_rt_Table); + + return(mc_rt_nelements); + } /* end Table_Read_Offset_Binary */ + +/******************************************************************************* +* long Read_Table_Handle(t_Table *Table, FILE *fid, int block_number) +* input Table:pointer to a t_Table structure +* fid: pointer to FILE handle +* block_number: if the file does contain more than one +* data block, then indicates which one to get (from index 1) +* a 0 value means append/catenate all +* mc_rt_max_lines: if non 0, only reads that number of lines +* return modified Table t_Table structure containing data, header, ... +* number of read elements (-1: error, 0:header only) +* The routine stores any line starting with '#', '%' and ';' into the header +* Other lines are interpreted as numerical data, and stored. +* Data block should be a rectangular matrix or vector. +* Data block may be rebined with Table_Rebin (also sort in ascending order) +*******************************************************************************/ + long Table_Read_Handle(t_Table *mc_rt_Table, FILE *mc_rt_hfile, long mc_rt_block_number, long mc_rt_max_lines) + { /* reads all/a data block in 'file' and returns a Table structure */ + double *mc_rt_Data; + char *mc_rt_Header; + long mc_rt_malloc_size = 1024; + long mc_rt_malloc_size_h = 4096; + char mc_rt_flag_exit_loop = 0; + long mc_rt_Rows = 0, mc_rt_Columns = 0; + long mc_rt_count_in_array = 0; + long mc_rt_count_in_header = 0; + long mc_rt_cur_block_number = 0; + char mc_rt_flag_in_array = 0; + + Table_Init(mc_rt_Table); + + if(!mc_rt_hfile) + { + fprintf(stderr, "Error: File handle is NULL (Table_Read_Handle).\n"); + return (-1); + } + mc_rt_Header = (char*) malloc(mc_rt_malloc_size_h*sizeof(char)); + mc_rt_Data = (double*)malloc(mc_rt_malloc_size *sizeof(double)); + if ((mc_rt_Header == NULL) || (mc_rt_Data == NULL)) + { + fprintf(stderr, "Error: Could not allocate Table and Header (Table_Read_Handle).\n"); + return (-1); + } + mc_rt_Header[0] = '\0'; + + while (!mc_rt_flag_exit_loop) + { + char mc_rt_line[4096]; + long mc_rt_back_pos=0; + + mc_rt_back_pos = ftell(mc_rt_hfile); + if (fgets(mc_rt_line, 4096, mc_rt_hfile) != NULL) + { /* tries to read some informations from the file */ + int mc_rt_i=0; + /* first skip blank and tabulation characters */ + while (mc_rt_line[mc_rt_i] == ' ' || mc_rt_line[mc_rt_i] == '\t') mc_rt_i++; + if ((mc_rt_line[mc_rt_i] == '#') || (mc_rt_line[mc_rt_i] == '%') + || (mc_rt_line[mc_rt_i] == ';') || (mc_rt_line[mc_rt_i] == '/')) + { + if (mc_rt_flag_in_array && mc_rt_block_number) + mc_rt_count_in_header = 0; /* comment comes after a data block */ + mc_rt_count_in_header += strlen(mc_rt_line); + if (mc_rt_count_in_header+4096 > mc_rt_malloc_size_h) + { /* if succeed and in array : add (and realloc if necessary) */ + mc_rt_malloc_size_h = mc_rt_count_in_header+4096; + mc_rt_Header = (char*)realloc(mc_rt_Header, mc_rt_malloc_size_h*sizeof(char)); + } + strncat(mc_rt_Header, mc_rt_line, 4096); + mc_rt_flag_in_array = 0; /* will start a new data block */ + } /* line is a comment */ + else + { + double mc_rt_X; + + /* get the number of columns splitting mc_rt_line with strtok */ + if (sscanf(mc_rt_line,"%lg ",&mc_rt_X) == 1) /* mc_rt_line begins at least with one num */ + { + char *mc_rt_InputTokens, *mc_rt_lexeme; + char mc_rt_End_Line_Scanning_Flag= 0; + long mc_rt_This_Line_Columns = 0; + + mc_rt_InputTokens = mc_rt_line; + + while (!mc_rt_End_Line_Scanning_Flag) + { + mc_rt_lexeme = (char *)strtok(mc_rt_InputTokens, " ,;\t\n\r"); + mc_rt_InputTokens = NULL; + if ((mc_rt_lexeme != NULL) && (strlen(mc_rt_lexeme) != 0)) + { + if (sscanf(mc_rt_lexeme,"%lg ",&mc_rt_X) == 1) /* found a number */ + { + if (mc_rt_flag_in_array == 0 + && (((mc_rt_block_number == 0) || (mc_rt_block_number > mc_rt_cur_block_number)))) /* not already in a block -> start */ + { /* starts a new data block */ + if (mc_rt_block_number) + { /* initialise a new data block */ + mc_rt_Rows = 0; + mc_rt_count_in_array = 0; + } /* else append */ + mc_rt_cur_block_number++; + mc_rt_flag_in_array = 1; + mc_rt_This_Line_Columns= 0; /* starts the first data row of this block */ + + } + if (mc_rt_flag_in_array && ((mc_rt_block_number == 0) || (mc_rt_block_number == mc_rt_cur_block_number))) + { /* append all, or within requested block -> store data in row */ + if (mc_rt_count_in_array >= mc_rt_malloc_size) + { /* if succeed and in array : add (and realloc if necessary) */ + mc_rt_malloc_size = mc_rt_count_in_array+1024; + mc_rt_Data = (double*)realloc(mc_rt_Data, mc_rt_malloc_size*sizeof(double)); + if (mc_rt_Data == NULL) + { + fprintf(stderr, "Error: Can not re-allocate memory %i (Table_Read_Handle).\n", mc_rt_malloc_size*sizeof(double)); + return (-1); + } + } + /* test if we've read already the desired number of data lines */ + if (mc_rt_This_Line_Columns == 0 + && mc_rt_max_lines && mc_rt_Rows >= mc_rt_max_lines) { + mc_rt_End_Line_Scanning_Flag = 1; + mc_rt_flag_exit_loop = 1; + mc_rt_flag_in_array = 0; + /* reposition to begining of line */ + fseek(mc_rt_hfile, mc_rt_back_pos, SEEK_SET); + } else { /* store into data array */ + if (mc_rt_This_Line_Columns == 0) mc_rt_Rows++; + mc_rt_Data[mc_rt_count_in_array] = mc_rt_X; + mc_rt_count_in_array++; + mc_rt_This_Line_Columns++; + } + } + else + { /* not in a block to store */ + if ((mc_rt_block_number) && (mc_rt_cur_block_number > mc_rt_block_number)) + { /* we finished to extract block -> force end of file reading */ + mc_rt_End_Line_Scanning_Flag = 1; + mc_rt_flag_exit_loop = 1; + mc_rt_flag_in_array = 0; + } + } + } /* end if sscanf mc_rt_lexeme -> numerical */ + else + { /* token is not numerical in that line */ + mc_rt_End_Line_Scanning_Flag = 1; mc_rt_flag_in_array = 0; + } + } + else + { /* no more tokens in mc_rt_line */ + mc_rt_End_Line_Scanning_Flag = 1; + if (mc_rt_This_Line_Columns) mc_rt_Columns = mc_rt_This_Line_Columns; + } + } /* end while mc_rt_End_Line_Scanning_Flag */ + } + else + { /* non-comment line does not begin with a number: ignore line */ + mc_rt_flag_in_array = 0; + } + } /* end: if not mc_rt_line comment else numerical */ + } /* end: if fgets */ + else mc_rt_flag_exit_loop = 1; /* else fgets : end of file */ + } /* end while mc_rt_flag_exit_loop */ + + mc_rt_Table->block_number = mc_rt_block_number; + if (mc_rt_count_in_header) mc_rt_Header = (char*)realloc(mc_rt_Header, mc_rt_count_in_header*sizeof(char)); + mc_rt_Table->header = mc_rt_Header; + if (mc_rt_count_in_array*mc_rt_Rows*mc_rt_Columns == 0) + { + mc_rt_Table->rows = 0; + mc_rt_Table->columns = 0; + free(mc_rt_Data); + return (0); + } + if (mc_rt_Rows * mc_rt_Columns != mc_rt_count_in_array) + { + fprintf(stderr, "Warning: Read_Table : Data has %li values that should be %li x %li\n", mc_rt_count_in_array, mc_rt_Rows, mc_rt_Columns); + mc_rt_Columns = mc_rt_count_in_array; mc_rt_Rows = 1; + } + mc_rt_Data = (double*)realloc(mc_rt_Data, mc_rt_count_in_array*sizeof(double)); + mc_rt_Table->data = mc_rt_Data; + mc_rt_Table->rows = mc_rt_Rows; + mc_rt_Table->columns = mc_rt_Columns; + Table_Stat(mc_rt_Table); + return (mc_rt_count_in_array); + + } /* end Table_Read_Handle */ + +/******************************************************************************* +* long Rebin_Table(t_Table *Table) +* input Table: table containing data +* return new Table with increasing, evenly spaced first column (index 0) +* number of data elements (-1: error, 0:header only) +*******************************************************************************/ + long Table_Rebin(t_Table *mc_rt_Table) + { + double mc_rt_new_step=0; + long mc_rt_i; + long mc_rt_tmp; + char mc_rt_monotonic = 1; + /* performs linear interpolation on X axis (0-th column) */ + + if (!mc_rt_Table->data + || mc_rt_Table->rows*mc_rt_Table->columns == 0 || !mc_rt_Table->step_x) + return(0); + mc_rt_tmp = mc_rt_Table->rows; + mc_rt_new_step = mc_rt_Table->step_x; + for (mc_rt_i=0; mc_rt_i < mc_rt_Table->rows - 1; mc_rt_i++) + { + double mc_rt_current_step; + double mc_rt_X, mc_rt_diff; + mc_rt_X = Table_Index(*mc_rt_Table,mc_rt_i ,0); + mc_rt_diff = Table_Index(*mc_rt_Table,mc_rt_i+1,0) - mc_rt_X; + mc_rt_current_step = fabs(mc_rt_diff); + if ((mc_rt_Table->max_x - mc_rt_Table->min_x)*mc_rt_diff < 0 && mc_rt_monotonic && mc_rt_Table->columns > 1) + { + fprintf(stderr, "Warning: Rebin_Table : Data from file '%s' (%li x %li) is not monotonic (at row %li)\n", mc_rt_Table->filename, mc_rt_Table->rows, mc_rt_Table->columns, mc_rt_i); + mc_rt_monotonic = 0; + } + if (mc_rt_current_step > 0 && mc_rt_current_step < mc_rt_new_step) mc_rt_new_step = mc_rt_current_step; + else mc_rt_tmp--; + } /* for */ + if (fabs(mc_rt_new_step/mc_rt_Table->step_x) >= 0.98) + return (mc_rt_Table->rows*mc_rt_Table->columns); + if (mc_rt_tmp > 0 && mc_rt_new_step > 0 && mc_rt_Table->columns > 1) /* table was not already evenly sampled */ + { + long mc_rt_Length_Table; + double *mc_rt_New_Table; + /* modify step if leads to too many points */ + if (mc_rt_Table->rows > 2000) + if (mc_rt_new_step < mc_rt_Table->step_x) + mc_rt_new_step = mc_rt_Table->step_x; + if (mc_rt_new_step*10 < mc_rt_Table->step_x) + mc_rt_new_step = mc_rt_Table->step_x/10; + mc_rt_Length_Table = ceil(fabs(mc_rt_Table->max_x - mc_rt_Table->min_x)/mc_rt_new_step); + mc_rt_New_Table = (double*)malloc(mc_rt_Length_Table*mc_rt_Table->columns*sizeof(double)); + + for (mc_rt_i=0; mc_rt_i < mc_rt_Length_Table; mc_rt_i++) + { + long mc_rt_j; + long mc_rt_old_i; + double mc_rt_X; + double mc_rt_X1, mc_rt_X2, mc_rt_Y1, mc_rt_Y2; + char mc_rt_test=0; + mc_rt_X = mc_rt_Table->min_x + mc_rt_i*mc_rt_new_step; + mc_rt_New_Table[mc_rt_i*mc_rt_Table->columns] = mc_rt_X; + /* look for index surrounding X in the old table -> index old_i, old-1 */ + for (mc_rt_old_i=1; mc_rt_old_i < mc_rt_Table->rows-1; mc_rt_old_i++) + { + mc_rt_X2 = Table_Index(*mc_rt_Table,mc_rt_old_i ,0); + mc_rt_X1 = Table_Index(*mc_rt_Table,mc_rt_old_i-1,0); + if (mc_rt_Table->min_x < mc_rt_Table->max_x) + mc_rt_test = ((mc_rt_X1 <= mc_rt_X) && (mc_rt_X < mc_rt_X2)); + else + mc_rt_test = ((mc_rt_X2 <= mc_rt_X) && (mc_rt_X < mc_rt_X1)); + if (mc_rt_test) break; + } + + for (mc_rt_j=1; mc_rt_j < mc_rt_Table->columns; mc_rt_j++) + { + mc_rt_Y2 = Table_Index(*mc_rt_Table,mc_rt_old_i ,mc_rt_j); + mc_rt_Y1 = Table_Index(*mc_rt_Table,mc_rt_old_i-1,mc_rt_j); + if (mc_rt_X2-mc_rt_X1) + { + /* linear interpolation */ + double mc_rt_slope = (mc_rt_Y2-mc_rt_Y1)/(mc_rt_X2-mc_rt_X1); + mc_rt_New_Table[mc_rt_i*mc_rt_Table->columns+mc_rt_j] = mc_rt_Y1+mc_rt_slope*(mc_rt_X-mc_rt_X1); + } + else + mc_rt_New_Table[mc_rt_i*mc_rt_Table->columns+mc_rt_j] = mc_rt_Y2; + } + + } /* end for i */ + mc_rt_Table->rows = mc_rt_Length_Table; + mc_rt_Table->step_x = mc_rt_new_step; + free(mc_rt_Table->data); + mc_rt_Table->data = mc_rt_New_Table; + } /* end if tmp */ + return (mc_rt_Table->rows*mc_rt_Table->columns); + } /* end Rebin_Table */ + +/******************************************************************************* +* double Table_Index(t_Table Table, long i, long j) +* input Table: table containing data +* i : index of row (0:mc_rt_Rows-1) +* j : index of column (0:Columns-1) +* return Value = data[i][j] +* Returns Value from the i-th row, j-th column of Table +* Tests are performed on indexes i,j to avoid errors +*******************************************************************************/ + double Table_Index(t_Table mc_rt_Table, long mc_rt_i, long mc_rt_j) + { + long mc_rt_AbsIndex; + + if (mc_rt_i < 0) mc_rt_i = 0; + if (mc_rt_i >= mc_rt_Table.rows) mc_rt_i = mc_rt_Table.rows-1; + if (mc_rt_j < 0) mc_rt_j = 0; + if (mc_rt_j >= mc_rt_Table.columns) mc_rt_j = mc_rt_Table.columns-1; + mc_rt_AbsIndex = mc_rt_i*(mc_rt_Table.columns)+mc_rt_j; + if (mc_rt_Table.data != NULL) + return(mc_rt_Table.data[mc_rt_AbsIndex]); + else + return(0); + } + +/******************************************************************************* +* double Table_Value(t_Table Table, double X, long j) +* input Table: table containing data +* X : data value in the first column (index 0) +* j : index of column from which is extracted the Value (0:Columns-1) +* return Value = data[index for X][j] +* Returns Value from the j-th column of Table corresponding to the +* X value for the 1st column (index 0) +* Tests are performed (within Table_Index) on indexes i,j to avoid errors +* NOTE: data should rather be monotonic, and evenly sampled. +*******************************************************************************/ + double Table_Value(t_Table mc_rt_Table, double X, long j) + { + long mc_rt_Index; + double mc_rt_Value; + + if (mc_rt_Table.step_x != 0) + mc_rt_Index = floor((X - mc_rt_Table.min_x)/mc_rt_Table.step_x); + else mc_rt_Index=0; + mc_rt_Value = Table_Index(mc_rt_Table, mc_rt_Index, j); + + return(mc_rt_Value); + } +/******************************************************************************* +* void Table_Free(t_Table *Table) +*******************************************************************************/ + void Table_Free(t_Table *mc_rt_Table) + { + if (mc_rt_Table->data != NULL) free(mc_rt_Table->data); + if (mc_rt_Table->header != NULL) free(mc_rt_Table->header); + mc_rt_Table->data = NULL; + mc_rt_Table->header = NULL; + } +/****************************************************************************** +* void Table_Info(t_Table Table) +* prints informations about a Table +*******************************************************************************/ + void Table_Info(t_Table mc_rt_Table) + { + printf("Table from file '%s'", mc_rt_Table.filename); + if (mc_rt_Table.block_number) printf(" (block %li)", mc_rt_Table.block_number); + if ((mc_rt_Table.data != NULL) && (mc_rt_Table.rows*mc_rt_Table.columns)) + { + printf(" is %li x %li.\n", mc_rt_Table.rows, mc_rt_Table.columns); + /* printf("Data axis range %f-%f, step=%f\n", mc_rt_Table.min_x, mc_rt_Table.max_x, mc_rt_Table.step_x); */ + } + else printf(" is empty.\n"); + } + +/****************************************************************************** +* void Table_Init(t_Table *Table) +* initialise a Table to empty +*******************************************************************************/ + void Table_Init(t_Table *mc_rt_Table) + { + mc_rt_Table->data = NULL; + mc_rt_Table->header = NULL; + mc_rt_Table->filename[0]= '\0'; + mc_rt_Table->rows = 0; + mc_rt_Table->columns = 0; + mc_rt_Table->min_x = 0; + mc_rt_Table->max_x = 0; + mc_rt_Table->step_x = 0; + mc_rt_Table->block_number = 0; + } + +/****************************************************************************** +* void Table_Star(t_Table *Table) +* computes min/max/mean step of 1st column +*******************************************************************************/ + static void Table_Stat(t_Table *mc_rt_Table) + { + long mc_rt_i; + double mc_rt_max_x, mc_rt_min_x; + + if (!mc_rt_Table->rows || !mc_rt_Table->columns) return; + mc_rt_max_x = mc_rt_Table->data[0]; + mc_rt_min_x = mc_rt_Table->data[(mc_rt_Table->rows-1)*mc_rt_Table->columns]; + + for (mc_rt_i=0; mc_rt_i < mc_rt_Table->rows - 1; mc_rt_i++) + { + double mc_rt_X; + mc_rt_X = Table_Index(*mc_rt_Table,mc_rt_i ,0); + if (mc_rt_X < mc_rt_min_x) mc_rt_min_x = mc_rt_X; + if (mc_rt_X > mc_rt_max_x) mc_rt_max_x = mc_rt_X; + } /* for */ + mc_rt_Table->max_x = mc_rt_max_x; + mc_rt_Table->min_x = mc_rt_min_x; + mc_rt_Table->step_x = (mc_rt_Table->max_x - mc_rt_Table->min_x)/mc_rt_Table->rows; + } + +/* end of read_table-lib.c */ + + +long Virtual_input_Read_Input(char *aFile, char *aType, t_Table *aTable, long *aOffset) + { + long max_lines = 50000; + long length=0; + char bType[32]; + + if (!aFile) return (-1); + if (aType) strcpy(bType, aType); + else strcpy(bType, "???"); + + Table_Free(aTable); + + /* Try to Open neutron input text file. */ + if((aFile && aType == NULL) || !strcmp(bType,"text")) { + Table_Read_Offset(aTable, aFile, 0, aOffset, max_lines); /* read data from file into rTable */ + strcpy(bType, "text"); + } + if (!aTable->data && aType && aType[0] != 't') + Table_Read_Offset_Binary(aTable, aFile, aType, aOffset, max_lines, 11); + + return(aTable->rows); + } +#line 5023 "dmcafter.c" +#line 103 "/afs/psi.ch/project/sinq/sl-linux/lib/mcstas/sources/Virtual_input.comp" + int rep=0; /* Neutron repeat count */ + long pos=0; + long nrows=0; + long nread=0; + long Offset=0; + t_Table rTable; +#line 5031 "dmcafter.c" +#undef bufsize +#undef repeat_count +#undef Virtual_input_Read_Input +#undef rTable +#undef Offset +#undef nread +#undef nrows +#undef pos +#undef rep +#undef type +#undef file +#undef mccompcurname +#undef mccompcurindex + +/* User declarations for component 'out2_slit' [7]. */ +#define mccompcurname out2_slit +#define mccompcurindex 3 +#define xmin mccout2_slit_xmin +#define xmax mccout2_slit_xmax +#define ymin mccout2_slit_ymin +#define ymax mccout2_slit_ymax +#define radius mccout2_slit_radius +/* Shared user declarations for all components 'Slit'. */ +#undef radius +#undef ymax +#undef ymin +#undef xmax +#undef xmin +#undef mccompcurname +#undef mccompcurindex + +/* User declarations for component 'PSD_sample' [7]. */ +#define mccompcurname PSD_sample +#define mccompcurindex 4 +#define Nsum mccPSD_sample_Nsum +#define psum mccPSD_sample_psum +#define p2sum mccPSD_sample_p2sum +#define currentCount mccPSD_sample_currentCount +#define xmin mccPSD_sample_xmin +#define xmax mccPSD_sample_xmax +#define ymin mccPSD_sample_ymin +#define ymax mccPSD_sample_ymax +#define controlfile mccPSD_sample_controlfile +#define dumpCount mccPSD_sample_dumpCount +/* Shared user declarations for all components 'MKMonitor'. */ +#line 50 "MKMonitor.comp" + void dumpTotal(char *ffilename, long totalCounts){ + FILE *fd = NULL; + char tmp[1024]; + + strncpy(tmp,ffilename, 1000); + strcat(tmp,"tmp"); + fd = fopen(tmp,"w"); + if(fd != NULL){ + fprintf(fd,"%ld\n",totalCounts); + fclose(fd); + rename(tmp,ffilename); + unlink(tmp); + } + } +#line 5092 "dmcafter.c" +#line 67 "MKMonitor.comp" + long currentCount; + double Nsum; + double psum, p2sum; +#line 5097 "dmcafter.c" +#undef dumpCount +#undef controlfile +#undef ymax +#undef ymin +#undef xmax +#undef xmin +#undef currentCount +#undef p2sum +#undef psum +#undef Nsum +#undef mccompcurname +#undef mccompcurindex + +/* User declarations for component 'sample' [7]. */ +#define mccompcurname sample +#define mccompcurindex 6 +#define reflections mccsample_reflections +#define my_s_v2 mccsample_my_s_v2 +#define my_a_v mccsample_my_a_v +#define q_v mccsample_q_v +#define d_phi0 mccsample_d_phi0 +#define radius mccsample_radius +#define focus_r mccsample_focus_r +#define h mccsample_h +#define pack mccsample_pack +#define Vc mccsample_Vc +#define sigma_a mccsample_sigma_a +#define sigma_inc mccsample_sigma_inc +#define frac mccsample_frac +#define focus_xw mccsample_focus_xw +#define focus_yh mccsample_focus_yh +#define focus_aw mccsample_focus_aw +#define focus_ah mccsample_focus_ah +#define target_x mccsample_target_x +#define target_y mccsample_target_y +#define target_z mccsample_target_z +#define target_index mccsample_target_index +/* Shared user declarations for all components 'PowderN'. */ +#line 70 "PowderN.comp" + /* used for reading data table from file */ + +/* Declare structures and functions only once in each instrument. */ +#ifndef POWDERN_DECL +#define POWDERN_DECL + + struct line_data + { + double F2; /* Value of structure factor */ + double q; /* Qvector */ + int j; /* Multiplicity */ + double DW; /* Debye-Waller factor */ + double w; /* Intrinsic line width */ + }; + + struct line_info_struct + { + struct line_data *list; /* Reflection array */ + int count; /* Number of reflections */ + }; + + void + read_line_data(char *SC_file, struct line_info_struct *info) + { + struct line_data *list = NULL; + int size = 0; + t_Table sTable; /* sample data table structure from SC_file */ + int i=0; + + Table_Read(&sTable, SC_file, 1); /* read 1st block data from SC_file into sTable*/ + if (sTable.columns < 5) + exit(fprintf(stderr, "PowderN: Error: The number of columns in %s should be at least %d for [j,q,F2,DW,w]\n", SC_file, 5)); + if (!sTable.rows) + exit(fprintf(stderr, "PowderN: Error: The number of rows in %s should be at least %d\n", SC_file, 1)); + else size = sTable.rows; + printf("PowderN: Reading in %d rows from %s... ",size, SC_file); + /* allocate line_data array */ + list = (struct line_data*)malloc(size*sizeof(struct line_data)); + for (i=0; ilist = list; + info->count = i; + } +#endif /* !POWDERN_DECL */ + +#line 5203 "dmcafter.c" +#line 141 "PowderN.comp" + struct line_info_struct line_info; + int Nq; + double *my_s_v2, my_s_v2_sum; + double my_a_v, my_inc, *q_v; + double *w_v, v, solid_angle; +#line 5210 "dmcafter.c" +#undef target_index +#undef target_z +#undef target_y +#undef target_x +#undef focus_ah +#undef focus_aw +#undef focus_yh +#undef focus_xw +#undef frac +#undef sigma_inc +#undef sigma_a +#undef Vc +#undef pack +#undef h +#undef focus_r +#undef radius +#undef d_phi0 +#undef q_v +#undef my_a_v +#undef my_s_v2 +#undef reflections +#undef mccompcurname +#undef mccompcurindex + +/* User declarations for component 'Det9' [7]. */ +#define mccompcurname Det9 +#define mccompcurindex 7 +#define options mccDet9_options +#define filename mccDet9_filename +#define DEFS mccDet9_DEFS +#define Vars mccDet9_Vars +#define xwidth mccDet9_xwidth +#define yheight mccDet9_yheight +#define zthick mccDet9_zthick +#define xmin mccDet9_xmin +#define xmax mccDet9_xmax +#define ymin mccDet9_ymin +#define ymax mccDet9_ymax +#define zmin mccDet9_zmin +#define zmax mccDet9_zmax +/* Shared user declarations for all components 'Monitor_nD'. */ +#line 208 "/afs/psi.ch/project/sinq/sl-linux/lib/mcstas/monitors/Monitor_nD.comp" +/******************************************************************************* +* +* McStas, neutron ray-tracing package +* Copyright 1997-2002, All rights reserved +* Risoe National Laboratory, Roskilde, Denmark +* Institut Laue Langevin, Grenoble, France +* +* Library: share/monitor_nd-lib.h +* +* %Identification +* Written by: EF +* Date: Aug 28, 2002 +* Origin: ILL +* Release: McStas 1.6 +* Version: 1.1 +* +* This file is to be imported by the monitor_nd related components +* It handles some shared functions. +* +* Usage: within SHARE +* %include "monitor_nd-lib" +* +* $Id$ +* +* $Log$ +* Revision 1.1 2007/01/30 03:19:43 koennecke +* - Fixed state monitor eclipse commit problems. Siiiiiiiggggghhhhhh! +* +* Revision 1.8 2003/02/11 12:28:46 farhi +* Variouxs bug fixes after tests in the lib directory +* mcstas_r : disable output with --no-out.. flag. Fix 1D McStas output +* read_table:corrected MC_SYS_DIR -> MCSTAS define +* monitor_nd-lib: fix Log(signal) log(coord) +* HOPG.trm: reduce 4000 points -> 400 which is enough and faster to resample +* Progress_bar: precent -> percent parameter +* CS: ---------------------------------------------------------------------- +* +* Revision 1.1 2002/08/28 11:39:00 ef +* Initial revision extracted from lib/monitors/Monitor_nD.comp +*******************************************************************************/ + +#ifndef MONITOR_ND_LIB_H + +#define MONITOR_ND_LIB_H "1.1.1" +#define MONnD_COORD_NMAX 30 /* max number of variables to record */ + + typedef struct MonitornD_Defines + { + char COORD_NONE ; + char COORD_X ; + char COORD_Y ; + char COORD_Z ; + char COORD_VX ; + char COORD_VY ; + char COORD_VZ ; + char COORD_T ; + char COORD_P ; + char COORD_SX ; + char COORD_SY ; + char COORD_SZ ; + char COORD_KX ; + char COORD_KY ; + char COORD_KZ ; + char COORD_K ; + char COORD_V ; + char COORD_ENERGY; + char COORD_LAMBDA; + char COORD_RADIUS; + char COORD_HDIV ; + char COORD_VDIV ; + char COORD_ANGLE ; + char COORD_NCOUNT; + char COORD_THETA ; + char COORD_PHI ; + char COORD_USER1 ; + char COORD_USER2 ; + + /* token modifiers */ + char COORD_VAR ; /* next token should be a variable or normal option */ + char COORD_MIN ; /* next token is a min value */ + char COORD_MAX ; /* next token is a max value */ + char COORD_DIM ; /* next token is a bin value */ + char COORD_FIL ; /* next token is a filename */ + char COORD_EVNT ; /* next token is a buffer size value */ + char COORD_3HE ; /* next token is a 3He pressure value */ + char COORD_INTERM; /* next token is an intermediate save value (percent) */ + char COORD_LOG ; /* next variable will be in log scale */ + char COORD_ABS ; /* next variable will be in abs scale */ + char COORD_SIGNAL; /* next variable will be the signal var */ + int COORD_AUTO ; /* set auto limits */ + + char TOKEN_DEL[32]; /* token separators */ + + char SHAPE_SQUARE; /* shape of the monitor */ + char SHAPE_DISK ; + char SHAPE_SPHERE; + char SHAPE_CYLIND; + char SHAPE_BANANA; /* cylinder without top/bottom, on restricted angular area */ + char SHAPE_BOX ; + + } MonitornD_Defines_type; + + typedef struct MonitornD_Variables + { + double area; + double Sphere_Radius ; + double Cylinder_Height ; + char Flag_With_Borders ; /* 2 means xy borders too */ + char Flag_List ; /* 1 store 1 buffer, 2 is list all, 3 list all+append */ + char Flag_Multiple ; /* 1 when n1D, 0 for 2D */ + char Flag_Verbose ; + int Flag_Shape ; + char Flag_Auto_Limits ; /* get limits from first Buffer */ + char Flag_Absorb ; /* monitor is also a slit */ + char Flag_per_cm2 ; /* flux is per cm2 */ + char Flag_log ; /* log10 of the flux */ + char Flag_parallel ; /* set neutron state back after detection (parallel components) */ + char Flag_Binary_List ; + char Flag_capture ; /* lambda monitor with lambda/lambda(2200m/s = 1.7985 Angs) weightening */ + int Flag_signal ; /* 0:monitor p, else monitor a mean value */ + + long Coord_Number ; /* total number of variables to monitor, plus intensity (0) */ + long Buffer_Block ; /* Buffer size for list or auto limits */ + long Neutron_Counter ; /* event counter, simulation total counts is mcget_ncount() */ + long Buffer_Counter ; /* index in Buffer size (for realloc) */ + long Buffer_Size ; + int Coord_Type[MONnD_COORD_NMAX]; /* type of variable */ + char Coord_Label[MONnD_COORD_NMAX][30]; /* label of variable */ + char Coord_Var[MONnD_COORD_NMAX][30]; /* short id of variable */ + long Coord_Bin[MONnD_COORD_NMAX]; /* bins of variable array */ + double Coord_Min[MONnD_COORD_NMAX]; + double Coord_Max[MONnD_COORD_NMAX]; + char Monitor_Label[MONnD_COORD_NMAX*30]; /* Label for monitor */ + char Mon_File[128]; /* output file name */ + + double cx,cy,cz; + double cvx, cvy, cvz; + double csx, csy, csz; + double cs1, cs2, ct, cp; + double He3_pressure; + char Flag_UsePreMonitor ; /* use a previously stored neutron parameter set */ + char UserName1[128]; + char UserName2[128]; + double UserVariable1; + double UserVariable2; + double Intermediate; + double IntermediateCnts; + char option[1024]; + + double Nsum; + double psum, p2sum; + double **Mon2D_N; + double **Mon2D_p; + double **Mon2D_p2; + double *Mon2D_Buffer; + + double mxmin,mxmax,mymin,mymax,mzmin,mzmax; + + char compcurname[128]; + + } MonitornD_Variables_type; + +/* monitor_nd-lib function prototypes */ +/* ========================================================================= */ + +void Monitor_nD_Init(MonitornD_Defines_type *, MonitornD_Variables_type *, MCNUM, MCNUM, MCNUM, MCNUM, MCNUM, MCNUM, MCNUM, MCNUM, MCNUM); +double Monitor_nD_Trace(MonitornD_Defines_type *, MonitornD_Variables_type *); +void Monitor_nD_Save(MonitornD_Defines_type *, MonitornD_Variables_type *); +void Monitor_nD_Finally(MonitornD_Defines_type *, MonitornD_Variables_type *); +void Monitor_nD_McDisplay(MonitornD_Defines_type *, + MonitornD_Variables_type *); + +#endif + +/* end of monitor_nd-lib.h */ +/******************************************************************************* +* +* McStas, neutron ray-tracing package +* Copyright 1997-2002, All rights reserved +* Risoe National Laboratory, Roskilde, Denmark +* Institut Laue Langevin, Grenoble, France +* +* Library: share/monitor_nd-lib.c +* +* %Identification +* Written by: EF +* Date: Aug 28, 2002 +* Origin: ILL +* Release: McStas 1.6 +* Version: 1.1 +* +* This file is to be imported by the monitor_nd related components +* It handles some shared functions. Embedded within instrument in runtime mode. +* Variable names have prefix 'mc_mn_' for 'McStas Monitor' to avoid conflicts +* +* Usage: within SHARE +* %include "monitor_nd-lib" +* +* $Id$ +* +* $Log$ +* Revision 1.1 2007/01/30 03:19:43 koennecke +* - Fixed state monitor eclipse commit problems. Siiiiiiiggggghhhhhh! +* +* Revision 1.15 2004/02/26 12:55:41 farhi +* Handles 0d monitor outputs for bins=0, and limits are restrictive (i.e. neutron must be within all limits to be stored in monitor) +* +* Revision 1.14 2004/02/04 18:01:12 farhi +* Use hdiv=theta and vdiv=phi for banana. +* +* Revision 1.13 2003/08/26 12:33:27 farhi +* Corrected computation of angle PHI (was projected on vertical plane) +* +* Revision 1.12 2003/04/15 16:01:28 farhi +* incoming/outgoing syntax mismatch correction +* +* Revision 1.11 2003/04/15 15:45:56 farhi +* outgoing time is default (vs. incoming) +* +* Revision 1.10 2003/04/09 15:49:25 farhi +* corrected bug when no signal and auto limits requested +* +* Revision 1.9 2003/02/18 09:11:36 farhi +* Corrected binary format for lists +* +* Revision 1.1 2002/08/28 11:39:00 ef +* Initial revision extracted from lib/monitors/Monitor_nD.comp +*******************************************************************************/ + +#ifndef MONITOR_ND_LIB_H +#error McStas : please import this library with %include "monitor_nd-lib" +#endif + +/* ========================================================================= */ +/* ADD: E.Farhi, Aug 6th, 2001: Monitor_nD section */ +/* this routine is used to parse options */ +/* ========================================================================= */ + +void Monitor_nD_Init(MonitornD_Defines_type *mc_mn_DEFS, + MonitornD_Variables_type *mc_mn_Vars, + MCNUM mc_mn_xwidth, + MCNUM mc_mn_yheight, + MCNUM mc_mn_zthick, + MCNUM mc_mn_xmin, + MCNUM mc_mn_xmax, + MCNUM mc_mn_ymin, + MCNUM mc_mn_ymax, + MCNUM mc_mn_zmin, + MCNUM mc_mn_zmax) + { + long mc_mn_carg = 1; + char *mc_mn_option_copy, *mc_mn_token; + char mc_mn_Flag_New_token = 1; + char mc_mn_Flag_End = 1; + char mc_mn_Flag_All = 0; + char mc_mn_Flag_No = 0; + char mc_mn_Flag_abs = 0; + int mc_mn_Flag_auto = 0; /* -1: all, 1: the current variable */ + int mc_mn_Set_Vars_Coord_Type; + char mc_mn_Set_Vars_Coord_Label[30]; + char mc_mn_Set_Vars_Coord_Var[30]; + char mc_mn_Short_Label[MONnD_COORD_NMAX][30]; + int mc_mn_Set_Coord_Mode; + long mc_mn_i=0, mc_mn_j=0; + double mc_mn_lmin, mc_mn_lmax, mc_mn_XY; + long mc_mn_t; + + + mc_mn_t = (long)time(NULL); + + mc_mn_DEFS->COORD_NONE =0; + mc_mn_DEFS->COORD_X =1; + mc_mn_DEFS->COORD_Y =2; + mc_mn_DEFS->COORD_Z =3; + mc_mn_DEFS->COORD_VX =4; + mc_mn_DEFS->COORD_VY =5; + mc_mn_DEFS->COORD_VZ =6; + mc_mn_DEFS->COORD_T =7; + mc_mn_DEFS->COORD_P =8; + mc_mn_DEFS->COORD_SX =9; + mc_mn_DEFS->COORD_SY =10; + mc_mn_DEFS->COORD_SZ =11; + mc_mn_DEFS->COORD_KX =12; + mc_mn_DEFS->COORD_KY =13; + mc_mn_DEFS->COORD_KZ =14; + mc_mn_DEFS->COORD_K =15; + mc_mn_DEFS->COORD_V =16; + mc_mn_DEFS->COORD_ENERGY =17; + mc_mn_DEFS->COORD_LAMBDA =18; + mc_mn_DEFS->COORD_RADIUS =19; + mc_mn_DEFS->COORD_HDIV =20; + mc_mn_DEFS->COORD_VDIV =21; + mc_mn_DEFS->COORD_ANGLE =22; + mc_mn_DEFS->COORD_NCOUNT =23; + mc_mn_DEFS->COORD_THETA =24; + mc_mn_DEFS->COORD_PHI =25; + mc_mn_DEFS->COORD_USER1 =26; + mc_mn_DEFS->COORD_USER2 =27; + +/* mc_mn_token modifiers */ + mc_mn_DEFS->COORD_VAR =0; /* next mc_mn_token should be a variable or normal option */ + mc_mn_DEFS->COORD_MIN =1; /* next mc_mn_token is a min value */ + mc_mn_DEFS->COORD_MAX =2; /* next mc_mn_token is a max value */ + mc_mn_DEFS->COORD_DIM =3; /* next mc_mn_token is a bin value */ + mc_mn_DEFS->COORD_FIL =4; /* next mc_mn_token is a filename */ + mc_mn_DEFS->COORD_EVNT =5; /* next mc_mn_token is a buffer size value */ + mc_mn_DEFS->COORD_3HE =6; /* next mc_mn_token is a 3He pressure value */ + mc_mn_DEFS->COORD_INTERM =7; /* next mc_mn_token is an intermediate save value (%) */ + mc_mn_DEFS->COORD_LOG =32; /* next variable will be in log scale */ + mc_mn_DEFS->COORD_ABS =64; /* next variable will be in abs scale */ + mc_mn_DEFS->COORD_SIGNAL =128; /* next variable will be the signal var */ + mc_mn_DEFS->COORD_AUTO =256; /* set auto limits */ + + strcpy(mc_mn_DEFS->TOKEN_DEL, " =,;[](){}:"); /* mc_mn_token separators */ + + mc_mn_DEFS->SHAPE_SQUARE =0; /* shape of the monitor */ + mc_mn_DEFS->SHAPE_DISK =1; + mc_mn_DEFS->SHAPE_SPHERE =2; + mc_mn_DEFS->SHAPE_CYLIND =3; + mc_mn_DEFS->SHAPE_BANANA =4; + mc_mn_DEFS->SHAPE_BOX =5; + + mc_mn_Vars->Sphere_Radius = 0; + mc_mn_Vars->Cylinder_Height = 0; + mc_mn_Vars->Flag_With_Borders = 0; /* 2 means xy borders too */ + mc_mn_Vars->Flag_List = 0; /* 1 store 1 buffer, 2 is list all */ + mc_mn_Vars->Flag_Multiple = 0; /* 1 when n1D, 0 for 2D */ + mc_mn_Vars->Flag_Verbose = 0; + mc_mn_Vars->Flag_Shape = mc_mn_DEFS->SHAPE_SQUARE; + mc_mn_Vars->Flag_Auto_Limits = 0; /* get limits from first Buffer */ + mc_mn_Vars->Flag_Absorb = 0; /* monitor is also a slit */ + mc_mn_Vars->Flag_per_cm2 = 0; /* flux is per cm2 */ + mc_mn_Vars->Flag_log = 0; /* log10 of the flux */ + mc_mn_Vars->Flag_parallel = 0; /* set neutron state back after detection (parallel components) */ + mc_mn_Vars->Flag_Binary_List = 0; /* save list as a binary file (smaller) */ + mc_mn_Vars->Coord_Number = 0; /* total number of variables to monitor, plus intensity (0) */ + mc_mn_Vars->Buffer_Block = 10000; /* Buffer size for list or auto limits */ + mc_mn_Vars->Neutron_Counter = 0; /* event counter, simulation total counts is mcget_ncount() */ + mc_mn_Vars->Buffer_Counter = 0; /* mc_mn_index in Buffer size (for realloc) */ + mc_mn_Vars->Buffer_Size = 0; + mc_mn_Vars->UserVariable1 = 0; + mc_mn_Vars->UserVariable2 = 0; + mc_mn_Vars->He3_pressure = 0; + mc_mn_Vars->IntermediateCnts = 0; + mc_mn_Vars->Flag_capture = 0; + mc_mn_Vars->Flag_signal = mc_mn_DEFS->COORD_P; + + mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_NONE; + mc_mn_Set_Coord_Mode = mc_mn_DEFS->COORD_VAR; + + /* handle size parameters */ + /* normal use is with xwidth, yheight, zthick */ + /* if xmin,xmax,ymin,ymax,zmin,zmax are non 0, use them */ + if (fabs(mc_mn_xmin-mc_mn_xmax) == 0) + { mc_mn_Vars->mxmin = -fabs(mc_mn_xwidth)/2; mc_mn_Vars->mxmax = fabs(mc_mn_xwidth)/2; } + else + { if (mc_mn_xmin < mc_mn_xmax) {mc_mn_Vars->mxmin = mc_mn_xmin; mc_mn_Vars->mxmax = mc_mn_xmax;} + else {mc_mn_Vars->mxmin = mc_mn_xmax; mc_mn_Vars->mxmax = mc_mn_xmin;} + } + if (fabs(mc_mn_ymin-mc_mn_ymax) == 0) + { mc_mn_Vars->mymin = -fabs(mc_mn_yheight)/2; mc_mn_Vars->mymax = fabs(mc_mn_yheight)/2; } + else + { if (mc_mn_ymin < mc_mn_ymax) {mc_mn_Vars->mymin = mc_mn_ymin; mc_mn_Vars->mymax = mc_mn_ymax;} + else {mc_mn_Vars->mymin = mc_mn_ymax; mc_mn_Vars->mymax = mc_mn_ymin;} + } + if (fabs(mc_mn_zmin-mc_mn_zmax) == 0) + { mc_mn_Vars->mzmin = -fabs(mc_mn_zthick)/2; mc_mn_Vars->mzmax = fabs(mc_mn_zthick)/2; } + else + { if (mc_mn_zmin < mc_mn_zmax) {mc_mn_Vars->mzmin = mc_mn_zmin; mc_mn_Vars->mzmax = mc_mn_zmax; } + else {mc_mn_Vars->mzmin = mc_mn_zmax; mc_mn_Vars->mzmax = mc_mn_zmin; } + } + + if (fabs(mc_mn_Vars->mzmax-mc_mn_Vars->mzmin) == 0) + mc_mn_Vars->Flag_Shape = mc_mn_DEFS->SHAPE_SQUARE; + else + mc_mn_Vars->Flag_Shape = mc_mn_DEFS->SHAPE_BOX; + + /* parse option string */ + + mc_mn_option_copy = (char*)malloc(strlen(mc_mn_Vars->option)+1); + if (mc_mn_option_copy == NULL) + { + fprintf(stderr,"Monitor_nD: %s cannot allocate mc_mn_option_copy (%li). Fatal.\n", mc_mn_Vars->compcurname, strlen(mc_mn_Vars->option)); + exit(-1); + } + + if (strlen(mc_mn_Vars->option)) + { + mc_mn_Flag_End = 0; + strcpy(mc_mn_option_copy, mc_mn_Vars->option); + } + + if (strstr(mc_mn_Vars->option, "cm2") || strstr(mc_mn_Vars->option, "cm^2")) mc_mn_Vars->Flag_per_cm2 = 1; + + if (strstr(mc_mn_Vars->option, "binary") || strstr(mc_mn_Vars->option, "float")) + mc_mn_Vars->Flag_Binary_List = 1; + if (strstr(mc_mn_Vars->option, "double")) + mc_mn_Vars->Flag_Binary_List = 2; + + if (mc_mn_Vars->Flag_per_cm2) strncpy(mc_mn_Vars->Coord_Label[0],"Intensity [n/cm^2/s]",30); + else strncpy(mc_mn_Vars->Coord_Label[0],"Intensity [n/s]",30); + strncpy(mc_mn_Vars->Coord_Var[0],"p",30); + mc_mn_Vars->Coord_Type[0] = mc_mn_DEFS->COORD_P; + mc_mn_Vars->Coord_Bin[0] = 1; + mc_mn_Vars->Coord_Min[0] = 0; + mc_mn_Vars->Coord_Max[0] = FLT_MAX; + + /* default file name is comp name+dateID */ + sprintf(mc_mn_Vars->Mon_File, "%s_%li", mc_mn_Vars->compcurname, mc_mn_t); + + mc_mn_carg = 1; + while((mc_mn_Flag_End == 0) && (mc_mn_carg < 128)) + { + if (mc_mn_Flag_New_token) /* to get the previous mc_mn_token sometimes */ + { + if (mc_mn_carg == 1) mc_mn_token=(char *)strtok(mc_mn_option_copy,mc_mn_DEFS->TOKEN_DEL); + else mc_mn_token=(char *)strtok(NULL,mc_mn_DEFS->TOKEN_DEL); + if (mc_mn_token == NULL) mc_mn_Flag_End=1; + } + mc_mn_Flag_New_token = 1; + if ((mc_mn_token != NULL) && (strlen(mc_mn_token) != 0)) + { + /* first handle option values from preceeding keyword mc_mn_token detected */ + if (mc_mn_Set_Coord_Mode == mc_mn_DEFS->COORD_MAX) + { + if (!mc_mn_Flag_All) + mc_mn_Vars->Coord_Max[mc_mn_Vars->Coord_Number] = atof(mc_mn_token); + else + for (mc_mn_i = 0; mc_mn_i <= mc_mn_Vars->Coord_Number; mc_mn_Vars->Coord_Max[mc_mn_i++] = atof(mc_mn_token)); + mc_mn_Set_Coord_Mode = mc_mn_DEFS->COORD_VAR; mc_mn_Flag_All = 0; + } + if (mc_mn_Set_Coord_Mode == mc_mn_DEFS->COORD_MIN) + { + if (!mc_mn_Flag_All) + mc_mn_Vars->Coord_Min[mc_mn_Vars->Coord_Number] = atof(mc_mn_token); + else + for (mc_mn_i = 0; mc_mn_i <= mc_mn_Vars->Coord_Number; mc_mn_Vars->Coord_Min[mc_mn_i++] = atof(mc_mn_token)); + mc_mn_Set_Coord_Mode = mc_mn_DEFS->COORD_MAX; + } + if (mc_mn_Set_Coord_Mode == mc_mn_DEFS->COORD_DIM) + { + if (!mc_mn_Flag_All) + mc_mn_Vars->Coord_Bin[mc_mn_Vars->Coord_Number] = atoi(mc_mn_token); + else + for (mc_mn_i = 0; mc_mn_i <= mc_mn_Vars->Coord_Number; mc_mn_Vars->Coord_Bin[mc_mn_i++] = atoi(mc_mn_token)); + mc_mn_Set_Coord_Mode = mc_mn_DEFS->COORD_VAR; mc_mn_Flag_All = 0; + } + if (mc_mn_Set_Coord_Mode == mc_mn_DEFS->COORD_FIL) + { + if (!mc_mn_Flag_No) strncpy(mc_mn_Vars->Mon_File,mc_mn_token,128); + else { strcpy(mc_mn_Vars->Mon_File,""); mc_mn_Vars->Coord_Number = 0; mc_mn_Flag_End = 1;} + mc_mn_Set_Coord_Mode = mc_mn_DEFS->COORD_VAR; + } + if (mc_mn_Set_Coord_Mode == mc_mn_DEFS->COORD_EVNT) + { + if (!strcmp(mc_mn_token, "all") || mc_mn_Flag_All) mc_mn_Vars->Flag_List = 2; + else { mc_mn_i = atoi(mc_mn_token); if (mc_mn_i) mc_mn_Vars->Buffer_Block = mc_mn_i; + mc_mn_Vars->Flag_List = 1; } + mc_mn_Set_Coord_Mode = mc_mn_DEFS->COORD_VAR; mc_mn_Flag_All = 0; + } + if (mc_mn_Set_Coord_Mode == mc_mn_DEFS->COORD_3HE) + { + mc_mn_Vars->He3_pressure = atof(mc_mn_token); + mc_mn_Set_Coord_Mode = mc_mn_DEFS->COORD_VAR; mc_mn_Flag_All = 0; + } + if (mc_mn_Set_Coord_Mode == mc_mn_DEFS->COORD_INTERM) + { + mc_mn_Vars->Intermediate = atof(mc_mn_token); + mc_mn_Set_Coord_Mode = mc_mn_DEFS->COORD_VAR; mc_mn_Flag_All = 0; + } + + /* now look for general option keywords */ + if (!strcmp(mc_mn_token, "borders")) mc_mn_Vars->Flag_With_Borders = 1; + if (!strcmp(mc_mn_token, "verbose")) mc_mn_Vars->Flag_Verbose = 1; + if (!strcmp(mc_mn_token, "log")) mc_mn_Vars->Flag_log = 1; + if (!strcmp(mc_mn_token, "abs")) mc_mn_Flag_abs = 1; + if (!strcmp(mc_mn_token, "multiple")) mc_mn_Vars->Flag_Multiple = 1; + if (!strcmp(mc_mn_token, "list")) + { mc_mn_Vars->Flag_List = 1; + mc_mn_Set_Coord_Mode = mc_mn_DEFS->COORD_EVNT; } + + if (!strcmp(mc_mn_token, "limits") || !strcmp(mc_mn_token, "min")) mc_mn_Set_Coord_Mode = mc_mn_DEFS->COORD_MIN; + if (!strcmp(mc_mn_token, "slit") || !strcmp(mc_mn_token, "absorb")) + { mc_mn_Vars->Flag_Absorb = 1; } + if (!strcmp(mc_mn_token, "max")) mc_mn_Set_Coord_Mode = mc_mn_DEFS->COORD_MAX; + if (!strcmp(mc_mn_token, "bins")) mc_mn_Set_Coord_Mode = mc_mn_DEFS->COORD_DIM; + if (!strcmp(mc_mn_token, "file")) + { mc_mn_Set_Coord_Mode = mc_mn_DEFS->COORD_FIL; + if (mc_mn_Flag_No) { strcpy(mc_mn_Vars->Mon_File,""); mc_mn_Vars->Coord_Number = 0; mc_mn_Flag_End = 1;}} + if (!strcmp(mc_mn_token, "unactivate")) { mc_mn_Flag_End = 1; mc_mn_Vars->Coord_Number = 0; } + if (!strcmp(mc_mn_token, "all")) mc_mn_Flag_All = 1; + if (!strcmp(mc_mn_token, "sphere")) mc_mn_Vars->Flag_Shape = mc_mn_DEFS->SHAPE_SPHERE; + if (!strcmp(mc_mn_token, "cylinder")) mc_mn_Vars->Flag_Shape = mc_mn_DEFS->SHAPE_CYLIND; + if (!strcmp(mc_mn_token, "banana")) mc_mn_Vars->Flag_Shape = mc_mn_DEFS->SHAPE_BANANA; + if (!strcmp(mc_mn_token, "square")) mc_mn_Vars->Flag_Shape = mc_mn_DEFS->SHAPE_SQUARE; + if (!strcmp(mc_mn_token, "disk")) mc_mn_Vars->Flag_Shape = mc_mn_DEFS->SHAPE_DISK; + if (!strcmp(mc_mn_token, "box")) mc_mn_Vars->Flag_Shape = mc_mn_DEFS->SHAPE_BOX; + if (!strcmp(mc_mn_token, "parallel")) mc_mn_Vars->Flag_parallel = 1; + if (!strcmp(mc_mn_token, "capture")) mc_mn_Vars->Flag_capture = 1; + if (!strcmp(mc_mn_token, "auto") && (mc_mn_Flag_auto != -1)) + { mc_mn_Vars->Flag_Auto_Limits = 1; + if (mc_mn_Flag_All) mc_mn_Flag_auto = -1; + else mc_mn_Flag_auto = 1; + } + if (!strcmp(mc_mn_token, "premonitor")) + mc_mn_Vars->Flag_UsePreMonitor = 1; + if (!strcmp(mc_mn_token, "3He_pressure")) + mc_mn_Vars->He3_pressure = 3; + if (!strcmp(mc_mn_token, "intermediate")) + { mc_mn_Set_Coord_Mode = mc_mn_DEFS->COORD_INTERM; + mc_mn_Vars->Intermediate = 5; } + if (!strcmp(mc_mn_token, "no") || !strcmp(mc_mn_token, "not")) mc_mn_Flag_No = 1; + if (!strcmp(mc_mn_token, "signal")) mc_mn_Set_Coord_Mode = mc_mn_DEFS->COORD_SIGNAL; + + /* now look for variable names to monitor */ + mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_NONE; mc_mn_lmin = 0; mc_mn_lmax = 0; + + if (!strcmp(mc_mn_token, "x")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_X; strcpy(mc_mn_Set_Vars_Coord_Label,"x [m]"); strcpy(mc_mn_Set_Vars_Coord_Var,"x"); mc_mn_lmin = mc_mn_Vars->mxmin; mc_mn_lmax = mc_mn_Vars->mxmax; } + if (!strcmp(mc_mn_token, "y")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_Y; strcpy(mc_mn_Set_Vars_Coord_Label,"y [m]"); strcpy(mc_mn_Set_Vars_Coord_Var,"y"); mc_mn_lmin = mc_mn_Vars->mymin; mc_mn_lmax = mc_mn_Vars->mymax; } + if (!strcmp(mc_mn_token, "z")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_Z; strcpy(mc_mn_Set_Vars_Coord_Label,"z [m]"); strcpy(mc_mn_Set_Vars_Coord_Var,"z"); mc_mn_lmin = mc_mn_Vars->mzmin; mc_mn_lmax = mc_mn_Vars->mzmax; } + if (!strcmp(mc_mn_token, "k") || !strcmp(mc_mn_token, "wavevector")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_K; strcpy(mc_mn_Set_Vars_Coord_Label,"|k| [Angs-1]"); strcpy(mc_mn_Set_Vars_Coord_Var,"k"); mc_mn_lmin = 0; mc_mn_lmax = 10; } + if (!strcmp(mc_mn_token, "v")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_V; strcpy(mc_mn_Set_Vars_Coord_Label,"Velocity [m/s]"); strcpy(mc_mn_Set_Vars_Coord_Var,"v"); mc_mn_lmin = 0; mc_mn_lmax = 10000; } + if (!strcmp(mc_mn_token, "t") || !strcmp(mc_mn_token, "time")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_T; strcpy(mc_mn_Set_Vars_Coord_Label,"TOF [s]"); strcpy(mc_mn_Set_Vars_Coord_Var,"t"); mc_mn_lmin = 0; mc_mn_lmax = .1; } + if ((!strcmp(mc_mn_token, "p") || !strcmp(mc_mn_token, "intensity") || !strcmp(mc_mn_token, "flux"))) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_P; + if (mc_mn_Vars->Flag_per_cm2) strcpy(mc_mn_Set_Vars_Coord_Label,"Intensity [n/cm^2/s]"); + else strcpy(mc_mn_Set_Vars_Coord_Label,"Intensity [n/s]"); + strcpy(mc_mn_Set_Vars_Coord_Var,"I"); + mc_mn_lmin = 0; mc_mn_lmax = FLT_MAX; } + + if (!strcmp(mc_mn_token, "vx")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_VX; strcpy(mc_mn_Set_Vars_Coord_Label,"vx [m/s]"); strcpy(mc_mn_Set_Vars_Coord_Var,"vx"); mc_mn_lmin = -1000; mc_mn_lmax = 1000; } + if (!strcmp(mc_mn_token, "vy")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_VY; strcpy(mc_mn_Set_Vars_Coord_Label,"vy [m/s]"); strcpy(mc_mn_Set_Vars_Coord_Var,"vy"); mc_mn_lmin = -1000; mc_mn_lmax = 1000; } + if (!strcmp(mc_mn_token, "vz")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_VZ; strcpy(mc_mn_Set_Vars_Coord_Label,"vz [m/s]"); strcpy(mc_mn_Set_Vars_Coord_Var,"vz"); mc_mn_lmin = -10000; mc_mn_lmax = 10000; } + if (!strcmp(mc_mn_token, "kx")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_KX; strcpy(mc_mn_Set_Vars_Coord_Label,"kx [Angs-1]"); strcpy(mc_mn_Set_Vars_Coord_Var,"kx"); mc_mn_lmin = -1; mc_mn_lmax = 1; } + if (!strcmp(mc_mn_token, "ky")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_KY; strcpy(mc_mn_Set_Vars_Coord_Label,"ky [Angs-1]"); strcpy(mc_mn_Set_Vars_Coord_Var,"ky"); mc_mn_lmin = -1; mc_mn_lmax = 1; } + if (!strcmp(mc_mn_token, "kz")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_KZ; strcpy(mc_mn_Set_Vars_Coord_Label,"kz [Angs-1]"); strcpy(mc_mn_Set_Vars_Coord_Var,"kz"); mc_mn_lmin = -10; mc_mn_lmax = 10; } + if (!strcmp(mc_mn_token, "sx")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_SX; strcpy(mc_mn_Set_Vars_Coord_Label,"sx [1]"); strcpy(mc_mn_Set_Vars_Coord_Var,"sx"); mc_mn_lmin = -1; mc_mn_lmax = 1; } + if (!strcmp(mc_mn_token, "sy")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_SY; strcpy(mc_mn_Set_Vars_Coord_Label,"sy [1]"); strcpy(mc_mn_Set_Vars_Coord_Var,"sy"); mc_mn_lmin = -1; mc_mn_lmax = 1; } + if (!strcmp(mc_mn_token, "sz")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_SZ; strcpy(mc_mn_Set_Vars_Coord_Label,"sz [1]"); strcpy(mc_mn_Set_Vars_Coord_Var,"sz"); mc_mn_lmin = -1; mc_mn_lmax = 1; } + + if (!strcmp(mc_mn_token, "energy") || !strcmp(mc_mn_token, "omega")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_ENERGY; strcpy(mc_mn_Set_Vars_Coord_Label,"Energy [meV]"); strcpy(mc_mn_Set_Vars_Coord_Var,"E"); mc_mn_lmin = 0; mc_mn_lmax = 100; } + if (!strcmp(mc_mn_token, "lambda") || !strcmp(mc_mn_token, "wavelength")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_LAMBDA; strcpy(mc_mn_Set_Vars_Coord_Label,"Wavelength [Angs]"); strcpy(mc_mn_Set_Vars_Coord_Var,"L"); mc_mn_lmin = 0; mc_mn_lmax = 100; } + if (!strcmp(mc_mn_token, "radius")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_RADIUS; strcpy(mc_mn_Set_Vars_Coord_Label,"Radius [m]"); strcpy(mc_mn_Set_Vars_Coord_Var,"R"); mc_mn_lmin = 0; mc_mn_lmax = mc_mn_xmax; } + if (!strcmp(mc_mn_token, "angle")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_ANGLE; strcpy(mc_mn_Set_Vars_Coord_Label,"Angle [deg]"); strcpy(mc_mn_Set_Vars_Coord_Var,"A"); mc_mn_lmin = -5; mc_mn_lmax = 5; } + if (!strcmp(mc_mn_token, "hdiv")|| !strcmp(mc_mn_token, "divergence") || !strcmp(mc_mn_token, "xdiv") || !strcmp(mc_mn_token, "dx")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_HDIV; strcpy(mc_mn_Set_Vars_Coord_Label,"Hor. Divergence [deg]"); strcpy(mc_mn_Set_Vars_Coord_Var,"HD"); mc_mn_lmin = -5; mc_mn_lmax = 5; } + if (!strcmp(mc_mn_token, "vdiv") || !strcmp(mc_mn_token, "ydiv") || !strcmp(mc_mn_token, "dy")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_VDIV; strcpy(mc_mn_Set_Vars_Coord_Label,"Vert. Divergence [deg]"); strcpy(mc_mn_Set_Vars_Coord_Var,"VD"); mc_mn_lmin = -5; mc_mn_lmax = 5; } + if (!strcmp(mc_mn_token, "theta") || !strcmp(mc_mn_token, "longitude")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_THETA; strcpy(mc_mn_Set_Vars_Coord_Label,"Longitude [deg]"); strcpy(mc_mn_Set_Vars_Coord_Var,"th"); mc_mn_lmin = -180; mc_mn_lmax = 180; } + if (!strcmp(mc_mn_token, "phi") || !strcmp(mc_mn_token, "lattitude")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_PHI; strcpy(mc_mn_Set_Vars_Coord_Label,"Lattitude [deg]"); strcpy(mc_mn_Set_Vars_Coord_Var,"ph"); mc_mn_lmin = -180; mc_mn_lmax = 180; } + if (!strcmp(mc_mn_token, "ncounts")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_NCOUNT; strcpy(mc_mn_Set_Vars_Coord_Label,"Neutrons [1]"); strcpy(mc_mn_Set_Vars_Coord_Var,"N"); mc_mn_lmin = 0; mc_mn_lmax = 1e10; } + if (!strcmp(mc_mn_token, "user") || !strcmp(mc_mn_token, "user1")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_USER1; strncpy(mc_mn_Set_Vars_Coord_Label,mc_mn_Vars->UserName1,32); strcpy(mc_mn_Set_Vars_Coord_Var,"U1"); mc_mn_lmin = -1e10; mc_mn_lmax = 1e10; } + if (!strcmp(mc_mn_token, "user2")) + { mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_USER2; strncpy(mc_mn_Set_Vars_Coord_Label,mc_mn_Vars->UserName2,32); strcpy(mc_mn_Set_Vars_Coord_Var,"U2"); mc_mn_lmin = -1e10; mc_mn_lmax = 1e10; } + + /* now stores variable keywords detected, if any */ + if (mc_mn_Set_Vars_Coord_Type != mc_mn_DEFS->COORD_NONE) + { + int mc_mn_Coord_Number = mc_mn_Vars->Coord_Number; + if (mc_mn_Vars->Flag_log) { mc_mn_Set_Vars_Coord_Type |= mc_mn_DEFS->COORD_LOG; mc_mn_Vars->Flag_log = 0; } + if (mc_mn_Flag_abs) { mc_mn_Set_Vars_Coord_Type |= mc_mn_DEFS->COORD_ABS; mc_mn_Flag_abs = 0; } + if (mc_mn_Flag_auto != 0) { mc_mn_Set_Vars_Coord_Type |= mc_mn_DEFS->COORD_AUTO; mc_mn_Flag_auto = 0; } + if (mc_mn_Set_Coord_Mode == mc_mn_DEFS->COORD_SIGNAL) + { + mc_mn_Coord_Number = 0; + mc_mn_Vars->Flag_signal = mc_mn_Set_Vars_Coord_Type; + } + else + { + if (mc_mn_Coord_Number < MONnD_COORD_NMAX) + { mc_mn_Coord_Number++; + mc_mn_Vars->Coord_Number = mc_mn_Coord_Number; } + else if (mc_mn_Vars->Flag_Verbose) printf("Monitor_nD: %s reached max number of variables (%i).\n", mc_mn_Vars->compcurname, MONnD_COORD_NMAX); + } + mc_mn_Vars->Coord_Type[mc_mn_Coord_Number] = mc_mn_Set_Vars_Coord_Type; + strncpy(mc_mn_Vars->Coord_Label[mc_mn_Coord_Number], mc_mn_Set_Vars_Coord_Label,30); + strncpy(mc_mn_Vars->Coord_Var[mc_mn_Coord_Number], mc_mn_Set_Vars_Coord_Var,30); + if (mc_mn_lmin > mc_mn_lmax) { mc_mn_XY = mc_mn_lmin; mc_mn_lmin=mc_mn_lmax; mc_mn_lmax = mc_mn_XY; } + mc_mn_Vars->Coord_Min[mc_mn_Coord_Number] = mc_mn_lmin; + mc_mn_Vars->Coord_Max[mc_mn_Coord_Number] = mc_mn_lmax; + if (mc_mn_Set_Coord_Mode != mc_mn_DEFS->COORD_SIGNAL) mc_mn_Vars->Coord_Bin[mc_mn_Coord_Number] = 20; + mc_mn_Set_Coord_Mode = mc_mn_DEFS->COORD_VAR; + mc_mn_Flag_All = 0; + mc_mn_Flag_No = 0; + } + mc_mn_carg++; + } /* end if mc_mn_token */ + } /* end while mc_mn_carg */ + free(mc_mn_option_copy); + if (mc_mn_carg == 128) printf("Monitor_nD: %s reached max number of mc_mn_tokens (%i). Skipping.\n", mc_mn_Vars->compcurname, 128); + + if ((mc_mn_Vars->Flag_Shape == mc_mn_DEFS->SHAPE_BOX) && (fabs(mc_mn_Vars->mzmax - mc_mn_Vars->mzmin) == 0)) mc_mn_Vars->Flag_Shape = mc_mn_DEFS->SHAPE_SQUARE; + + if (mc_mn_Vars->Flag_log == 1) mc_mn_Vars->Coord_Type[0] |= mc_mn_DEFS->COORD_LOG; + if (mc_mn_Vars->Coord_Number == 0) + { mc_mn_Vars->Flag_Auto_Limits=0; mc_mn_Vars->Flag_Multiple=0; mc_mn_Vars->Flag_List=0; } + + /* now setting Monitor Name from variable mc_mn_labels */ + strcpy(mc_mn_Vars->Monitor_Label,""); + for (mc_mn_i = 0; mc_mn_i <= mc_mn_Vars->Coord_Number; mc_mn_i++) + { + if (mc_mn_Flag_auto != 0) mc_mn_Vars->Coord_Type[mc_mn_i] |= mc_mn_DEFS->COORD_AUTO; + mc_mn_Set_Vars_Coord_Type = (mc_mn_Vars->Coord_Type[mc_mn_i] & 31); + if ((mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_THETA) + || (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_PHI) + || (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_X) + || (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_Y) + || (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_Z) + || (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_RADIUS)) + strcpy(mc_mn_Short_Label[mc_mn_i],"Position"); + else + if ((mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_VX) + || (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_VY) + || (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_VZ) + || (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_V)) + strcpy(mc_mn_Short_Label[mc_mn_i],"Velocity"); + else + if ((mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_KX) + || (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_KY) + || (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_KZ) + || (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_K)) + strcpy(mc_mn_Short_Label[mc_mn_i],"Wavevector"); + else + if ((mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_SX) + || (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_SY) + || (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_SZ)) + strcpy(mc_mn_Short_Label[mc_mn_i],"Spin"); + else + if ((mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_HDIV) + || (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_VDIV) + || (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_ANGLE)) + strcpy(mc_mn_Short_Label[mc_mn_i],"Divergence"); + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_ENERGY) + strcpy(mc_mn_Short_Label[mc_mn_i],"Energy"); + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_LAMBDA) + strcpy(mc_mn_Short_Label[mc_mn_i],"Wavelength"); + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_NCOUNT) + strcpy(mc_mn_Short_Label[mc_mn_i],"Neutron counts"); + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_T) + strcpy(mc_mn_Short_Label[mc_mn_i],"Time Of Flight"); + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_P) + strcpy(mc_mn_Short_Label[mc_mn_i],"Intensity"); + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_USER1) + strncpy(mc_mn_Short_Label[mc_mn_i],mc_mn_Vars->UserName1,32); + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_USER2) + strncpy(mc_mn_Short_Label[mc_mn_i],mc_mn_Vars->UserName2,32); + else + strcpy(mc_mn_Short_Label[mc_mn_i],"Unknown"); + + if (mc_mn_Vars->Coord_Type[mc_mn_i] & mc_mn_DEFS->COORD_ABS) + { strcat(mc_mn_Vars->Coord_Label[mc_mn_i]," (abs)"); } + + if (mc_mn_Vars->Coord_Type[mc_mn_i] & mc_mn_DEFS->COORD_LOG) + { strcat(mc_mn_Vars->Coord_Label[mc_mn_i]," (log)"); } + + strcat(mc_mn_Vars->Monitor_Label, " "); + strcat(mc_mn_Vars->Monitor_Label, mc_mn_Short_Label[mc_mn_i]); + } /* end for mc_mn_Short_Label */ + + strcat(mc_mn_Vars->Monitor_Label, " Monitor"); + if (mc_mn_Vars->Flag_Shape == mc_mn_DEFS->SHAPE_SQUARE) strcat(mc_mn_Vars->Monitor_Label, " (Square)"); + if (mc_mn_Vars->Flag_Shape == mc_mn_DEFS->SHAPE_DISK) strcat(mc_mn_Vars->Monitor_Label, " (Disk)"); + if (mc_mn_Vars->Flag_Shape == mc_mn_DEFS->SHAPE_SPHERE) strcat(mc_mn_Vars->Monitor_Label, " (Sphere)"); + if (mc_mn_Vars->Flag_Shape == mc_mn_DEFS->SHAPE_CYLIND) strcat(mc_mn_Vars->Monitor_Label, " (Cylinder)"); + if (mc_mn_Vars->Flag_Shape == mc_mn_DEFS->SHAPE_BANANA) strcat(mc_mn_Vars->Monitor_Label, " (Banana)"); + if (mc_mn_Vars->Flag_Shape == mc_mn_DEFS->SHAPE_BOX) strcat(mc_mn_Vars->Monitor_Label, " (Box)"); + if ((mc_mn_Vars->Flag_Shape == mc_mn_DEFS->SHAPE_CYLIND) || (mc_mn_Vars->Flag_Shape == mc_mn_DEFS->SHAPE_BANANA) || (mc_mn_Vars->Flag_Shape == mc_mn_DEFS->SHAPE_SPHERE) || (mc_mn_Vars->Flag_Shape == mc_mn_DEFS->SHAPE_BOX)) + { + if (strstr(mc_mn_Vars->option, "incoming")) + { + mc_mn_Vars->Flag_Shape = abs(mc_mn_Vars->Flag_Shape); + strcat(mc_mn_Vars->Monitor_Label, " [in]"); + } + else /* if strstr(mc_mn_Vars->option, "outgoing")) */ + { + mc_mn_Vars->Flag_Shape = -abs(mc_mn_Vars->Flag_Shape); + strcat(mc_mn_Vars->Monitor_Label, " [out]"); + } + } + if (mc_mn_Vars->Flag_UsePreMonitor == 1) + { + strcat(mc_mn_Vars->Monitor_Label, " at "); + strncat(mc_mn_Vars->Monitor_Label, mc_mn_Vars->UserName1,32); + } + if (mc_mn_Vars->Flag_log == 1) strcat(mc_mn_Vars->Monitor_Label, " [log] "); + + /* mc_mn_Vars->Coord_Number 0 : intensity or signal + * mc_mn_Vars->Coord_Number 1:n : detector variables */ + + /* now allocate memory to store variables in TRACE */ + if ((mc_mn_Vars->Coord_Number != 2) && !mc_mn_Vars->Flag_Multiple && !mc_mn_Vars->Flag_List) + { mc_mn_Vars->Flag_Multiple = 1; mc_mn_Vars->Flag_List = 0; } /* default is n1D */ + + /* list and auto limits case : mc_mn_Vars->Flag_List or mc_mn_Vars->Flag_Auto_Limits + * -> Buffer to flush and suppress after mc_mn_Vars->Flag_Auto_Limits + */ + if ((mc_mn_Vars->Flag_Auto_Limits || mc_mn_Vars->Flag_List) && mc_mn_Vars->Coord_Number) + { /* Dim : (mc_mn_Vars->Coord_Number+1)*mc_mn_Vars->Buffer_Block matrix (for p, dp) */ + mc_mn_Vars->Mon2D_Buffer = (double *)malloc((mc_mn_Vars->Coord_Number+1)*mc_mn_Vars->Buffer_Block*sizeof(double)); + if (mc_mn_Vars->Mon2D_Buffer == NULL) + { printf("Monitor_nD: %s cannot allocate mc_mn_Vars->Mon2D_Buffer (%li). No list and auto limits.\n", mc_mn_Vars->compcurname, mc_mn_Vars->Buffer_Block*(mc_mn_Vars->Coord_Number+1)*sizeof(double)); mc_mn_Vars->Flag_List = 0; mc_mn_Vars->Flag_Auto_Limits = 0; } + else + { + for (mc_mn_i=0; mc_mn_i < (mc_mn_Vars->Coord_Number+1)*mc_mn_Vars->Buffer_Block; mc_mn_Vars->Mon2D_Buffer[mc_mn_i++] = (double)0); + } + mc_mn_Vars->Buffer_Size = mc_mn_Vars->Buffer_Block; + } + + /* 1D and n1D case : mc_mn_Vars->Flag_Multiple */ + if (mc_mn_Vars->Flag_Multiple && mc_mn_Vars->Coord_Number) + { /* Dim : mc_mn_Vars->Coord_Number*mc_mn_Vars->Coord_Bin[mc_mn_i] vectors */ + mc_mn_Vars->Mon2D_N = (double **)malloc((mc_mn_Vars->Coord_Number)*sizeof(double *)); + mc_mn_Vars->Mon2D_p = (double **)malloc((mc_mn_Vars->Coord_Number)*sizeof(double *)); + mc_mn_Vars->Mon2D_p2 = (double **)malloc((mc_mn_Vars->Coord_Number)*sizeof(double *)); + if ((mc_mn_Vars->Mon2D_N == NULL) || (mc_mn_Vars->Mon2D_p == NULL) || (mc_mn_Vars->Mon2D_p2 == NULL)) + { fprintf(stderr,"Monitor_nD: %s n1D cannot allocate mc_mn_Vars->Mon2D_N/p/2p (%li). Fatal.\n", mc_mn_Vars->compcurname, (mc_mn_Vars->Coord_Number)*sizeof(double *)); exit(-1); } + for (mc_mn_i= 1; mc_mn_i <= mc_mn_Vars->Coord_Number; mc_mn_i++) + { + mc_mn_Vars->Mon2D_N[mc_mn_i-1] = (double *)malloc(mc_mn_Vars->Coord_Bin[mc_mn_i]*sizeof(double)); + mc_mn_Vars->Mon2D_p[mc_mn_i-1] = (double *)malloc(mc_mn_Vars->Coord_Bin[mc_mn_i]*sizeof(double)); + mc_mn_Vars->Mon2D_p2[mc_mn_i-1] = (double *)malloc(mc_mn_Vars->Coord_Bin[mc_mn_i]*sizeof(double)); + if ((mc_mn_Vars->Mon2D_N == NULL) || (mc_mn_Vars->Mon2D_p == NULL) || (mc_mn_Vars->Mon2D_p2 == NULL)) + { fprintf(stderr,"Monitor_nD: %s n1D cannot allocate %s mc_mn_Vars->Mon2D_N/p/2p[%li] (%li). Fatal.\n", mc_mn_Vars->compcurname, mc_mn_Vars->Coord_Var[mc_mn_i], mc_mn_i, (mc_mn_Vars->Coord_Bin[mc_mn_i])*sizeof(double *)); exit(-1); } + else + { + for (mc_mn_j=0; mc_mn_j < mc_mn_Vars->Coord_Bin[mc_mn_i]; mc_mn_j++ ) + { mc_mn_Vars->Mon2D_N[mc_mn_i-1][mc_mn_j] = (double)0; mc_mn_Vars->Mon2D_p[mc_mn_i-1][mc_mn_j] = (double)0; mc_mn_Vars->Mon2D_p2[mc_mn_i-1][mc_mn_j] = (double)0; } + } + } + } + else /* 2D case : mc_mn_Vars->Coord_Number==2 and !mc_mn_Vars->Flag_Multiple and !mc_mn_Vars->Flag_List */ + if ((mc_mn_Vars->Coord_Number == 2) && !mc_mn_Vars->Flag_Multiple) + { /* Dim : mc_mn_Vars->Coord_Bin[1]*mc_mn_Vars->Coord_Bin[2] matrix */ + mc_mn_Vars->Mon2D_N = (double **)malloc((mc_mn_Vars->Coord_Bin[1])*sizeof(double *)); + mc_mn_Vars->Mon2D_p = (double **)malloc((mc_mn_Vars->Coord_Bin[1])*sizeof(double *)); + mc_mn_Vars->Mon2D_p2 = (double **)malloc((mc_mn_Vars->Coord_Bin[1])*sizeof(double *)); + if ((mc_mn_Vars->Mon2D_N == NULL) || (mc_mn_Vars->Mon2D_p == NULL) || (mc_mn_Vars->Mon2D_p2 == NULL)) + { fprintf(stderr,"Monitor_nD: %s 2D cannot allocate %s mc_mn_Vars->Mon2D_N/p/2p (%li). Fatal.\n", mc_mn_Vars->compcurname, mc_mn_Vars->Coord_Var[1], (mc_mn_Vars->Coord_Bin[1])*sizeof(double *)); exit(-1); } + for (mc_mn_i= 0; mc_mn_i < mc_mn_Vars->Coord_Bin[1]; mc_mn_i++) + { + mc_mn_Vars->Mon2D_N[mc_mn_i] = (double *)malloc(mc_mn_Vars->Coord_Bin[2]*sizeof(double)); + mc_mn_Vars->Mon2D_p[mc_mn_i] = (double *)malloc(mc_mn_Vars->Coord_Bin[2]*sizeof(double)); + mc_mn_Vars->Mon2D_p2[mc_mn_i] = (double *)malloc(mc_mn_Vars->Coord_Bin[2]*sizeof(double)); + if ((mc_mn_Vars->Mon2D_N == NULL) || (mc_mn_Vars->Mon2D_p == NULL) || (mc_mn_Vars->Mon2D_p2 == NULL)) + { fprintf(stderr,"Monitor_nD: %s 2D cannot allocate %s mc_mn_Vars->Mon2D_N/p/2p[%li] (%li). Fatal.\n", mc_mn_Vars->compcurname, mc_mn_Vars->Coord_Var[1], mc_mn_i, (mc_mn_Vars->Coord_Bin[2])*sizeof(double *)); exit(-1); } + else + { + for (mc_mn_j=0; mc_mn_j < mc_mn_Vars->Coord_Bin[2]; mc_mn_j++ ) + { mc_mn_Vars->Mon2D_N[mc_mn_i][mc_mn_j] = (double)0; mc_mn_Vars->Mon2D_p[mc_mn_i][mc_mn_j] = (double)0; mc_mn_Vars->Mon2D_p2[mc_mn_i][mc_mn_j] = (double)0; } + } + } + } + /* no Mon2D allocated for + * (mc_mn_Vars->Coord_Number != 2) && !mc_mn_Vars->Flag_Multiple && mc_mn_Vars->Flag_List */ + + mc_mn_Vars->psum = 0; + mc_mn_Vars->p2sum = 0; + mc_mn_Vars->Nsum = 0; + + mc_mn_Vars->area = fabs(mc_mn_Vars->mxmax - mc_mn_Vars->mxmin)*fabs(mc_mn_Vars->mymax - mc_mn_Vars->mymin)*1E4; /* in cm**2 for square and box shapes */ + mc_mn_Vars->Sphere_Radius = fabs(mc_mn_Vars->mxmax - mc_mn_Vars->mxmin)/2; + if ((abs(mc_mn_Vars->Flag_Shape) == mc_mn_DEFS->SHAPE_DISK) || (abs(mc_mn_Vars->Flag_Shape) == mc_mn_DEFS->SHAPE_SPHERE)) + { + mc_mn_Vars->area = PI*mc_mn_Vars->Sphere_Radius*mc_mn_Vars->Sphere_Radius; /* disk shapes */ + } + if (mc_mn_Vars->area == 0) mc_mn_Vars->Coord_Number = 0; + if (mc_mn_Vars->Coord_Number == 0 && mc_mn_Vars->Flag_Verbose) printf("Monitor_nD: %s is unactivated (0D)\n", mc_mn_Vars->compcurname); + mc_mn_Vars->Cylinder_Height = fabs(mc_mn_Vars->mymax - mc_mn_Vars->mymin); + + if (mc_mn_Vars->Intermediate < 0) mc_mn_Vars->Intermediate = 0; + if (mc_mn_Vars->Intermediate > 1) mc_mn_Vars->Intermediate /= 100; + mc_mn_Vars->IntermediateCnts = mc_mn_Vars->Intermediate*mcget_ncount(); + + if (mc_mn_Vars->Flag_Verbose) + { + printf("Monitor_nD: %s is a %s.\n", mc_mn_Vars->compcurname, mc_mn_Vars->Monitor_Label); + printf("Monitor_nD: version %s with options=%s\n", MONITOR_ND_LIB_H, mc_mn_Vars->option); + } + } /* end Monitor_nD_Init */ + +/* ========================================================================= */ +/* ADD: E.Farhi, Aug 6th, 2001: Monitor_nD section */ +/* this routine is used to monitor one propagating neutron */ +/* ========================================================================= */ + +double Monitor_nD_Trace(MonitornD_Defines_type *mc_mn_DEFS, MonitornD_Variables_type *mc_mn_Vars) +{ + + double mc_mn_XY=0; + long mc_mn_i,mc_mn_j; + double mc_mn_pp; + double mc_mn_Coord[MONnD_COORD_NMAX]; + long mc_mn_Coord_Index[MONnD_COORD_NMAX]; + char mc_mn_While_End =0; + long mc_mn_While_Buffer=0; + char mc_mn_Set_Vars_Coord_Type = mc_mn_DEFS->COORD_NONE; + + /* mc_mn_Vars->Flag_Auto_Limits */ + if ((mc_mn_Vars->Buffer_Counter >= mc_mn_Vars->Buffer_Block) && (mc_mn_Vars->Flag_Auto_Limits == 1) && (mc_mn_Vars->Coord_Number > 0)) + { + /* auto limits case : get limits in Buffer for each variable */ + /* Dim : (mc_mn_Vars->Coord_Number+1)*mc_mn_Vars->Buffer_Block matrix (for p, dp) */ + if (mc_mn_Vars->Flag_Verbose) printf("Monitor_nD: %s getting %li Auto Limits from List (%li).\n", mc_mn_Vars->compcurname, mc_mn_Vars->Coord_Number, mc_mn_Vars->Buffer_Counter); + for (mc_mn_i = 1; mc_mn_i <= mc_mn_Vars->Coord_Number; mc_mn_i++) + { + if (mc_mn_Vars->Coord_Type[mc_mn_i] & mc_mn_DEFS->COORD_AUTO) + { + mc_mn_Vars->Coord_Min[mc_mn_i] = FLT_MAX; + mc_mn_Vars->Coord_Max[mc_mn_i] = -FLT_MAX; + for (mc_mn_j = 0; mc_mn_j < mc_mn_Vars->Buffer_Block; mc_mn_j++) + { + mc_mn_XY = mc_mn_Vars->Mon2D_Buffer[mc_mn_i+mc_mn_j*(mc_mn_Vars->Coord_Number+1)]; /* scanning variables in Buffer */ + if (mc_mn_XY < mc_mn_Vars->Coord_Min[mc_mn_i]) mc_mn_Vars->Coord_Min[mc_mn_i] = mc_mn_XY; + if (mc_mn_XY > mc_mn_Vars->Coord_Max[mc_mn_i]) mc_mn_Vars->Coord_Max[mc_mn_i] = mc_mn_XY; + } + } + } + mc_mn_Vars->Flag_Auto_Limits = 2; /* pass to 2nd auto limits step */ + } + + /* manage realloc for list all if Buffer size exceeded */ + if ((mc_mn_Vars->Buffer_Counter >= mc_mn_Vars->Buffer_Block) && (mc_mn_Vars->Flag_List >= 2)) + { + if (mc_mn_Vars->Buffer_Size >= 20000 || mc_mn_Vars->Flag_List == 3) + { /* save current (possibly append) and re-use Buffer */ + Monitor_nD_Save(mc_mn_DEFS, mc_mn_Vars); + mc_mn_Vars->Flag_List = 3; + mc_mn_Vars->Buffer_Block = mc_mn_Vars->Buffer_Size; + mc_mn_Vars->Buffer_Counter = 0; + mc_mn_Vars->Neutron_Counter = 0; + } + else + { + mc_mn_Vars->Mon2D_Buffer = (double *)realloc(mc_mn_Vars->Mon2D_Buffer, (mc_mn_Vars->Coord_Number+1)*(mc_mn_Vars->Neutron_Counter+mc_mn_Vars->Buffer_Block)*sizeof(double)); + if (mc_mn_Vars->Mon2D_Buffer == NULL) + { printf("Monitor_nD: %s cannot reallocate mc_mn_Vars->Mon2D_Buffer[%li] (%li). Skipping.\n", mc_mn_Vars->compcurname, mc_mn_i, (mc_mn_Vars->Neutron_Counter+mc_mn_Vars->Buffer_Block)*sizeof(double)); mc_mn_Vars->Flag_List = 1; } + else { mc_mn_Vars->Buffer_Counter = 0; mc_mn_Vars->Buffer_Size = mc_mn_Vars->Neutron_Counter+mc_mn_Vars->Buffer_Block; } + } + } + + while (!mc_mn_While_End) + { /* we generate mc_mn_Coord[] and Coord_mc_mn_index[] from Buffer (auto limits) or passing neutron */ + if ((mc_mn_Vars->Flag_Auto_Limits == 2) && (mc_mn_Vars->Coord_Number > 0)) + { + if (mc_mn_While_Buffer < mc_mn_Vars->Buffer_Block) + { + /* first while loops (mc_mn_While_Buffer) */ + /* auto limits case : scan Buffer within limits and store in Mon2D */ + mc_mn_pp = mc_mn_Vars->Mon2D_Buffer[mc_mn_While_Buffer*(mc_mn_Vars->Coord_Number+1)]; + mc_mn_Coord[0] = mc_mn_pp; + + for (mc_mn_i = 1; mc_mn_i <= mc_mn_Vars->Coord_Number; mc_mn_i++) + { + /* scanning variables in Buffer */ + mc_mn_XY = (mc_mn_Vars->Coord_Max[mc_mn_i]-mc_mn_Vars->Coord_Min[mc_mn_i]); + + mc_mn_Coord[mc_mn_i] = mc_mn_Vars->Mon2D_Buffer[mc_mn_i+mc_mn_While_Buffer*(mc_mn_Vars->Coord_Number+1)]; + if (mc_mn_XY > 0) mc_mn_Coord_Index[mc_mn_i] = floor((mc_mn_Coord[mc_mn_i]-mc_mn_Vars->Coord_Min[mc_mn_i])*mc_mn_Vars->Coord_Bin[mc_mn_i]/mc_mn_XY); + else mc_mn_Coord_Index[mc_mn_i] = 0; + if (mc_mn_Vars->Flag_With_Borders) + { + if (mc_mn_Coord_Index[mc_mn_i] < 0) mc_mn_Coord_Index[mc_mn_i] = 0; + if (mc_mn_Coord_Index[mc_mn_i] >= mc_mn_Vars->Coord_Bin[mc_mn_i]) mc_mn_Coord_Index[mc_mn_i] = mc_mn_Vars->Coord_Bin[mc_mn_i] - 1; + } + } /* end for */ + mc_mn_While_Buffer++; + } /* end if in Buffer */ + else /* (mc_mn_While_Buffer >= mc_mn_Vars->Buffer_Block) && (mc_mn_Vars->Flag_Auto_Limits == 2) */ + { + mc_mn_Vars->Flag_Auto_Limits = 0; + if (!mc_mn_Vars->Flag_List) /* free Buffer not needed (no list to output) */ + { /* Dim : (mc_mn_Vars->Coord_Number+1)*mc_mn_Vars->Buffer_Block matrix (for p, dp) */ + free(mc_mn_Vars->Mon2D_Buffer); mc_mn_Vars->Mon2D_Buffer = NULL; + } + } + } + else /* mc_mn_Vars->Flag_Auto_Limits == 0 or 1 */ + { + for (mc_mn_i = 0; mc_mn_i <= mc_mn_Vars->Coord_Number; mc_mn_i++) + { /* handle current neutron : last while */ + + mc_mn_XY = 0; + mc_mn_Set_Vars_Coord_Type = (mc_mn_Vars->Coord_Type[mc_mn_i] & 31); + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_X) mc_mn_XY = mc_mn_Vars->cx; + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_Y) mc_mn_XY = mc_mn_Vars->cy; + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_Z) mc_mn_XY = mc_mn_Vars->cz; + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_VX) mc_mn_XY = mc_mn_Vars->cvx; + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_VY) mc_mn_XY = mc_mn_Vars->cvy; + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_VZ) mc_mn_XY = mc_mn_Vars->cvz; + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_KX) mc_mn_XY = V2K*mc_mn_Vars->cvx; + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_KY) mc_mn_XY = V2K*mc_mn_Vars->cvy; + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_KZ) mc_mn_XY = V2K*mc_mn_Vars->cvz; + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_SX) mc_mn_XY = mc_mn_Vars->csx; + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_SY) mc_mn_XY = mc_mn_Vars->csy; + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_SZ) mc_mn_XY = mc_mn_Vars->csz; + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_T) mc_mn_XY = mc_mn_Vars->ct; + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_P) mc_mn_XY = mc_mn_Vars->cp; + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_HDIV) mc_mn_XY = RAD2DEG*atan2(mc_mn_Vars->cvx,mc_mn_Vars->cvz); + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_VDIV) mc_mn_XY = RAD2DEG*atan2(mc_mn_Vars->cvy,mc_mn_Vars->cvz); + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_V) mc_mn_XY = sqrt(mc_mn_Vars->cvx*mc_mn_Vars->cvx+mc_mn_Vars->cvy*mc_mn_Vars->cvy+mc_mn_Vars->cvz*mc_mn_Vars->cvz); + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_RADIUS) mc_mn_XY = sqrt(mc_mn_Vars->cx*mc_mn_Vars->cx+mc_mn_Vars->cy*mc_mn_Vars->cy); + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_K) { mc_mn_XY = sqrt(mc_mn_Vars->cvx*mc_mn_Vars->cvx+mc_mn_Vars->cvy*mc_mn_Vars->cvy+mc_mn_Vars->cvz*mc_mn_Vars->cvz); mc_mn_XY *= V2K; } + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_ENERGY) { mc_mn_XY = mc_mn_Vars->cvx*mc_mn_Vars->cvx+mc_mn_Vars->cvy*mc_mn_Vars->cvy+mc_mn_Vars->cvz*mc_mn_Vars->cvz; mc_mn_XY *= VS2E; } + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_LAMBDA) { mc_mn_XY = sqrt(mc_mn_Vars->cvx*mc_mn_Vars->cvx+mc_mn_Vars->cvy*mc_mn_Vars->cvy+mc_mn_Vars->cvz*mc_mn_Vars->cvz); mc_mn_XY *= V2K; if (mc_mn_XY != 0) mc_mn_XY = 2*PI/mc_mn_XY; } + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_NCOUNT) mc_mn_XY = mc_mn_Coord[mc_mn_i]+1; + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_ANGLE) + { mc_mn_XY = sqrt(mc_mn_Vars->cvx*mc_mn_Vars->cvx+mc_mn_Vars->cvy*mc_mn_Vars->cvy+mc_mn_Vars->cvz*mc_mn_Vars->cvz); + if (mc_mn_Vars->cvz != 0) + { + mc_mn_XY= RAD2DEG*atan2(mc_mn_XY,mc_mn_Vars->cvz); + } else mc_mn_XY = 0; + } + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_THETA) { if (mc_mn_Vars->cz != 0) mc_mn_XY = RAD2DEG*atan2(mc_mn_Vars->cx,mc_mn_Vars->cz); } + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_PHI) { if (mc_mn_Vars->cz != 0) mc_mn_XY = RAD2DEG*atan2(sqrt(mc_mn_Vars->cx*mc_mn_Vars->cx+mc_mn_Vars->cy*mc_mn_Vars->cy),mc_mn_Vars->cz); } + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_USER1) mc_mn_XY = mc_mn_Vars->UserVariable1; + else + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_USER2) mc_mn_XY = mc_mn_Vars->UserVariable2; + else + mc_mn_XY = 0; + + if (mc_mn_Vars->Coord_Type[mc_mn_i] & mc_mn_DEFS->COORD_ABS) mc_mn_XY=fabs(mc_mn_XY); + + if (mc_mn_i && (mc_mn_Vars->Coord_Type[mc_mn_i] & mc_mn_DEFS->COORD_LOG)) /* not for the flux */ + { if (mc_mn_XY > 0) mc_mn_XY = log(mc_mn_XY)/log(10); + else mc_mn_XY = -100; } + + mc_mn_Coord[mc_mn_i] = mc_mn_XY; + if (mc_mn_i == 0) { mc_mn_pp = mc_mn_XY; mc_mn_Coord_Index[mc_mn_i] = 0; } + else if (!mc_mn_Vars->Flag_Auto_Limits) + { + mc_mn_XY = (mc_mn_Vars->Coord_Max[mc_mn_i]-mc_mn_Vars->Coord_Min[mc_mn_i]); + if (mc_mn_XY > 0) mc_mn_Coord_Index[mc_mn_i] = floor((mc_mn_Coord[mc_mn_i]-mc_mn_Vars->Coord_Min[mc_mn_i])*mc_mn_Vars->Coord_Bin[mc_mn_i]/mc_mn_XY); + else mc_mn_Coord_Index[mc_mn_i] = 0; + if (mc_mn_Vars->Flag_With_Borders) + { + if (mc_mn_Coord_Index[mc_mn_i] < 0) mc_mn_Coord_Index[mc_mn_i] = 0; + if (mc_mn_Coord_Index[mc_mn_i] >= mc_mn_Vars->Coord_Bin[mc_mn_i]) mc_mn_Coord_Index[mc_mn_i] = mc_mn_Vars->Coord_Bin[mc_mn_i] - 1; + } + } /* else Auto_Limits will get Index later from Buffer */ + } /* end for mc_mn_i */ + mc_mn_While_End = 1; + } /* end else if mc_mn_Vars->Flag_Auto_Limits == 2 */ + + if (mc_mn_Vars->Flag_Auto_Limits != 2) /* not when reading auto limits Buffer */ + { /* now store Coord into Buffer (no mc_mn_index needed) if necessary */ + if ((mc_mn_Vars->Buffer_Counter < mc_mn_Vars->Buffer_Block) && ((mc_mn_Vars->Flag_List) || (mc_mn_Vars->Flag_Auto_Limits == 1))) + { + for (mc_mn_i = 0; mc_mn_i <= mc_mn_Vars->Coord_Number; mc_mn_i++) + { + mc_mn_Vars->Mon2D_Buffer[mc_mn_i + mc_mn_Vars->Neutron_Counter*(mc_mn_Vars->Coord_Number+1)] = mc_mn_Coord[mc_mn_i]; + } + mc_mn_Vars->Buffer_Counter++; + if (mc_mn_Vars->Flag_Verbose && (mc_mn_Vars->Buffer_Counter >= mc_mn_Vars->Buffer_Block) && (mc_mn_Vars->Flag_List == 1)) printf("Monitor_nD: %s %li neutrons stored in List.\n", mc_mn_Vars->compcurname, mc_mn_Vars->Buffer_Counter); + } + mc_mn_Vars->Neutron_Counter++; + } /* end (mc_mn_Vars->Flag_Auto_Limits != 2) */ + + /* store n1d/2d section for Buffer or current neutron in while */ + if (mc_mn_Vars->Flag_Auto_Limits != 1) /* not when storing auto limits Buffer */ + { + /* 1D and n1D case : mc_mn_Vars->Flag_Multiple */ + if (mc_mn_Vars->Flag_Multiple) + { /* Dim : mc_mn_Vars->Coord_Number*mc_mn_Vars->Coord_Bin[mc_mn_i] vectors (intensity is not included) */ + /* check limits: monitors define a phase space to record */ + char within_limits=1; + for (mc_mn_i= 1; mc_mn_i <= mc_mn_Vars->Coord_Number; mc_mn_i++) + { + mc_mn_j = mc_mn_Coord_Index[mc_mn_i]; + if (mc_mn_j < 0 || mc_mn_j >= mc_mn_Vars->Coord_Bin[mc_mn_i]) + within_limits=0; + } + if (within_limits) + for (mc_mn_i= 1; mc_mn_i <= mc_mn_Vars->Coord_Number; mc_mn_i++) + { + mc_mn_j = mc_mn_Coord_Index[mc_mn_i]; + if (mc_mn_j >= 0 && mc_mn_j < mc_mn_Vars->Coord_Bin[mc_mn_i]) + { + mc_mn_Vars->Mon2D_N[mc_mn_i-1][mc_mn_j]++; + mc_mn_Vars->Mon2D_p[mc_mn_i-1][mc_mn_j] += mc_mn_pp; + mc_mn_Vars->Mon2D_p2[mc_mn_i-1][mc_mn_j] += mc_mn_pp*mc_mn_pp; + } + } + } + else /* 2D case : mc_mn_Vars->Coord_Number==2 and !mc_mn_Vars->Flag_Multiple and !mc_mn_Vars->Flag_List */ + if ((mc_mn_Vars->Coord_Number == 2) && !mc_mn_Vars->Flag_Multiple) + { /* Dim : mc_mn_Vars->Coord_Bin[1]*mc_mn_Vars->Coord_Bin[2] matrix */ + mc_mn_i = mc_mn_Coord_Index[1]; + mc_mn_j = mc_mn_Coord_Index[2]; + if (mc_mn_i >= 0 && mc_mn_i < mc_mn_Vars->Coord_Bin[1] && mc_mn_j >= 0 && mc_mn_j < mc_mn_Vars->Coord_Bin[2]) + { + mc_mn_Vars->Mon2D_N[mc_mn_i][mc_mn_j]++; + mc_mn_Vars->Mon2D_p[mc_mn_i][mc_mn_j] += mc_mn_pp; + mc_mn_Vars->Mon2D_p2[mc_mn_i][mc_mn_j] += mc_mn_pp*mc_mn_pp; + } + } + } /* end (mc_mn_Vars->Flag_Auto_Limits != 1) */ + } /* end while */ + return mc_mn_pp; +} /* end Monitor_nD_Trace */ + +/* ========================================================================= */ +/* ADD: E.Farhi, Aug 6th, 2001: Monitor_nD section */ +/* this routine is used to save data files */ +/* ========================================================================= */ + +void Monitor_nD_Save(MonitornD_Defines_type *mc_mn_DEFS, MonitornD_Variables_type *mc_mn_Vars) + { + char *mc_mn_fname; + long mc_mn_i,mc_mn_j; + double *mc_mn_p0m = NULL; + double *mc_mn_p1m = NULL; + double *mc_mn_p2m = NULL; + char mc_mn_Coord_X_Label[1024]; + double mc_mn_min1d, mc_mn_max1d; + double mc_mn_min2d, mc_mn_max2d; + long mc_mn_bin1d, mc_mn_bin2d; + char mc_mn_While_End = 0; + long mc_mn_While_Buffer = 0; + double mc_mn_XY, mc_mn_pp; + double mc_mn_Coord[MONnD_COORD_NMAX]; + long mc_mn_Coord_Index[MONnD_COORD_NMAX]; + char mc_mn_label[1024]; + double mc_mn_ratio; + + mc_mn_ratio = 100*mcget_run_num()/mcget_ncount(); + + if (mc_mn_ratio < 99) + { + if (mc_mn_Vars->Flag_Verbose) printf("Monitor_nD: %s save intermediate results (%.2f %%).\n", mc_mn_Vars->compcurname, mc_mn_ratio); + } + /* check Buffer flush when end of simulation reached */ + if ((mc_mn_Vars->Buffer_Counter <= mc_mn_Vars->Buffer_Block) && mc_mn_Vars->Flag_Auto_Limits && mc_mn_Vars->Mon2D_Buffer && mc_mn_Vars->Buffer_Counter) + { + /* Get Auto Limits */ + if (mc_mn_Vars->Flag_Verbose) printf("Monitor_nD: %s getting %li Auto Limits from List (%li).\n", mc_mn_Vars->compcurname, mc_mn_Vars->Coord_Number, mc_mn_Vars->Buffer_Counter); + for (mc_mn_i = 1; mc_mn_i <= mc_mn_Vars->Coord_Number; mc_mn_i++) + { + if (mc_mn_Vars->Coord_Type[mc_mn_i] & mc_mn_DEFS->COORD_AUTO) + { + mc_mn_Vars->Coord_Min[mc_mn_i] = FLT_MAX; + mc_mn_Vars->Coord_Max[mc_mn_i] = -FLT_MAX; + + for (mc_mn_j = 0; mc_mn_j < mc_mn_Vars->Buffer_Counter; mc_mn_j++) + { + mc_mn_XY = mc_mn_Vars->Mon2D_Buffer[mc_mn_i+mc_mn_j*(mc_mn_Vars->Coord_Number+1)]; /* scanning variables in Buffer */ + if (mc_mn_XY < mc_mn_Vars->Coord_Min[mc_mn_i]) mc_mn_Vars->Coord_Min[mc_mn_i] = mc_mn_XY; + if (mc_mn_XY > mc_mn_Vars->Coord_Max[mc_mn_i]) mc_mn_Vars->Coord_Max[mc_mn_i] = mc_mn_XY; + + } + } + } + mc_mn_Vars->Flag_Auto_Limits = 2; /* pass to 2nd auto limits step */ + mc_mn_Vars->Buffer_Block = mc_mn_Vars->Buffer_Counter; + + while (!mc_mn_While_End) + { /* we generate mc_mn_Coord[] and Coord_mc_mn_index[] from Buffer (auto limits) or passing neutron */ + if (mc_mn_While_Buffer < mc_mn_Vars->Buffer_Block) + { + /* first while loops (mc_mn_While_Buffer) */ + mc_mn_Coord[0] = mc_mn_Vars->Mon2D_Buffer[mc_mn_While_Buffer*(mc_mn_Vars->Coord_Number+1)]; + + /* auto limits case : scan Buffer within limits and store in Mon2D */ + for (mc_mn_i = 1; mc_mn_i <= mc_mn_Vars->Coord_Number; mc_mn_i++) + { + /* scanning variables in Buffer */ + mc_mn_XY = (mc_mn_Vars->Coord_Max[mc_mn_i]-mc_mn_Vars->Coord_Min[mc_mn_i]); + mc_mn_Coord[mc_mn_i] = mc_mn_Vars->Mon2D_Buffer[mc_mn_i+mc_mn_While_Buffer*(mc_mn_Vars->Coord_Number+1)]; + if (mc_mn_XY > 0) mc_mn_Coord_Index[mc_mn_i] = floor((mc_mn_Coord[mc_mn_i]-mc_mn_Vars->Coord_Min[mc_mn_i])*mc_mn_Vars->Coord_Bin[mc_mn_i]/mc_mn_XY); + else mc_mn_Coord_Index[mc_mn_i] = 0; + if (mc_mn_Vars->Flag_With_Borders) + { + if (mc_mn_Coord_Index[mc_mn_i] < 0) mc_mn_Coord_Index[mc_mn_i] = 0; + if (mc_mn_Coord_Index[mc_mn_i] >= mc_mn_Vars->Coord_Bin[mc_mn_i]) mc_mn_Coord_Index[mc_mn_i] = mc_mn_Vars->Coord_Bin[mc_mn_i] - 1; + } + } /* end for */ + mc_mn_While_Buffer++; + } /* end if in Buffer */ + else /* (mc_mn_While_Buffer >= mc_mn_Vars->Buffer_Block) && (mc_mn_Vars->Flag_Auto_Limits == 2) */ + { + mc_mn_Vars->Flag_Auto_Limits = 0; + mc_mn_While_End = 1; + } + + /* store n1d/2d section from Buffer */ + + mc_mn_pp = mc_mn_Coord[0]; + /* 1D and n1D case : mc_mn_Vars->Flag_Multiple */ + if (mc_mn_Vars->Flag_Multiple) + { /* Dim : mc_mn_Vars->Coord_Number*mc_mn_Vars->Coord_Bin[mc_mn_i] vectors (intensity is not included) */ + for (mc_mn_i= 0; mc_mn_i < mc_mn_Vars->Coord_Number; mc_mn_i++) + { + mc_mn_j = mc_mn_Coord_Index[mc_mn_i+1]; + if (mc_mn_j >= 0 && mc_mn_j < mc_mn_Vars->Coord_Bin[mc_mn_i+1]) + { + mc_mn_Vars->Mon2D_N[mc_mn_i][mc_mn_j]++; + mc_mn_Vars->Mon2D_p[mc_mn_i][mc_mn_j] += mc_mn_pp; + mc_mn_Vars->Mon2D_p2[mc_mn_i][mc_mn_j] += mc_mn_pp*mc_mn_pp; + } + } + } + else /* 2D case : mc_mn_Vars->Coord_Number==2 and !mc_mn_Vars->Flag_Multiple and !mc_mn_Vars->Flag_List */ + if ((mc_mn_Vars->Coord_Number == 2) && !mc_mn_Vars->Flag_Multiple) + { /* Dim : mc_mn_Vars->Coord_Bin[1]*mc_mn_Vars->Coord_Bin[2] matrix */ + mc_mn_i = mc_mn_Coord_Index[1]; + mc_mn_j = mc_mn_Coord_Index[2]; + if (mc_mn_i >= 0 && mc_mn_i < mc_mn_Vars->Coord_Bin[1] && mc_mn_j >= 0 && mc_mn_j < mc_mn_Vars->Coord_Bin[2]) + { + mc_mn_Vars->Mon2D_N[mc_mn_i][mc_mn_j]++; + mc_mn_Vars->Mon2D_p[mc_mn_i][mc_mn_j] += mc_mn_pp; + mc_mn_Vars->Mon2D_p2[mc_mn_i][mc_mn_j] += mc_mn_pp*mc_mn_pp; + } + } /* end store 2D/1D */ + } /* end while */ + } /* end Force Get Limits */ + + /* write output files (sent to file as p[i*n + j] vectors) */ + if (mc_mn_Vars->Coord_Number == 0) + { + double mc_mn_Nsum; + double mc_mn_psum, mc_mn_p2sum; + mc_mn_Nsum = mc_mn_Vars->Nsum; + mc_mn_psum = mc_mn_Vars->psum; + mc_mn_p2sum= mc_mn_Vars->p2sum; + if (mc_mn_Vars->Flag_signal != mc_mn_DEFS->COORD_P && mc_mn_Nsum > 0) + { mc_mn_psum /=mc_mn_Nsum; mc_mn_p2sum /= mc_mn_Nsum*mc_mn_Nsum; } + /* DETECTOR_OUT_0D(mc_mn_Vars->Monitor_Label, mc_mn_Vars->Nsum, mc_mn_Vars->psum, mc_mn_Vars->p2sum); */ + mcdetector_out_0D(mc_mn_Vars->Monitor_Label, mc_mn_Nsum, mc_mn_psum, mc_mn_p2sum, mc_mn_Vars->compcurname); + } + else + if (strlen(mc_mn_Vars->Mon_File) > 0) + { + mc_mn_fname = (char*)malloc(strlen(mc_mn_Vars->Mon_File)+10*mc_mn_Vars->Coord_Number); + if (mc_mn_Vars->Flag_List && mc_mn_Vars->Mon2D_Buffer) /* List: DETECTOR_OUT_2D */ + { + int loc_ascii_only; + char formatName[64]; + char *formatName_orig; + + if (mc_mn_Vars->Flag_List >= 2) mc_mn_Vars->Buffer_Size = mc_mn_Vars->Neutron_Counter; + if (mc_mn_Vars->Buffer_Size >= mc_mn_Vars->Neutron_Counter) + mc_mn_Vars->Buffer_Size = mc_mn_Vars->Neutron_Counter; + strcpy(mc_mn_fname,mc_mn_Vars->Mon_File); + if (strchr(mc_mn_Vars->Mon_File,'.') == NULL) strcat(mc_mn_fname, "_list"); + + mc_mn_min1d = 1; mc_mn_max1d = mc_mn_Vars->Coord_Number+1; + mc_mn_min2d = 0; mc_mn_max2d = mc_mn_Vars->Buffer_Size; + mc_mn_bin1d = mc_mn_Vars->Coord_Number+1; mc_mn_bin2d = mc_mn_Vars->Buffer_Size; + strcpy(mc_mn_Coord_X_Label,""); + for (mc_mn_i= 0; mc_mn_i <= mc_mn_Vars->Coord_Number; mc_mn_i++) + { + if (mc_mn_min2d < mc_mn_Vars->Coord_Min[mc_mn_i]) mc_mn_min2d = mc_mn_Vars->Coord_Min[mc_mn_i]; + if (mc_mn_max2d < mc_mn_Vars->Coord_Max[mc_mn_i]) mc_mn_max2d = mc_mn_Vars->Coord_Max[mc_mn_i]; + strcat(mc_mn_Coord_X_Label, mc_mn_Vars->Coord_Var[mc_mn_i]); + strcat(mc_mn_Coord_X_Label, " "); + if (strchr(mc_mn_Vars->Mon_File,'.') == NULL) + { strcat(mc_mn_fname, "."); strcat(mc_mn_fname, mc_mn_Vars->Coord_Var[mc_mn_i]); } + } + if (mc_mn_Vars->Flag_Verbose) printf("Monitor_nD: %s write monitor file %s List (%lix%li).\n", mc_mn_Vars->compcurname, mc_mn_fname,mc_mn_bin2d,mc_mn_bin1d); + + /* handle the type of list output */ + loc_ascii_only = mcascii_only; + formatName_orig = mcformat.Name; /* copy the pointer position */ + strcpy(formatName, mcformat.Name); + if (mc_mn_Vars->Flag_List >= 1) + { + strcat(formatName, " partial "); + if (mc_mn_Vars->Flag_List > 2) + { strcat(formatName, " append "); mcascii_only = 1; } + if (mc_mn_Vars->Flag_Binary_List) mcascii_only = 1; + if (mc_mn_Vars->Flag_Binary_List == 1) + strcat(formatName, " binary float "); + else if (mc_mn_Vars->Flag_Binary_List == 2) + strcat(formatName, " binary double "); + } + if (mc_mn_min2d == mc_mn_max2d) mc_mn_max2d = mc_mn_min2d+1e-6; + if (mc_mn_min1d == mc_mn_max1d) mc_mn_max1d = mc_mn_min1d+1e-6; + strcpy(mc_mn_label, mc_mn_Vars->Monitor_Label); + if (!mc_mn_Vars->Flag_Binary_List) + { mc_mn_bin2d=-mc_mn_bin2d; } + mcformat.Name = formatName; + mcdetector_out_2D( + mc_mn_label, + "List of neutron events", + mc_mn_Coord_X_Label, + mc_mn_min2d, mc_mn_max2d, + mc_mn_min1d, mc_mn_max1d, + mc_mn_bin2d, + mc_mn_bin1d, + NULL,mc_mn_Vars->Mon2D_Buffer,NULL, + mc_mn_fname, mc_mn_Vars->compcurname); + + /* reset the original type of output */ + mcascii_only = loc_ascii_only; + mcformat.Name= formatName_orig; + } + if (mc_mn_Vars->Flag_Multiple) /* n1D: DETECTOR_OUT_1D */ + { + for (mc_mn_i= 0; mc_mn_i < mc_mn_Vars->Coord_Number; mc_mn_i++) + { + + strcpy(mc_mn_fname,mc_mn_Vars->Mon_File); + if (strchr(mc_mn_Vars->Mon_File,'.') == NULL) + { strcat(mc_mn_fname, "."); strcat(mc_mn_fname, mc_mn_Vars->Coord_Var[mc_mn_i+1]); } + sprintf(mc_mn_Coord_X_Label, "%s monitor", mc_mn_Vars->Coord_Label[mc_mn_i+1]); + strcpy(mc_mn_label, mc_mn_Coord_X_Label); + if (mc_mn_Vars->Coord_Bin[mc_mn_i+1] > 0) { /* 1D monitor */ + if (mc_mn_Vars->Flag_Verbose) printf("Monitor_nD: %s write monitor file %s 1D (%li).\n", mc_mn_Vars->compcurname, mc_mn_fname, mc_mn_Vars->Coord_Bin[mc_mn_i+1]); + mc_mn_min1d = mc_mn_Vars->Coord_Min[mc_mn_i+1]; + mc_mn_max1d = mc_mn_Vars->Coord_Max[mc_mn_i+1]; + if (mc_mn_min1d == mc_mn_max1d) mc_mn_max1d = mc_mn_min1d+1e-6; + mc_mn_p1m = (double *)malloc(mc_mn_Vars->Coord_Bin[mc_mn_i+1]*sizeof(double)); + mc_mn_p2m = (double *)malloc(mc_mn_Vars->Coord_Bin[mc_mn_i+1]*sizeof(double)); + if (mc_mn_p2m == NULL) /* use Raw Buffer line output */ + { + if (mc_mn_Vars->Flag_Verbose) printf("Monitor_nD: %s cannot allocate memory for output. Using raw data.\n", mc_mn_Vars->compcurname); + if (mc_mn_p1m != NULL) free(mc_mn_p1m); + mcdetector_out_1D( + mc_mn_label, + mc_mn_Vars->Coord_Label[mc_mn_i+1], + mc_mn_Vars->Coord_Label[0], + mc_mn_Vars->Coord_Var[mc_mn_i+1], + mc_mn_min1d, mc_mn_max1d, + mc_mn_Vars->Coord_Bin[mc_mn_i+1], + mc_mn_Vars->Mon2D_N[mc_mn_i],mc_mn_Vars->Mon2D_p[mc_mn_i],mc_mn_Vars->Mon2D_p2[mc_mn_i], + mc_mn_fname, mc_mn_Vars->compcurname); + } /* if (mc_mn_p2m == NULL) */ + else + { + if (mc_mn_Vars->Flag_log != 0) + { + mc_mn_XY = FLT_MAX; + for (mc_mn_j=0; mc_mn_j < mc_mn_Vars->Coord_Bin[mc_mn_i+1]; mc_mn_j++) /* search min of signal */ + if ((mc_mn_XY > mc_mn_Vars->Mon2D_p[mc_mn_i][mc_mn_j]) && (mc_mn_Vars->Mon2D_p[mc_mn_i][mc_mn_j] > 0)) mc_mn_XY = mc_mn_Vars->Mon2D_p[mc_mn_i][mc_mn_j]; + if (mc_mn_XY <= 0) mc_mn_XY = -log(FLT_MAX)/log(10); else mc_mn_XY = log(mc_mn_XY)/log(10)-1; + } /* if */ + + for (mc_mn_j=0; mc_mn_j < mc_mn_Vars->Coord_Bin[mc_mn_i+1]; mc_mn_j++) + { + mc_mn_p1m[mc_mn_j] = mc_mn_Vars->Mon2D_p[mc_mn_i][mc_mn_j]; + mc_mn_p2m[mc_mn_j] = mc_mn_Vars->Mon2D_p2[mc_mn_i][mc_mn_j]; + if (mc_mn_Vars->Flag_signal != mc_mn_DEFS->COORD_P && mc_mn_Vars->Mon2D_N[mc_mn_i][mc_mn_j] > 0) + { /* normalize mean signal to the number of events */ + mc_mn_p1m[mc_mn_j] /= mc_mn_Vars->Mon2D_N[mc_mn_i][mc_mn_j]; + mc_mn_p2m[mc_mn_j] /= mc_mn_Vars->Mon2D_N[mc_mn_i][mc_mn_j]*mc_mn_Vars->Mon2D_N[mc_mn_i][mc_mn_j]; + } + if (mc_mn_Vars->Flag_log != 0) + { + if ((mc_mn_p1m[mc_mn_j] > 0) && (mc_mn_p2m[mc_mn_j] > 0)) + { + mc_mn_p2m[mc_mn_j] /= mc_mn_p1m[mc_mn_j]*mc_mn_p1m[mc_mn_j]; + mc_mn_p1m[mc_mn_j] = log(mc_mn_p1m[mc_mn_j])/log(10); + } + else + { + mc_mn_p1m[mc_mn_j] = mc_mn_XY; + mc_mn_p2m[mc_mn_j] = 0; + } + } + } /* for */ + mcdetector_out_1D( + mc_mn_label, + mc_mn_Vars->Coord_Label[mc_mn_i+1], + mc_mn_Vars->Coord_Label[0], + mc_mn_Vars->Coord_Var[mc_mn_i+1], + mc_mn_min1d, mc_mn_max1d, + mc_mn_Vars->Coord_Bin[mc_mn_i+1], + mc_mn_Vars->Mon2D_N[mc_mn_i],mc_mn_p1m,mc_mn_p2m, + mc_mn_fname, mc_mn_Vars->compcurname); + + } /* else */ + if (mc_mn_p1m != NULL) free(mc_mn_p1m); mc_mn_p1m=NULL; + if (mc_mn_p2m != NULL) free(mc_mn_p2m); mc_mn_p2m=NULL; + } else { /* 0d monitor */ + mcdetector_out_0D(mc_mn_label, mc_mn_Vars->Mon2D_p[mc_mn_i][0], mc_mn_Vars->Mon2D_p2[mc_mn_i][0], mc_mn_Vars->Mon2D_N[mc_mn_i][0], mc_mn_Vars->compcurname); + } + + + } /* for */ + } /* if 1D */ + else + if (mc_mn_Vars->Coord_Number == 2) /* 2D: DETECTOR_OUT_2D */ + { + strcpy(mc_mn_fname,mc_mn_Vars->Mon_File); + + mc_mn_p0m = (double *)malloc(mc_mn_Vars->Coord_Bin[1]*mc_mn_Vars->Coord_Bin[2]*sizeof(double)); + mc_mn_p1m = (double *)malloc(mc_mn_Vars->Coord_Bin[1]*mc_mn_Vars->Coord_Bin[2]*sizeof(double)); + mc_mn_p2m = (double *)malloc(mc_mn_Vars->Coord_Bin[1]*mc_mn_Vars->Coord_Bin[2]*sizeof(double)); + if (mc_mn_p2m == NULL) + { + if (mc_mn_Vars->Flag_Verbose) printf("Monitor_nD: %s cannot allocate memory for 2D array (%li). Skipping.\n", mc_mn_Vars->compcurname, 3*mc_mn_Vars->Coord_Bin[1]*mc_mn_Vars->Coord_Bin[2]*sizeof(double)); + if (mc_mn_p0m != NULL) free(mc_mn_p0m); + if (mc_mn_p1m != NULL) free(mc_mn_p1m); + } + else + { + if (mc_mn_Vars->Flag_log != 0) + { + mc_mn_XY = FLT_MAX; + for (mc_mn_i= 0; mc_mn_i < mc_mn_Vars->Coord_Bin[1]; mc_mn_i++) + for (mc_mn_j= 0; mc_mn_j < mc_mn_Vars->Coord_Bin[2]; mc_mn_j++) /* search min of signal */ + if ((mc_mn_XY > mc_mn_Vars->Mon2D_p[mc_mn_i][mc_mn_j]) && (mc_mn_Vars->Mon2D_p[mc_mn_i][mc_mn_j]>0)) mc_mn_XY = mc_mn_Vars->Mon2D_p[mc_mn_i][mc_mn_j]; + if (mc_mn_XY <= 0) mc_mn_XY = -log(FLT_MAX)/log(10); else mc_mn_XY = log(mc_mn_XY)/log(10)-1; + } + for (mc_mn_i= 0; mc_mn_i < mc_mn_Vars->Coord_Bin[1]; mc_mn_i++) + { + for (mc_mn_j= 0; mc_mn_j < mc_mn_Vars->Coord_Bin[2]; mc_mn_j++) + { + long mc_mn_index; + mc_mn_index = mc_mn_j + mc_mn_i*mc_mn_Vars->Coord_Bin[2]; + mc_mn_p0m[mc_mn_index] = mc_mn_Vars->Mon2D_N[mc_mn_i][mc_mn_j]; + mc_mn_p1m[mc_mn_index] = mc_mn_Vars->Mon2D_p[mc_mn_i][mc_mn_j]; + mc_mn_p2m[mc_mn_index] = mc_mn_Vars->Mon2D_p2[mc_mn_i][mc_mn_j]; + if (mc_mn_Vars->Flag_signal != mc_mn_DEFS->COORD_P && mc_mn_p0m[mc_mn_index] > 0) + { + mc_mn_p1m[mc_mn_index] /= mc_mn_p0m[mc_mn_index]; + mc_mn_p2m[mc_mn_index] /= mc_mn_p0m[mc_mn_index]*mc_mn_p0m[mc_mn_index]; + } + + if (mc_mn_Vars->Flag_log != 0) + { + if ((mc_mn_p1m[mc_mn_index] > 0) && (mc_mn_p2m[mc_mn_index] > 0)) + { + mc_mn_p2m[mc_mn_index] /= (mc_mn_p1m[mc_mn_index]*mc_mn_p1m[mc_mn_index]); + mc_mn_p1m[mc_mn_index] = log(mc_mn_p1m[mc_mn_index])/log(10); + + } + else + { + mc_mn_p1m[mc_mn_index] = mc_mn_XY; + mc_mn_p2m[mc_mn_index] = 0; + } + } + } + } + if (strchr(mc_mn_Vars->Mon_File,'.') == NULL) + { strcat(mc_mn_fname, "."); strcat(mc_mn_fname, mc_mn_Vars->Coord_Var[1]); + strcat(mc_mn_fname, "_"); strcat(mc_mn_fname, mc_mn_Vars->Coord_Var[2]); } + if (mc_mn_Vars->Flag_Verbose) printf("Monitor_nD: %s write monitor file %s 2D (%lix%li).\n", mc_mn_Vars->compcurname, mc_mn_fname, mc_mn_Vars->Coord_Bin[1], mc_mn_Vars->Coord_Bin[2]); + + mc_mn_min1d = mc_mn_Vars->Coord_Min[1]; + mc_mn_max1d = mc_mn_Vars->Coord_Max[1]; + if (mc_mn_min1d == mc_mn_max1d) mc_mn_max1d = mc_mn_min1d+1e-6; + mc_mn_min2d = mc_mn_Vars->Coord_Min[2]; + mc_mn_max2d = mc_mn_Vars->Coord_Max[2]; + if (mc_mn_min2d == mc_mn_max2d) mc_mn_max2d = mc_mn_min2d+1e-6; + strcpy(mc_mn_label, mc_mn_Vars->Monitor_Label); + + mcdetector_out_2D( + mc_mn_label, + mc_mn_Vars->Coord_Label[1], + mc_mn_Vars->Coord_Label[2], + mc_mn_min1d, mc_mn_max1d, + mc_mn_min2d, mc_mn_max2d, + mc_mn_Vars->Coord_Bin[1], + mc_mn_Vars->Coord_Bin[2], + mc_mn_p0m,mc_mn_p1m,mc_mn_p2m, + mc_mn_fname, mc_mn_Vars->compcurname); + + if (mc_mn_p0m != NULL) free(mc_mn_p0m); + if (mc_mn_p1m != NULL) free(mc_mn_p1m); + if (mc_mn_p2m != NULL) free(mc_mn_p2m); + } + } + free(mc_mn_fname); + } + } /* end Monitor_nD_Save */ + +/* ========================================================================= */ +/* ADD: E.Farhi, Aug 6th, 2001: Monitor_nD section */ +/* this routine is used to free memory */ +/* ========================================================================= */ + +void Monitor_nD_Finally(MonitornD_Defines_type *mc_mn_DEFS, + MonitornD_Variables_type *mc_mn_Vars) + { + int mc_mn_i; + + /* Now Free memory Mon2D.. */ + if ((mc_mn_Vars->Flag_Auto_Limits || mc_mn_Vars->Flag_List) && mc_mn_Vars->Coord_Number) + { /* Dim : (mc_mn_Vars->Coord_Number+1)*mc_mn_Vars->Buffer_Block matrix (for p, dp) */ + if (mc_mn_Vars->Mon2D_Buffer != NULL) free(mc_mn_Vars->Mon2D_Buffer); + } + + /* 1D and n1D case : mc_mn_Vars->Flag_Multiple */ + if (mc_mn_Vars->Flag_Multiple && mc_mn_Vars->Coord_Number) + { /* Dim : mc_mn_Vars->Coord_Number*mc_mn_Vars->Coord_Bin[mc_mn_i] vectors */ + for (mc_mn_i= 0; mc_mn_i < mc_mn_Vars->Coord_Number; mc_mn_i++) + { + free(mc_mn_Vars->Mon2D_N[mc_mn_i]); + free(mc_mn_Vars->Mon2D_p[mc_mn_i]); + free(mc_mn_Vars->Mon2D_p2[mc_mn_i]); + } + free(mc_mn_Vars->Mon2D_N); + free(mc_mn_Vars->Mon2D_p); + free(mc_mn_Vars->Mon2D_p2); + } + + + /* 2D case : mc_mn_Vars->Coord_Number==2 and !mc_mn_Vars->Flag_Multiple and !mc_mn_Vars->Flag_List */ + if ((mc_mn_Vars->Coord_Number == 2) && !mc_mn_Vars->Flag_Multiple) + { /* Dim : mc_mn_Vars->Coord_Bin[1]*mc_mn_Vars->Coord_Bin[2] matrix */ + for (mc_mn_i= 0; mc_mn_i < mc_mn_Vars->Coord_Bin[1]; mc_mn_i++) + { + free(mc_mn_Vars->Mon2D_N[mc_mn_i]); + free(mc_mn_Vars->Mon2D_p[mc_mn_i]); + free(mc_mn_Vars->Mon2D_p2[mc_mn_i]); + } + free(mc_mn_Vars->Mon2D_N); + free(mc_mn_Vars->Mon2D_p); + free(mc_mn_Vars->Mon2D_p2); + } + } /* end Monitor_nD_Finally */ + +/* ========================================================================= */ +/* ADD: E.Farhi, Aug 6th, 2001: Monitor_nD section */ +/* this routine is used to display component */ +/* ========================================================================= */ + +void Monitor_nD_McDisplay(MonitornD_Defines_type *mc_mn_DEFS, + MonitornD_Variables_type *mc_mn_Vars) + { + double mc_mn_radius, mc_mn_h; + double mc_mn_xmin; + double mc_mn_xmax; + double mc_mn_ymin; + double mc_mn_ymax; + double mc_mn_zmin; + double mc_mn_zmax; + int mc_mn_i; + double mc_mn_hdiv_min=-180, mc_mn_hdiv_max=180, mc_mn_vdiv_min=-180, mc_mn_vdiv_max=180; + char mc_mn_restricted = 0; + + mc_mn_radius = mc_mn_Vars->Sphere_Radius; + mc_mn_h = mc_mn_Vars->Cylinder_Height; + mc_mn_xmin = mc_mn_Vars->mxmin; + mc_mn_xmax = mc_mn_Vars->mxmax; + mc_mn_ymin = mc_mn_Vars->mymin; + mc_mn_ymax = mc_mn_Vars->mymax; + mc_mn_zmin = mc_mn_Vars->mzmin; + mc_mn_zmax = mc_mn_Vars->mzmax; + + /* determine if there are angular limits set at start (no auto) in coord_types + * cylinder/banana: look for hdiv + * sphere: look for angle, radius (->atan2(val,mc_mn_radius)), hdiv, vdiv + * this activates a 'restricted' flag, to draw a region as blades on cylinder/sphere + */ + for (mc_mn_i= 0; mc_mn_i < mc_mn_Vars->Coord_Number; mc_mn_i++) + { + int mc_mn_Set_Vars_Coord_Type; + mc_mn_Set_Vars_Coord_Type = (mc_mn_Vars->Coord_Type[mc_mn_i] & 31); + if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_HDIV || mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_THETA) + { mc_mn_hdiv_min = mc_mn_Vars->Coord_Min[mc_mn_i]; mc_mn_hdiv_max = mc_mn_Vars->Coord_Max[mc_mn_i]; mc_mn_restricted = 1; } + else if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_VDIV || mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_PHI) + { mc_mn_vdiv_min = mc_mn_Vars->Coord_Min[mc_mn_i]; mc_mn_vdiv_max = mc_mn_Vars->Coord_Max[mc_mn_i];mc_mn_restricted = 1; } + else if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_ANGLE) + { mc_mn_hdiv_min = mc_mn_vdiv_min = mc_mn_Vars->Coord_Min[mc_mn_i]; + mc_mn_hdiv_max = mc_mn_vdiv_max = mc_mn_Vars->Coord_Max[mc_mn_i]; + mc_mn_restricted = 1; } + else if (mc_mn_Set_Vars_Coord_Type == mc_mn_DEFS->COORD_RADIUS) + { double angle; + angle = RAD2DEG*atan2(mc_mn_Vars->Coord_Max[mc_mn_i], mc_mn_radius); + mc_mn_hdiv_min = mc_mn_vdiv_min = angle; + mc_mn_hdiv_max = mc_mn_vdiv_max = angle; + mc_mn_restricted = 1; } + } + + if (!mc_mn_restricted && (abs(mc_mn_Vars->Flag_Shape) == mc_mn_DEFS->SHAPE_SPHERE)) + { + mcdis_magnify(""); + mcdis_circle("xy",0,0,0,mc_mn_radius); + mcdis_circle("xz",0,0,0,mc_mn_radius); + mcdis_circle("yz",0,0,0,mc_mn_radius); + } + else if (mc_mn_restricted && ((abs(mc_mn_Vars->Flag_Shape) == mc_mn_DEFS->SHAPE_CYLIND) || (abs(mc_mn_Vars->Flag_Shape) == mc_mn_DEFS->SHAPE_BANANA) || (abs(mc_mn_Vars->Flag_Shape) == mc_mn_DEFS->SHAPE_SPHERE))) + { + int NH=24, NV=24; + int ih, iv; + double width, height; + int issphere; + issphere = (abs(mc_mn_Vars->Flag_Shape) == mc_mn_DEFS->SHAPE_SPHERE); + width = (mc_mn_hdiv_max-mc_mn_hdiv_min)/NH; + height= (mc_mn_vdiv_max-mc_mn_vdiv_min)/NV; + mcdis_magnify("xyz"); + for(ih = 0; ih < NH; ih++) + for(iv = 0; iv < NV; iv++) + { + double theta0, phi0, theta1, phi1; + double x0,y0,z0,x1,y1,z1,x2,y2,z2,x3,y3,z3; + double ymin, ymax; + phi0 = (mc_mn_hdiv_min+ width*ih)*DEG2RAD; /* in xz plane */ + phi1 = (mc_mn_hdiv_min+ width*(ih+1))*DEG2RAD; + if (issphere) + { + theta0= (90-mc_mn_vdiv_min+height*iv)*DEG2RAD; + theta1= (90-mc_mn_vdiv_min+height*(iv+1))*DEG2RAD; + } else + { + theta0= theta1 = PI/2; + ymin = mc_mn_ymin+(mc_mn_ymax-mc_mn_ymin)*(iv/NV); + ymax = mc_mn_ymin+(mc_mn_ymax-mc_mn_ymin)*((iv+1)/NV); + } + z0 = mc_mn_radius*sin(theta0)*cos(phi0); + x0 = mc_mn_radius*sin(theta0)*sin(phi0); + if (issphere) y0 = mc_mn_radius*cos(theta0); else y0 = ymin; + z1 = mc_mn_radius*sin(theta1)*cos(phi0); + x1 = mc_mn_radius*sin(theta1)*sin(phi0); + if (issphere) y1 = mc_mn_radius*cos(theta1); else y1 = ymax; + z2 = mc_mn_radius*sin(theta1)*cos(phi1); + x2 = mc_mn_radius*sin(theta1)*sin(phi1); + y2 = y1; + z3 = mc_mn_radius*sin(theta0)*cos(phi1); + x3 = mc_mn_radius*sin(theta0)*sin(phi1); + y3 = y0; + mcdis_multiline(5, + x0,y0,z0, + x1,y1,z1, + x2,y2,z2, + x3,y3,z3, + x0,y0,z0); + } + } + else + if (abs(mc_mn_Vars->Flag_Shape) == mc_mn_DEFS->SHAPE_DISK) + { + mcdis_magnify(""); + mcdis_circle("xy",0,0,0,mc_mn_radius); + } + else + if (abs(mc_mn_Vars->Flag_Shape) == mc_mn_DEFS->SHAPE_SQUARE) + { + mcdis_magnify("xy"); + mcdis_multiline(5, (double)mc_mn_xmin, (double)mc_mn_ymin, 0.0, + (double)mc_mn_xmax, (double)mc_mn_ymin, 0.0, + (double)mc_mn_xmax, (double)mc_mn_ymax, 0.0, + (double)mc_mn_xmin, (double)mc_mn_ymax, 0.0, + (double)mc_mn_xmin, (double)mc_mn_ymin, 0.0); + } + else + if (!mc_mn_restricted && ((abs(mc_mn_Vars->Flag_Shape) == mc_mn_DEFS->SHAPE_CYLIND) || (abs(mc_mn_Vars->Flag_Shape) == mc_mn_DEFS->SHAPE_BANANA))) + { + mcdis_magnify("xyz"); + mcdis_circle("xz", 0, mc_mn_h/2.0, 0, mc_mn_radius); + mcdis_circle("xz", 0, -mc_mn_h/2.0, 0, mc_mn_radius); + mcdis_line(-mc_mn_radius, -mc_mn_h/2.0, 0, -mc_mn_radius, +mc_mn_h/2.0, 0); + mcdis_line(+mc_mn_radius, -mc_mn_h/2.0, 0, +mc_mn_radius, +mc_mn_h/2.0, 0); + mcdis_line(0, -mc_mn_h/2.0, -mc_mn_radius, 0, +mc_mn_h/2.0, -mc_mn_radius); + mcdis_line(0, -mc_mn_h/2.0, +mc_mn_radius, 0, +mc_mn_h/2.0, +mc_mn_radius); + } + else + if (abs(mc_mn_Vars->Flag_Shape) == mc_mn_DEFS->SHAPE_BOX) + { + mcdis_magnify("xyz"); + mcdis_multiline(5, mc_mn_xmin, mc_mn_ymin, mc_mn_zmin, + mc_mn_xmax, mc_mn_ymin, mc_mn_zmin, + mc_mn_xmax, mc_mn_ymax, mc_mn_zmin, + mc_mn_xmin, mc_mn_ymax, mc_mn_zmin, + mc_mn_xmin, mc_mn_ymin, mc_mn_zmin); + mcdis_multiline(5, mc_mn_xmin, mc_mn_ymin, mc_mn_zmax, + mc_mn_xmax, mc_mn_ymin, mc_mn_zmax, + mc_mn_xmax, mc_mn_ymax, mc_mn_zmax, + mc_mn_xmin, mc_mn_ymax, mc_mn_zmax, + mc_mn_xmin, mc_mn_ymin, mc_mn_zmax); + mcdis_line(mc_mn_xmin, mc_mn_ymin, mc_mn_zmin, mc_mn_xmin, mc_mn_ymin, mc_mn_zmax); + mcdis_line(mc_mn_xmax, mc_mn_ymin, mc_mn_zmin, mc_mn_xmax, mc_mn_ymin, mc_mn_zmax); + mcdis_line(mc_mn_xmin, mc_mn_ymax, mc_mn_zmin, mc_mn_xmin, mc_mn_ymax, mc_mn_zmax); + mcdis_line(mc_mn_xmax, mc_mn_ymax, mc_mn_zmin, mc_mn_xmax, mc_mn_ymax, mc_mn_zmax); + } + } /* end Monitor_nD_McDisplay */ + +/* end of monitor_nd-lib.c */ + +#line 6873 "dmcafter.c" +#line 213 "/afs/psi.ch/project/sinq/sl-linux/lib/mcstas/monitors/Monitor_nD.comp" + MonitornD_Defines_type DEFS; + MonitornD_Variables_type Vars; +#line 6877 "dmcafter.c" +#undef zmax +#undef zmin +#undef ymax +#undef ymin +#undef xmax +#undef xmin +#undef zthick +#undef yheight +#undef xwidth +#undef Vars +#undef DEFS +#undef filename +#undef options +#undef mccompcurname +#undef mccompcurindex + +Coords mcposamsa, mcposrmsa; +Rotation mcrotamsa, mcrotrmsa; +Coords mcposain, mcposrin; +Rotation mcrotain, mcrotrin; +Coords mcposaout2_slit, mcposrout2_slit; +Rotation mcrotaout2_slit, mcrotrout2_slit; +Coords mcposaPSD_sample, mcposrPSD_sample; +Rotation mcrotaPSD_sample, mcrotrPSD_sample; +Coords mcposasa_arm, mcposrsa_arm; +Rotation mcrotasa_arm, mcrotrsa_arm; +Coords mcposasample, mcposrsample; +Rotation mcrotasample, mcrotrsample; +Coords mcposaDet9, mcposrDet9; +Rotation mcrotaDet9, mcrotrDet9; + +MCNUM mcnx, mcny, mcnz, mcnvx, mcnvy, mcnvz, mcnt, mcnsx, mcnsy, mcnsz, mcnp; + +void mcinit(void) { +#define Det_start mcipDet_start +#define samplefile mcipsamplefile +#define monfile mcipmonfile +#define lambdafile mciplambdafile +#define repeat mciprepeat +#line 13 "dmcafter.instr" +{ +Det_end= Det_start + 80; +sprintf(option_list,"banana theta limits [%f %f] bins=400, file=det9.dat",Det_start,Det_end); +printf("%s \n",option_list); +} +#line 6923 "dmcafter.c" +#undef repeat +#undef lambdafile +#undef monfile +#undef samplefile +#undef Det_start + /* Computation of coordinate transformations. */ + { + Coords mctc1, mctc2; + Rotation mctr1; + + mcDEBUG_INSTR() + /* Component msa. */ + strcpy(mcsig_message, "msa (Init:Place/Rotate)"); + rot_set_rotation(mcrotamsa, + (0.0)*DEG2RAD, + (0.0)*DEG2RAD, + (0.0)*DEG2RAD); +#line 6941 "dmcafter.c" + rot_copy(mcrotrmsa, mcrotamsa); + mcposamsa = coords_set( +#line 23 "dmcafter.instr" + 0, +#line 23 "dmcafter.instr" + 0, +#line 23 "dmcafter.instr" + 0); +#line 6950 "dmcafter.c" + mctc1 = coords_neg(mcposamsa); + mcposrmsa = rot_apply(mcrotamsa, mctc1); + mcDEBUG_COMPONENT("msa", mcposamsa, mcrotamsa) + mccomp_posa[1] = mcposamsa; + mccomp_posr[1] = mcposrmsa; + /* Component in. */ + strcpy(mcsig_message, "in (Init:Place/Rotate)"); + rot_set_rotation(mctr1, +#line 31 "dmcafter.instr" + (0)*DEG2RAD, +#line 31 "dmcafter.instr" + (0)*DEG2RAD, +#line 31 "dmcafter.instr" + (0)*DEG2RAD); +#line 6965 "dmcafter.c" + rot_mul(mctr1, mcrotamsa, mcrotain); + rot_transpose(mcrotamsa, mctr1); + rot_mul(mcrotain, mctr1, mcrotrin); + mctc1 = coords_set( +#line 31 "dmcafter.instr" + 0, +#line 31 "dmcafter.instr" + 0, +#line 31 "dmcafter.instr" + 0.64); +#line 6976 "dmcafter.c" + rot_transpose(mcrotamsa, mctr1); + mctc2 = rot_apply(mctr1, mctc1); + mcposain = coords_add(mcposamsa, mctc2); + mctc1 = coords_sub(mcposamsa, mcposain); + mcposrin = rot_apply(mcrotain, mctc1); + mcDEBUG_COMPONENT("in", mcposain, mcrotain) + mccomp_posa[2] = mcposain; + mccomp_posr[2] = mcposrin; + /* Component out2_slit. */ + strcpy(mcsig_message, "out2_slit (Init:Place/Rotate)"); + rot_set_rotation(mctr1, +#line 35 "dmcafter.instr" + (0)*DEG2RAD, +#line 35 "dmcafter.instr" + (0)*DEG2RAD, +#line 35 "dmcafter.instr" + (0)*DEG2RAD); +#line 6994 "dmcafter.c" + rot_mul(mctr1, mcrotamsa, mcrotaout2_slit); + rot_transpose(mcrotain, mctr1); + rot_mul(mcrotaout2_slit, mctr1, mcrotrout2_slit); + mctc1 = coords_set( +#line 35 "dmcafter.instr" + 0, +#line 35 "dmcafter.instr" + 0, +#line 35 "dmcafter.instr" + 0.65); +#line 7005 "dmcafter.c" + rot_transpose(mcrotamsa, mctr1); + mctc2 = rot_apply(mctr1, mctc1); + mcposaout2_slit = coords_add(mcposamsa, mctc2); + mctc1 = coords_sub(mcposain, mcposaout2_slit); + mcposrout2_slit = rot_apply(mcrotaout2_slit, mctc1); + mcDEBUG_COMPONENT("out2_slit", mcposaout2_slit, mcrotaout2_slit) + mccomp_posa[3] = mcposaout2_slit; + mccomp_posr[3] = mcposrout2_slit; + /* Component PSD_sample. */ + strcpy(mcsig_message, "PSD_sample (Init:Place/Rotate)"); + rot_set_rotation(mctr1, + (0.0)*DEG2RAD, + (0.0)*DEG2RAD, + (0.0)*DEG2RAD); +#line 7020 "dmcafter.c" + rot_mul(mctr1, mcrotamsa, mcrotaPSD_sample); + rot_transpose(mcrotaout2_slit, mctr1); + rot_mul(mcrotaPSD_sample, mctr1, mcrotrPSD_sample); + mctc1 = coords_set( +#line 39 "dmcafter.instr" + 0, +#line 39 "dmcafter.instr" + 0, +#line 39 "dmcafter.instr" + 1.5); +#line 7031 "dmcafter.c" + rot_transpose(mcrotamsa, mctr1); + mctc2 = rot_apply(mctr1, mctc1); + mcposaPSD_sample = coords_add(mcposamsa, mctc2); + mctc1 = coords_sub(mcposaout2_slit, mcposaPSD_sample); + mcposrPSD_sample = rot_apply(mcrotaPSD_sample, mctc1); + mcDEBUG_COMPONENT("PSD_sample", mcposaPSD_sample, mcrotaPSD_sample) + mccomp_posa[4] = mcposaPSD_sample; + mccomp_posr[4] = mcposrPSD_sample; + /* Component sa_arm. */ + strcpy(mcsig_message, "sa_arm (Init:Place/Rotate)"); + rot_set_rotation(mctr1, +#line 53 "dmcafter.instr" + (0)*DEG2RAD, +#line 53 "dmcafter.instr" + (0)*DEG2RAD, +#line 53 "dmcafter.instr" + (0)*DEG2RAD); +#line 7049 "dmcafter.c" + rot_mul(mctr1, mcrotamsa, mcrotasa_arm); + rot_transpose(mcrotaPSD_sample, mctr1); + rot_mul(mcrotasa_arm, mctr1, mcrotrsa_arm); + mctc1 = coords_set( +#line 52 "dmcafter.instr" + 0, +#line 52 "dmcafter.instr" + 0, +#line 52 "dmcafter.instr" + 2.82); +#line 7060 "dmcafter.c" + rot_transpose(mcrotamsa, mctr1); + mctc2 = rot_apply(mctr1, mctc1); + mcposasa_arm = coords_add(mcposamsa, mctc2); + mctc1 = coords_sub(mcposaPSD_sample, mcposasa_arm); + mcposrsa_arm = rot_apply(mcrotasa_arm, mctc1); + mcDEBUG_COMPONENT("sa_arm", mcposasa_arm, mcrotasa_arm) + mccomp_posa[5] = mcposasa_arm; + mccomp_posr[5] = mcposrsa_arm; + /* Component sample. */ + strcpy(mcsig_message, "sample (Init:Place/Rotate)"); + rot_set_rotation(mctr1, + (0.0)*DEG2RAD, + (0.0)*DEG2RAD, + (0.0)*DEG2RAD); +#line 7075 "dmcafter.c" + rot_mul(mctr1, mcrotasa_arm, mcrotasample); + rot_transpose(mcrotasa_arm, mctr1); + rot_mul(mcrotasample, mctr1, mcrotrsample); + mctc1 = coords_set( +#line 59 "dmcafter.instr" + 0, +#line 59 "dmcafter.instr" + 0, +#line 59 "dmcafter.instr" + 0); +#line 7086 "dmcafter.c" + rot_transpose(mcrotasa_arm, mctr1); + mctc2 = rot_apply(mctr1, mctc1); + mcposasample = coords_add(mcposasa_arm, mctc2); + mctc1 = coords_sub(mcposasa_arm, mcposasample); + mcposrsample = rot_apply(mcrotasample, mctc1); + mcDEBUG_COMPONENT("sample", mcposasample, mcrotasample) + mccomp_posa[6] = mcposasample; + mccomp_posr[6] = mcposrsample; + /* Component Det9. */ + strcpy(mcsig_message, "Det9 (Init:Place/Rotate)"); + rot_set_rotation(mctr1, +#line 65 "dmcafter.instr" + (0)*DEG2RAD, +#line 65 "dmcafter.instr" + (0)*DEG2RAD, +#line 65 "dmcafter.instr" + (180)*DEG2RAD); +#line 7104 "dmcafter.c" + rot_mul(mctr1, mcrotasa_arm, mcrotaDet9); + rot_transpose(mcrotasample, mctr1); + rot_mul(mcrotaDet9, mctr1, mcrotrDet9); + mctc1 = coords_set( +#line 64 "dmcafter.instr" + 0, +#line 64 "dmcafter.instr" + 0, +#line 64 "dmcafter.instr" + 0.000001); +#line 7115 "dmcafter.c" + rot_transpose(mcrotasa_arm, mctr1); + mctc2 = rot_apply(mctr1, mctc1); + mcposaDet9 = coords_add(mcposasa_arm, mctc2); + mctc1 = coords_sub(mcposasample, mcposaDet9); + mcposrDet9 = rot_apply(mcrotaDet9, mctc1); + mcDEBUG_COMPONENT("Det9", mcposaDet9, mcrotaDet9) + mccomp_posa[7] = mcposaDet9; + mccomp_posr[7] = mcposrDet9; + /* Component initializations. */ + /* Initializations for component msa. */ + strcpy(mcsig_message, "msa (Init)"); + + + /* Initializations for component in. */ + strcpy(mcsig_message, "in (Init)"); +#line 30 "dmcafter.instr" + mccin_repeat_count = mciprepeat; +#line 68 "dmcafter.instr" + mccin_bufsize = 0; +#line 7135 "dmcafter.c" + +#define mccompcurname in +#define mccompcurindex 2 +#define file mccin_file +#define type mccin_type +#define rep mccin_rep +#define pos mccin_pos +#define nrows mccin_nrows +#define nread mccin_nread +#define Offset mccin_Offset +#define rTable mccin_rTable +#define Virtual_input_Read_Input mccin_Virtual_input_Read_Input +{ /* Declarations of SETTING parameters. */ +MCNUM repeat_count = mccin_repeat_count; +MCNUM bufsize = mccin_bufsize; +#line 112 "/afs/psi.ch/project/sinq/sl-linux/lib/mcstas/sources/Virtual_input.comp" +{ + Table_Init(&rTable); + rep = repeat_count+1; + + if (!file || !repeat_count) + { + fprintf(stderr,"Virtual_input: %s: please give me a file name (file) to read (repeat_count>0).\n", NAME_CURRENT_COMP); + exit(-1); + } + if (type && strstr(type, "Vitess")) + { fprintf(stderr, "Virtual_input: %s: Vitess files may be read using the Vitess_input component\n", NAME_CURRENT_COMP); exit(-1); } + + if (bufsize) mcset_ncount(bufsize*repeat_count); + + printf("Virtual_input: %s: Reading neutron events from file '%s'. Repeat %g time(s)\n", NAME_CURRENT_COMP, file, repeat_count); +} +#line 7168 "dmcafter.c" +} /* End of SETTING parameter declarations. */ +#undef Virtual_input_Read_Input +#undef rTable +#undef Offset +#undef nread +#undef nrows +#undef pos +#undef rep +#undef type +#undef file +#undef mccompcurname +#undef mccompcurindex + + /* Initializations for component out2_slit. */ + strcpy(mcsig_message, "out2_slit (Init)"); +#line 34 "dmcafter.instr" + mccout2_slit_xmin = -0.01; +#line 34 "dmcafter.instr" + mccout2_slit_xmax = 0.01; +#line 34 "dmcafter.instr" + mccout2_slit_ymin = -0.06; +#line 34 "dmcafter.instr" + mccout2_slit_ymax = 0.06; +#line 42 "dmcafter.instr" + mccout2_slit_radius = 0; +#line 7194 "dmcafter.c" + +#define mccompcurname out2_slit +#define mccompcurindex 3 +{ /* Declarations of SETTING parameters. */ +MCNUM xmin = mccout2_slit_xmin; +MCNUM xmax = mccout2_slit_xmax; +MCNUM ymin = mccout2_slit_ymin; +MCNUM ymax = mccout2_slit_ymax; +MCNUM radius = mccout2_slit_radius; +#line 46 "/afs/psi.ch/project/sinq/sl-linux/lib/mcstas/optics/Slit.comp" +{ + if (xmin == 0 && xmax == 0 && ymin == 0 & ymax == 0 && radius == 0) + { fprintf(stderr,"Slit: %s: Error: give geometry\n", NAME_CURRENT_COMP); exit(-1); } + +} +#line 7210 "dmcafter.c" +} /* End of SETTING parameter declarations. */ +#undef mccompcurname +#undef mccompcurindex + + /* Initializations for component PSD_sample. */ + strcpy(mcsig_message, "PSD_sample (Init)"); +#line 38 "dmcafter.instr" + mccPSD_sample_xmin = -0.05; +#line 38 "dmcafter.instr" + mccPSD_sample_xmax = 0.05; +#line 38 "dmcafter.instr" + mccPSD_sample_ymin = -0.07; +#line 38 "dmcafter.instr" + mccPSD_sample_ymax = 0.07; +#line 38 "dmcafter.instr" + if(mcipmonfile) strncpy(mccPSD_sample_controlfile,mcipmonfile, 1024); else mccPSD_sample_controlfile[0]='\0'; +#line 45 "dmcafter.instr" + mccPSD_sample_dumpCount = 1000; +#line 7229 "dmcafter.c" + +#define mccompcurname PSD_sample +#define mccompcurindex 4 +#define Nsum mccPSD_sample_Nsum +#define psum mccPSD_sample_psum +#define p2sum mccPSD_sample_p2sum +#define currentCount mccPSD_sample_currentCount +{ /* Declarations of SETTING parameters. */ +MCNUM xmin = mccPSD_sample_xmin; +MCNUM xmax = mccPSD_sample_xmax; +MCNUM ymin = mccPSD_sample_ymin; +MCNUM ymax = mccPSD_sample_ymax; +char* controlfile = mccPSD_sample_controlfile; +int dumpCount = mccPSD_sample_dumpCount; +#line 72 "MKMonitor.comp" +{ + psum = 0; + p2sum = 0; + Nsum = 0; + currentCount = 0; +} +#line 7251 "dmcafter.c" +} /* End of SETTING parameter declarations. */ +#undef currentCount +#undef p2sum +#undef psum +#undef Nsum +#undef mccompcurname +#undef mccompcurindex + + /* Initializations for component sa_arm. */ + strcpy(mcsig_message, "sa_arm (Init)"); + + + /* Initializations for component sample. */ + strcpy(mcsig_message, "sample (Init)"); +#line 56 "dmcafter.instr" + mccsample_d_phi0 = 0; +#line 56 "dmcafter.instr" + mccsample_radius = sample_radius; +#line 61 "dmcafter.instr" + mccsample_focus_r = 0; +#line 56 "dmcafter.instr" + mccsample_h = sample_height; +#line 57 "dmcafter.instr" + mccsample_pack = 1; +#line 57 "dmcafter.instr" + mccsample_Vc = 1076.98; +#line 58 "dmcafter.instr" + mccsample_sigma_a = 0.2; +#line 62 "dmcafter.instr" + mccsample_sigma_inc = 0; +#line 63 "dmcafter.instr" + mccsample_frac = 0; +#line 63 "dmcafter.instr" + mccsample_focus_xw = 0; +#line 63 "dmcafter.instr" + mccsample_focus_yh = 0; +#line 58 "dmcafter.instr" + mccsample_focus_aw = 80; +#line 58 "dmcafter.instr" + mccsample_focus_ah = 3.5; +#line 64 "dmcafter.instr" + mccsample_target_x = 0; +#line 64 "dmcafter.instr" + mccsample_target_y = 0; +#line 64 "dmcafter.instr" + mccsample_target_z = 0; +#line 58 "dmcafter.instr" + mccsample_target_index = + 1; +#line 7300 "dmcafter.c" + +#define mccompcurname sample +#define mccompcurindex 6 +#define reflections mccsample_reflections +#define my_s_v2 mccsample_my_s_v2 +#define my_a_v mccsample_my_a_v +#define q_v mccsample_q_v +{ /* Declarations of SETTING parameters. */ +MCNUM d_phi0 = mccsample_d_phi0; +MCNUM radius = mccsample_radius; +MCNUM focus_r = mccsample_focus_r; +MCNUM h = mccsample_h; +MCNUM pack = mccsample_pack; +MCNUM Vc = mccsample_Vc; +MCNUM sigma_a = mccsample_sigma_a; +MCNUM sigma_inc = mccsample_sigma_inc; +MCNUM frac = mccsample_frac; +MCNUM focus_xw = mccsample_focus_xw; +MCNUM focus_yh = mccsample_focus_yh; +MCNUM focus_aw = mccsample_focus_aw; +MCNUM focus_ah = mccsample_focus_ah; +MCNUM target_x = mccsample_target_x; +MCNUM target_y = mccsample_target_y; +MCNUM target_z = mccsample_target_z; +int target_index = mccsample_target_index; +#line 148 "PowderN.comp" +{ + struct line_data *L; + read_line_data(reflections, &line_info); + L = line_info.list; + + Nq = line_info.count; + my_s_v2 = malloc(Nq*sizeof(double)); + q_v = malloc(Nq*sizeof(double)); + w_v = malloc(Nq*sizeof(double)); + int i; + my_a_v = pack*sigma_a/Vc*2200; /* Is not yet divided by v */ + my_inc = pack*sigma_inc/Vc; + my_s_v2_sum=0; + + for(i=0; i= repeat_count) { + nrows = Virtual_input_Read_Input(file, type, &rTable, &Offset); + if (nrows) { rep = 0; pos = 0; nread++; } + } + + if (!nrows) + { mcset_ncount(mcget_run_num()); ABSORB; } + /* for each buffer, loop: repeat counts */ + /* for each repeat count: loop: generate neutrons */ + if (pos >= nrows) + { rep++; pos = 0; } /* Reposition at start of buffer */ + + /* &p, &x, &y, &z, &vx, &vy, &vz, &t, &sx, &sy, &sz */ + mcrestore_neutron(rTable.data,pos, &x, &y, &z, &vx, &vy, &vz, &t, &sx, &sy, &sz, &p); + + pos++; + p /= repeat_count; + SCATTER; +} +#line 7523 "dmcafter.c" +} /* End of SETTING parameter declarations. */ +#undef Virtual_input_Read_Input +#undef rTable +#undef Offset +#undef nread +#undef nrows +#undef pos +#undef rep +#undef type +#undef file +#undef mccompcurname +#undef mccompcurindex +#undef sz +#undef sy +#undef sx +#undef p +#undef s2 +#undef s1 +#undef t +#undef vz +#undef vy +#undef vx +#undef z +#undef y +#undef x + mcDEBUG_STATE(mcnlx, mcnly, mcnlz, mcnlvx, mcnlvy, mcnlvz,mcnlt,mcnlsx,mcnlsy, mcnlp) + + /* Component out2_slit. */ + strcpy(mcsig_message, "out2_slit (Trace)"); + mcDEBUG_COMP("out2_slit") + mccoordschange(mcposrout2_slit, mcrotrout2_slit, + &mcnlx, &mcnly, &mcnlz, + &mcnlvx, &mcnlvy, &mcnlvz, + &mcnlt, &mcnlsx, &mcnlsy); + mccoordschange_polarisation(mcrotrout2_slit, &mcnlsx, &mcnlsy, &mcnlsz); + mcDEBUG_STATE(mcnlx, mcnly, mcnlz, mcnlvx, mcnlvy, mcnlvz,mcnlt,mcnlsx,mcnlsy, mcnlp) +#define x mcnlx +#define y mcnly +#define z mcnlz +#define vx mcnlvx +#define vy mcnlvy +#define vz mcnlvz +#define t mcnlt +#define s1 mcnlsx +#define s2 mcnlsy +#define p mcnlp + STORE_NEUTRON(3,mcnlx, mcnly, mcnlz, mcnlvx,mcnlvy,mcnlvz,mcnlt,mcnlsx,mcnlsy, mcnlsz, mcnlp); + mcScattered=0; +#define mccompcurname out2_slit +#define mccompcurindex 3 +{ /* Declarations of SETTING parameters. */ +MCNUM xmin = mccout2_slit_xmin; +MCNUM xmax = mccout2_slit_xmax; +MCNUM ymin = mccout2_slit_ymin; +MCNUM ymax = mccout2_slit_ymax; +MCNUM radius = mccout2_slit_radius; +#line 53 "/afs/psi.ch/project/sinq/sl-linux/lib/mcstas/optics/Slit.comp" +{ + PROP_Z0; + if (((radius == 0) && (xxmax || yymax)) + || ((radius != 0) && (x*x + y*y > radius*radius))) + ABSORB; + else + SCATTER; +} +#line 7589 "dmcafter.c" +} /* End of SETTING parameter declarations. */ +#undef mccompcurname +#undef mccompcurindex +#undef p +#undef s2 +#undef s1 +#undef t +#undef vz +#undef vy +#undef vx +#undef z +#undef y +#undef x + mcDEBUG_STATE(mcnlx, mcnly, mcnlz, mcnlvx, mcnlvy, mcnlvz,mcnlt,mcnlsx,mcnlsy, mcnlp) + + /* Component PSD_sample. */ + strcpy(mcsig_message, "PSD_sample (Trace)"); + mcDEBUG_COMP("PSD_sample") + mccoordschange(mcposrPSD_sample, mcrotrPSD_sample, + &mcnlx, &mcnly, &mcnlz, + &mcnlvx, &mcnlvy, &mcnlvz, + &mcnlt, &mcnlsx, &mcnlsy); + mccoordschange_polarisation(mcrotrPSD_sample, &mcnlsx, &mcnlsy, &mcnlsz); + mcDEBUG_STATE(mcnlx, mcnly, mcnlz, mcnlvx, mcnlvy, mcnlvz,mcnlt,mcnlsx,mcnlsy, mcnlp) +#define x mcnlx +#define y mcnly +#define z mcnlz +#define vx mcnlvx +#define vy mcnlvy +#define vz mcnlvz +#define t mcnlt +#define s1 mcnlsx +#define s2 mcnlsy +#define p mcnlp + STORE_NEUTRON(4,mcnlx, mcnly, mcnlz, mcnlvx,mcnlvy,mcnlvz,mcnlt,mcnlsx,mcnlsy, mcnlsz, mcnlp); + mcScattered=0; +#define mccompcurname PSD_sample +#define mccompcurindex 4 +#define Nsum mccPSD_sample_Nsum +#define psum mccPSD_sample_psum +#define p2sum mccPSD_sample_p2sum +#define currentCount mccPSD_sample_currentCount +{ /* Declarations of SETTING parameters. */ +MCNUM xmin = mccPSD_sample_xmin; +MCNUM xmax = mccPSD_sample_xmax; +MCNUM ymin = mccPSD_sample_ymin; +MCNUM ymax = mccPSD_sample_ymax; +char* controlfile = mccPSD_sample_controlfile; +int dumpCount = mccPSD_sample_dumpCount; +#line 79 "MKMonitor.comp" +{ + PROP_Z0; + if (x>xmin && xymin && y 0 && currentCount > dumpCount){ + dumpTotal(controlfile,(long)Nsum); + currentCount = 0; + } + SCATTER; + } +} +#line 7655 "dmcafter.c" +} /* End of SETTING parameter declarations. */ +#undef currentCount +#undef p2sum +#undef psum +#undef Nsum +#undef mccompcurname +#undef mccompcurindex +#undef p +#undef s2 +#undef s1 +#undef t +#undef vz +#undef vy +#undef vx +#undef z +#undef y +#undef x + mcDEBUG_STATE(mcnlx, mcnly, mcnlz, mcnlvx, mcnlvy, mcnlvz,mcnlt,mcnlsx,mcnlsy, mcnlp) + + /* Component sa_arm. */ + strcpy(mcsig_message, "sa_arm (Trace)"); + mcDEBUG_COMP("sa_arm") + mccoordschange(mcposrsa_arm, mcrotrsa_arm, + &mcnlx, &mcnly, &mcnlz, + &mcnlvx, &mcnlvy, &mcnlvz, + &mcnlt, &mcnlsx, &mcnlsy); + mccoordschange_polarisation(mcrotrsa_arm, &mcnlsx, &mcnlsy, &mcnlsz); + mcDEBUG_STATE(mcnlx, mcnly, mcnlz, mcnlvx, mcnlvy, mcnlvz,mcnlt,mcnlsx,mcnlsy, mcnlp) + STORE_NEUTRON(5,mcnlx, mcnly, mcnlz, mcnlvx,mcnlvy,mcnlvz,mcnlt,mcnlsx,mcnlsy, mcnlsz, mcnlp); + mcScattered=0; +#define mccompcurname sa_arm +#define mccompcurindex 5 +#undef mccompcurname +#undef mccompcurindex + mcDEBUG_STATE(mcnlx, mcnly, mcnlz, mcnlvx, mcnlvy, mcnlvz,mcnlt,mcnlsx,mcnlsy, mcnlp) + + /* Component sample. */ + strcpy(mcsig_message, "sample (Trace)"); + mcDEBUG_COMP("sample") + mccoordschange(mcposrsample, mcrotrsample, + &mcnlx, &mcnly, &mcnlz, + &mcnlvx, &mcnlvy, &mcnlvz, + &mcnlt, &mcnlsx, &mcnlsy); + mccoordschange_polarisation(mcrotrsample, &mcnlsx, &mcnlsy, &mcnlsz); + mcDEBUG_STATE(mcnlx, mcnly, mcnlz, mcnlvx, mcnlvy, mcnlvz,mcnlt,mcnlsx,mcnlsy, mcnlp) +#define x mcnlx +#define y mcnly +#define z mcnlz +#define vx mcnlvx +#define vy mcnlvy +#define vz mcnlvz +#define t mcnlt +#define s1 mcnlsx +#define s2 mcnlsy +#define p mcnlp + STORE_NEUTRON(6,mcnlx, mcnly, mcnlz, mcnlvx,mcnlvy,mcnlvz,mcnlt,mcnlsx,mcnlsy, mcnlsz, mcnlp); + mcScattered=0; +#define mccompcurname sample +#define mccompcurindex 6 +#define reflections mccsample_reflections +#define my_s_v2 mccsample_my_s_v2 +#define my_a_v mccsample_my_a_v +#define q_v mccsample_q_v +{ /* Declarations of SETTING parameters. */ +MCNUM d_phi0 = mccsample_d_phi0; +MCNUM radius = mccsample_radius; +MCNUM focus_r = mccsample_focus_r; +MCNUM h = mccsample_h; +MCNUM pack = mccsample_pack; +MCNUM Vc = mccsample_Vc; +MCNUM sigma_a = mccsample_sigma_a; +MCNUM sigma_inc = mccsample_sigma_inc; +MCNUM frac = mccsample_frac; +MCNUM focus_xw = mccsample_focus_xw; +MCNUM focus_yh = mccsample_focus_yh; +MCNUM focus_aw = mccsample_focus_aw; +MCNUM focus_ah = mccsample_focus_ah; +MCNUM target_x = mccsample_target_x; +MCNUM target_y = mccsample_target_y; +MCNUM target_z = mccsample_target_z; +int target_index = mccsample_target_index; +#line 172 "PowderN.comp" +{ + double t0, t1, v, v1,l_full, l, l_1, dt, d_phi, theta, my_s, my_s_n; + double aim_x, aim_y, aim_z, axis_x, axis_y, axis_z; + double arg, tmp_vx, tmp_vy, tmp_vz, p_in, vout_x, vout_y, vout_z; + int line; + + if(cylinder_intersect(&t0, &t1, x, y, z, vx, vy, vz, radius, h)) + { + if(t0 < 0) + ABSORB; + /* Neutron enters at t=t0. */ + v = sqrt(vx*vx + vy*vy + vz*vz); + l_full = v * (t1 - t0); /* Length of full path through sample */ + dt = rand01()*(t1 - t0); /* Time of scattering */ + PROP_DT(dt+t0); /* Point of scattering */ + l = v*dt; /* Penetration in sample */ + if (target_index){ + Coords ToTarget; + ToTarget = coords_sub(POS_A_COMP_INDEX(INDEX_CURRENT_COMP+target_index),POS_A_CURRENT_COMP); + ToTarget = rot_apply(ROT_A_CURRENT_COMP, ToTarget); + coords_get(ToTarget, &target_x, &target_y, &target_z); + } + aim_x = target_x-x; /* Vector pointing at target (anal./det.) */ + aim_y = target_y-y; + aim_z = target_z-z; + my_s = my_s_v2_sum/(v*v)+my_inc; + /* Total attenuation from scattering */ + + if (rand01() >= frac) + { /* Make coherent scattering event */ + /* Choose point on Debye-Scherrer cone */ + if (d_phi>0) + { + d_phi = d_phi0*DEG2RAD/2.0*randpm1(); + p *= d_phi0/360.0; + } + else + d_phi = 180*DEG2RAD*randpm1(); + line=floor(Nq*rand01()); /* Select between Nq powder lines */ + arg = (q_v[line]+w_v[line]*randnorm())/(2.0*v); + my_s_n = my_s_v2[line]/(v*v); + if(arg > 1) + ABSORB; /* No bragg scattering possible*/ + theta = asin(arg); /* Bragg scattering law */ + + vec_prod(axis_x, axis_y, axis_z, vx, vy, vz, aim_x, aim_y, aim_z); + rotate(tmp_vx, tmp_vy, tmp_vz, vx, vy, vz, 2*theta, axis_x, axis_y, axis_z); + rotate(vout_x, vout_y, vout_z, tmp_vx, tmp_vy, tmp_vz, d_phi, vx, vy, vz); + vx = vout_x; + vy = vout_y; + vz = vout_z; + + if(!cylinder_intersect(&t0, &t1, x, y, z, + vx, vy, vz, radius, h)) + { + /* Strange error: did not hit cylinder */ + printf("FATAL ERROR: Did not hit cylinder from inside.\n"); + exit(1); + } + l_1 = v*t1; + + p *= Nq*l_full*my_s_n*exp(-(my_a_v/v+my_s)*(l+l_1))/(1-frac); + /* printf("Powder p: %g , exp: %g\n",p,exp(-(my_a_v/v+my_s)*(l+l_1)));*/ + } /* Coherent scattering event */ + else + { /* Make incoherent scattering event */ + v = sqrt(vx*vx+vy*vy+vz*vz); + if(focus_aw && focus_ah) { + randvec_target_rect_angular(&vx, &vy, &vz, &solid_angle, + aim_x, aim_y, aim_z, focus_aw*DEG2RAD, focus_ah*DEG2RAD, ROT_A_CURRENT_COMP); + } else if(focus_xw && focus_yh) { + randvec_target_rect(&vx, &vy, &vz, &solid_angle, + aim_x, aim_y, aim_z, focus_xw, focus_yh, ROT_A_CURRENT_COMP); + } else { + randvec_target_sphere(&vx, &vy, &vz, &solid_angle, aim_x, aim_y, aim_z, focus_r); + } + v1 = sqrt(vx*vx+vy*vy+vz*vz); + vx *= v/v1; + vy *= v/v1; + vz *= v/v1; + if(!cylinder_intersect(&t0, &t1, x, y, z, + vx, vy, vz, radius, h)) + { + /* Strange error: did not hit cylinder */ + printf("FATAL ERROR: Did not hit cylinder from inside.\n"); + exit(1); + } + l_1 = v*t1; + + p_in=p; + p *= l_full*my_inc*exp(-(my_a_v/v+my_s)*(l+l_1))/(frac); + p *= solid_angle/(4*PI); + /* printf("Incoherent p_in: %g arg: %g l_1: %g t0: %g t1: %g p: %g \n", + p_in,(my_a_v/v+my_s)*(l+l_1),l_1,t0,t1,p); */ + } /* Incoherent scattering event */ + } + else + ABSORB; +} +#line 7837 "dmcafter.c" +} /* End of SETTING parameter declarations. */ +#undef q_v +#undef my_a_v +#undef my_s_v2 +#undef reflections +#undef mccompcurname +#undef mccompcurindex +#undef p +#undef s2 +#undef s1 +#undef t +#undef vz +#undef vy +#undef vx +#undef z +#undef y +#undef x + mcDEBUG_STATE(mcnlx, mcnly, mcnlz, mcnlvx, mcnlvy, mcnlvz,mcnlt,mcnlsx,mcnlsy, mcnlp) + + /* Component Det9. */ + strcpy(mcsig_message, "Det9 (Trace)"); + mcDEBUG_COMP("Det9") + mccoordschange(mcposrDet9, mcrotrDet9, + &mcnlx, &mcnly, &mcnlz, + &mcnlvx, &mcnlvy, &mcnlvz, + &mcnlt, &mcnlsx, &mcnlsy); + mccoordschange_polarisation(mcrotrDet9, &mcnlsx, &mcnlsy, &mcnlsz); + mcDEBUG_STATE(mcnlx, mcnly, mcnlz, mcnlvx, mcnlvy, mcnlvz,mcnlt,mcnlsx,mcnlsy, mcnlp) +#define x mcnlx +#define y mcnly +#define z mcnlz +#define vx mcnlvx +#define vy mcnlvy +#define vz mcnlvz +#define t mcnlt +#define s1 mcnlsx +#define s2 mcnlsy +#define p mcnlp +#define sx mcnlsx +#define sy mcnlsy +#define sz mcnlsz + STORE_NEUTRON(7,mcnlx, mcnly, mcnlz, mcnlvx,mcnlvy,mcnlvz,mcnlt,mcnlsx,mcnlsy, mcnlsz, mcnlp); + mcScattered=0; +#define mccompcurname Det9 +#define mccompcurindex 7 +#define options mccDet9_options +#define filename mccDet9_filename +#define DEFS mccDet9_DEFS +#define Vars mccDet9_Vars +{ /* Declarations of SETTING parameters. */ +MCNUM xwidth = mccDet9_xwidth; +MCNUM yheight = mccDet9_yheight; +MCNUM zthick = mccDet9_zthick; +MCNUM xmin = mccDet9_xmin; +MCNUM xmax = mccDet9_xmax; +MCNUM ymin = mccDet9_ymin; +MCNUM ymax = mccDet9_ymax; +MCNUM zmin = mccDet9_zmin; +MCNUM zmax = mccDet9_zmax; +#line 235 "/afs/psi.ch/project/sinq/sl-linux/lib/mcstas/monitors/Monitor_nD.comp" +{ + double XY=0; + double t0 = 0; + double t1 = 0; + double pp; + int intersect = 0; + char Flag_Restore = 0; + + if (abs(Vars.Flag_Shape) == DEFS.SHAPE_SQUARE) /* square xy */ + { + PROP_Z0; + intersect = (x>=Vars.mxmin && x<=Vars.mxmax && y>=Vars.mymin && y<=Vars.mymax); + } + else if (abs(Vars.Flag_Shape) == DEFS.SHAPE_DISK) /* disk xy */ + { + PROP_Z0; + intersect = ((x*x + y*y) <= Vars.Sphere_Radius*Vars.Sphere_Radius); + } + else if (abs(Vars.Flag_Shape) == DEFS.SHAPE_SPHERE) /* sphere */ + { + intersect = sphere_intersect(&t0, &t1, x, y, z, vx, vy, vz, Vars.Sphere_Radius); + /* intersect = (intersect && t0 > 0); */ + } + else if ((abs(Vars.Flag_Shape) == DEFS.SHAPE_CYLIND) || (abs(Vars.Flag_Shape) == DEFS.SHAPE_BANANA)) /* cylinder */ + { + intersect = cylinder_intersect(&t0, &t1, x, y, z, vx, vy, vz, Vars.Sphere_Radius, Vars.Cylinder_Height); + if ((abs(Vars.Flag_Shape) == DEFS.SHAPE_BANANA) && (intersect != 1)) intersect = 0; /* remove top/bottom for banana */ + } + else if (abs(Vars.Flag_Shape) == DEFS.SHAPE_BOX) /* box */ + { + intersect = box_intersect(&t0, &t1, x, y, z, vx, vy, vz, fabs(Vars.mxmax-Vars.mxmin), fabs(Vars.mymax-Vars.mymin), fabs(Vars.mzmax-Vars.mzmin)); + } + + if (intersect) + { + if ((abs(Vars.Flag_Shape) == DEFS.SHAPE_SPHERE) || (abs(Vars.Flag_Shape) == DEFS.SHAPE_CYLIND) || (abs(Vars.Flag_Shape) == DEFS.SHAPE_BOX) || (abs(Vars.Flag_Shape) == DEFS.SHAPE_BANANA)) + { + if (t0 < 0 && t1 > 0) + t0 = t; /* neutron was already inside ! */ + if (t1 < 0 && t0 > 0) /* neutron exit before entering !! */ + t1 = t; + /* t0 is now time of incoming intersection with the sphere. */ + if ((Vars.Flag_Shape < 0) && (t1 > 0)) + PROP_DT(t1); /* t1 outgoing beam */ + else + PROP_DT(t0); /* t0 incoming beam */ + } + + /* Now get the data to monitor: current or keep from PreMonitor */ + if (Vars.Flag_UsePreMonitor != 1) + { + Vars.cp = p; + Vars.cx = x; + Vars.cvx = vx; + Vars.csx = sx; + Vars.cy = y; + Vars.cvy = vy; + Vars.csy = sy; + Vars.cz = z; + Vars.cvz = vz; + Vars.csz = sz; + Vars.ct = t; + } + + if ((Vars.He3_pressure > 0) && (t1 != t0) && ((abs(Vars.Flag_Shape) == DEFS.SHAPE_SPHERE) || (abs(Vars.Flag_Shape) == DEFS.SHAPE_CYLIND) || (abs(Vars.Flag_Shape) == DEFS.SHAPE_BOX))) + { + XY = exp(-7.417*Vars.He3_pressure*fabs(t1-t0)*2*PI*K2V); + /* will monitor the absorbed part */ + Vars.cp *= 1-XY; + /* and modify the neutron weight after monitor, only remains 1-p_detect */ + p *= XY; + } + + if (Vars.Flag_per_cm2 && Vars.area != 0) Vars.cp /= Vars.area; + if (Vars.Flag_capture) + { + XY = sqrt(Vars.cvx*Vars.cvx+Vars.cvy*Vars.cvy+Vars.cvz*Vars.cvz); + XY *= V2K; + if (XY != 0) XY = 2*PI/XY; /* lambda. lambda(2200 m/2) = 1.7985 Angs */ + Vars.cp *= XY/1.7985; + } + + pp = Monitor_nD_Trace(&DEFS, &Vars); + Vars.Nsum++; + Vars.psum += pp; + Vars.p2sum += pp*pp; + SCATTER; + + /* now handles intermediate results saving */ + if ((Vars.Intermediate > 0) && (mcget_run_num() > Vars.IntermediateCnts)) + { + Vars.IntermediateCnts += Vars.Intermediate*mcget_ncount(); + /* save results for all monitors in the simulation */ + mcsave(NULL); + } + if (Vars.Flag_parallel) /* back to neutron state before detection */ + Flag_Restore = 1; + } /* end if intersection */ + else { + if (Vars.Flag_Absorb && !Vars.Flag_parallel) ABSORB; + else Flag_Restore = 1; /* no intersection, back to previous state */ + } + + if (Flag_Restore) + { + RESTORE_NEUTRON(mccompcurindex, x, y, z, vx, vy, vz, t, sx, sy, sz, p); + } +} +#line 8006 "dmcafter.c" +} /* End of SETTING parameter declarations. */ +#undef Vars +#undef DEFS +#undef filename +#undef options +#undef mccompcurname +#undef mccompcurindex +#undef sz +#undef sy +#undef sx +#undef p +#undef s2 +#undef s1 +#undef t +#undef vz +#undef vy +#undef vx +#undef z +#undef y +#undef x + mcDEBUG_STATE(mcnlx, mcnly, mcnlz, mcnlvx, mcnlvy, mcnlvz,mcnlt,mcnlsx,mcnlsy, mcnlp) + + mcabsorbAll: + mcDEBUG_LEAVE() + mcDEBUG_STATE(mcnlx, mcnly, mcnlz, mcnlvx, mcnlvy, mcnlvz,mcnlt,mcnlsx,mcnlsy, mcnlp) + /* Copy neutron state to global variables. */ + mcnx = mcnlx; + mcny = mcnly; + mcnz = mcnlz; + mcnvx = mcnlvx; + mcnvy = mcnlvy; + mcnvz = mcnlvz; + mcnt = mcnlt; + mcnsx = mcnlsx; + mcnsy = mcnlsy; + mcnsz = mcnlsz; + mcnp = mcnlp; +} + +void mcsave(FILE *handle) { + if (!handle) mcsiminfo_init(NULL); + /* User component SAVE code. */ + + /* User SAVE code for component 'PSD_sample'. */ + strcpy(mcsig_message, "PSD_sample (Save)"); +#define mccompcurname PSD_sample +#define mccompcurindex 4 +#define Nsum mccPSD_sample_Nsum +#define psum mccPSD_sample_psum +#define p2sum mccPSD_sample_p2sum +#define currentCount mccPSD_sample_currentCount +{ /* Declarations of SETTING parameters. */ +MCNUM xmin = mccPSD_sample_xmin; +MCNUM xmax = mccPSD_sample_xmax; +MCNUM ymin = mccPSD_sample_ymin; +MCNUM ymax = mccPSD_sample_ymax; +char* controlfile = mccPSD_sample_controlfile; +int dumpCount = mccPSD_sample_dumpCount; +#line 95 "MKMonitor.comp" +{ + DETECTOR_OUT_0D("Single monitor", Nsum, psum, p2sum); +} +#line 8069 "dmcafter.c" +} /* End of SETTING parameter declarations. */ +#undef currentCount +#undef p2sum +#undef psum +#undef Nsum +#undef mccompcurname +#undef mccompcurindex + + /* User SAVE code for component 'Det9'. */ + strcpy(mcsig_message, "Det9 (Save)"); +#define mccompcurname Det9 +#define mccompcurindex 7 +#define options mccDet9_options +#define filename mccDet9_filename +#define DEFS mccDet9_DEFS +#define Vars mccDet9_Vars +{ /* Declarations of SETTING parameters. */ +MCNUM xwidth = mccDet9_xwidth; +MCNUM yheight = mccDet9_yheight; +MCNUM zthick = mccDet9_zthick; +MCNUM xmin = mccDet9_xmin; +MCNUM xmax = mccDet9_xmax; +MCNUM ymin = mccDet9_ymin; +MCNUM ymax = mccDet9_ymax; +MCNUM zmin = mccDet9_zmin; +MCNUM zmax = mccDet9_zmax; +#line 345 "/afs/psi.ch/project/sinq/sl-linux/lib/mcstas/monitors/Monitor_nD.comp" +{ + /* save results, but do not free pointers */ + Monitor_nD_Save(&DEFS, &Vars); +} +#line 8101 "dmcafter.c" +} /* End of SETTING parameter declarations. */ +#undef Vars +#undef DEFS +#undef filename +#undef options +#undef mccompcurname +#undef mccompcurindex + + if (!handle) mcsiminfo_close(); +} +void mcfinally(void) { + /* User component FINALLY code. */ + mcsiminfo_init(NULL); + mcsave(mcsiminfo_file); /* save data when simulation ends */ + + /* User FINALLY code for component 'in'. */ + strcpy(mcsig_message, "in (Finally)"); +#define mccompcurname in +#define mccompcurindex 2 +#define file mccin_file +#define type mccin_type +#define rep mccin_rep +#define pos mccin_pos +#define nrows mccin_nrows +#define nread mccin_nread +#define Offset mccin_Offset +#define rTable mccin_rTable +#define Virtual_input_Read_Input mccin_Virtual_input_Read_Input +{ /* Declarations of SETTING parameters. */ +MCNUM repeat_count = mccin_repeat_count; +MCNUM bufsize = mccin_bufsize; +#line 154 "/afs/psi.ch/project/sinq/sl-linux/lib/mcstas/sources/Virtual_input.comp" +{ + Table_Free(&rTable); +} +#line 8137 "dmcafter.c" +} /* End of SETTING parameter declarations. */ +#undef Virtual_input_Read_Input +#undef rTable +#undef Offset +#undef nread +#undef nrows +#undef pos +#undef rep +#undef type +#undef file +#undef mccompcurname +#undef mccompcurindex + + /* User FINALLY code for component 'Det9'. */ + strcpy(mcsig_message, "Det9 (Finally)"); +#define mccompcurname Det9 +#define mccompcurindex 7 +#define options mccDet9_options +#define filename mccDet9_filename +#define DEFS mccDet9_DEFS +#define Vars mccDet9_Vars +{ /* Declarations of SETTING parameters. */ +MCNUM xwidth = mccDet9_xwidth; +MCNUM yheight = mccDet9_yheight; +MCNUM zthick = mccDet9_zthick; +MCNUM xmin = mccDet9_xmin; +MCNUM xmax = mccDet9_xmax; +MCNUM ymin = mccDet9_ymin; +MCNUM ymax = mccDet9_ymax; +MCNUM zmin = mccDet9_zmin; +MCNUM zmax = mccDet9_zmax; +#line 351 "/afs/psi.ch/project/sinq/sl-linux/lib/mcstas/monitors/Monitor_nD.comp" +{ + /* free pointers */ + Monitor_nD_Finally(&DEFS, &Vars); +} +#line 8174 "dmcafter.c" +} /* End of SETTING parameter declarations. */ +#undef Vars +#undef DEFS +#undef filename +#undef options +#undef mccompcurname +#undef mccompcurindex + + mcsiminfo_close(); +} +#define magnify mcdis_magnify +#define line mcdis_line +#define multiline mcdis_multiline +#define circle mcdis_circle +void mcdisplay(void) { + printf("MCDISPLAY: start\n"); + /* Component MCDISPLAY code. */ + + /* MCDISPLAY code for component 'msa'. */ + strcpy(mcsig_message, "msa (McDisplay)"); + printf("MCDISPLAY: component %s\n", "msa"); +#define mccompcurname msa +#define mccompcurindex 1 +#line 42 "/afs/psi.ch/project/sinq/sl-linux/lib/mcstas/optics/Arm.comp" +{ + /* A bit ugly; hard-coded dimensions. */ + magnify(""); + line(0,0,0,0.2,0,0); + line(0,0,0,0,0.2,0); + line(0,0,0,0,0,0.2); +} +#line 8206 "dmcafter.c" +#undef mccompcurname +#undef mccompcurindex + + /* MCDISPLAY code for component 'in'. */ + strcpy(mcsig_message, "in (McDisplay)"); + printf("MCDISPLAY: component %s\n", "in"); +#define mccompcurname in +#define mccompcurindex 2 +#define file mccin_file +#define type mccin_type +#define rep mccin_rep +#define pos mccin_pos +#define nrows mccin_nrows +#define nread mccin_nread +#define Offset mccin_Offset +#define rTable mccin_rTable +#define Virtual_input_Read_Input mccin_Virtual_input_Read_Input +{ /* Declarations of SETTING parameters. */ +MCNUM repeat_count = mccin_repeat_count; +MCNUM bufsize = mccin_bufsize; +#line 159 "/afs/psi.ch/project/sinq/sl-linux/lib/mcstas/sources/Virtual_input.comp" +{ + /* A bit ugly; hard-coded dimensions. */ + magnify(""); + line(0,0,0,0.1,0,0); + line(0,0,0,0,0.1,0); + line(0,0,0,0,0,0.1); +} +#line 8235 "dmcafter.c" +} /* End of SETTING parameter declarations. */ +#undef Virtual_input_Read_Input +#undef rTable +#undef Offset +#undef nread +#undef nrows +#undef pos +#undef rep +#undef type +#undef file +#undef mccompcurname +#undef mccompcurindex + + /* MCDISPLAY code for component 'out2_slit'. */ + strcpy(mcsig_message, "out2_slit (McDisplay)"); + printf("MCDISPLAY: component %s\n", "out2_slit"); +#define mccompcurname out2_slit +#define mccompcurindex 3 +{ /* Declarations of SETTING parameters. */ +MCNUM xmin = mccout2_slit_xmin; +MCNUM xmax = mccout2_slit_xmax; +MCNUM ymin = mccout2_slit_ymin; +MCNUM ymax = mccout2_slit_ymax; +MCNUM radius = mccout2_slit_radius; +#line 63 "/afs/psi.ch/project/sinq/sl-linux/lib/mcstas/optics/Slit.comp" +{ + double xw, yh; + magnify("xy"); + xw = (xmax - xmin)/2.0; + yh = (ymax - ymin)/2.0; + multiline(3, xmin-xw, (double)ymax, 0.0, + (double)xmin, (double)ymax, 0.0, + (double)xmin, ymax+yh, 0.0); + multiline(3, xmax+xw, (double)ymax, 0.0, + (double)xmax, (double)ymax, 0.0, + (double)xmax, ymax+yh, 0.0); + multiline(3, xmin-xw, (double)ymin, 0.0, + (double)xmin, (double)ymin, 0.0, + (double)xmin, ymin-yh, 0.0); + multiline(3, xmax+xw, (double)ymin, 0.0, + (double)xmax, (double)ymin, 0.0, + (double)xmax, ymin-yh, 0.0); +} +#line 8279 "dmcafter.c" +} /* End of SETTING parameter declarations. */ +#undef mccompcurname +#undef mccompcurindex + + /* MCDISPLAY code for component 'PSD_sample'. */ + strcpy(mcsig_message, "PSD_sample (McDisplay)"); + printf("MCDISPLAY: component %s\n", "PSD_sample"); +#define mccompcurname PSD_sample +#define mccompcurindex 4 +#define Nsum mccPSD_sample_Nsum +#define psum mccPSD_sample_psum +#define p2sum mccPSD_sample_p2sum +#define currentCount mccPSD_sample_currentCount +{ /* Declarations of SETTING parameters. */ +MCNUM xmin = mccPSD_sample_xmin; +MCNUM xmax = mccPSD_sample_xmax; +MCNUM ymin = mccPSD_sample_ymin; +MCNUM ymax = mccPSD_sample_ymax; +char* controlfile = mccPSD_sample_controlfile; +int dumpCount = mccPSD_sample_dumpCount; +#line 100 "MKMonitor.comp" +{ + magnify("xy"); + multiline(5, (double)xmin, (double)ymin, 0.0, + (double)xmax, (double)ymin, 0.0, + (double)xmax, (double)ymax, 0.0, + (double)xmin, (double)ymax, 0.0, + (double)xmin, (double)ymin, 0.0); +} +#line 8309 "dmcafter.c" +} /* End of SETTING parameter declarations. */ +#undef currentCount +#undef p2sum +#undef psum +#undef Nsum +#undef mccompcurname +#undef mccompcurindex + + /* MCDISPLAY code for component 'sa_arm'. */ + strcpy(mcsig_message, "sa_arm (McDisplay)"); + printf("MCDISPLAY: component %s\n", "sa_arm"); +#define mccompcurname sa_arm +#define mccompcurindex 5 +#line 42 "/afs/psi.ch/project/sinq/sl-linux/lib/mcstas/optics/Arm.comp" +{ + /* A bit ugly; hard-coded dimensions. */ + magnify(""); + line(0,0,0,0.2,0,0); + line(0,0,0,0,0.2,0); + line(0,0,0,0,0,0.2); +} +#line 8331 "dmcafter.c" +#undef mccompcurname +#undef mccompcurindex + + /* MCDISPLAY code for component 'sample'. */ + strcpy(mcsig_message, "sample (McDisplay)"); + printf("MCDISPLAY: component %s\n", "sample"); +#define mccompcurname sample +#define mccompcurindex 6 +#define reflections mccsample_reflections +#define my_s_v2 mccsample_my_s_v2 +#define my_a_v mccsample_my_a_v +#define q_v mccsample_q_v +{ /* Declarations of SETTING parameters. */ +MCNUM d_phi0 = mccsample_d_phi0; +MCNUM radius = mccsample_radius; +MCNUM focus_r = mccsample_focus_r; +MCNUM h = mccsample_h; +MCNUM pack = mccsample_pack; +MCNUM Vc = mccsample_Vc; +MCNUM sigma_a = mccsample_sigma_a; +MCNUM sigma_inc = mccsample_sigma_inc; +MCNUM frac = mccsample_frac; +MCNUM focus_xw = mccsample_focus_xw; +MCNUM focus_yh = mccsample_focus_yh; +MCNUM focus_aw = mccsample_focus_aw; +MCNUM focus_ah = mccsample_focus_ah; +MCNUM target_x = mccsample_target_x; +MCNUM target_y = mccsample_target_y; +MCNUM target_z = mccsample_target_z; +int target_index = mccsample_target_index; +#line 273 "PowderN.comp" +{ + magnify("xyz"); + circle("xz", 0, h/2.0, 0, radius); + circle("xz", 0, -h/2.0, 0, radius); + line(-radius, -h/2.0, 0, -radius, +h/2.0, 0); + line(+radius, -h/2.0, 0, +radius, +h/2.0, 0); + line(0, -h/2.0, -radius, 0, +h/2.0, -radius); + line(0, -h/2.0, +radius, 0, +h/2.0, +radius); +} +#line 8372 "dmcafter.c" +} /* End of SETTING parameter declarations. */ +#undef q_v +#undef my_a_v +#undef my_s_v2 +#undef reflections +#undef mccompcurname +#undef mccompcurindex + + /* MCDISPLAY code for component 'Det9'. */ + strcpy(mcsig_message, "Det9 (McDisplay)"); + printf("MCDISPLAY: component %s\n", "Det9"); +#define mccompcurname Det9 +#define mccompcurindex 7 +#define options mccDet9_options +#define filename mccDet9_filename +#define DEFS mccDet9_DEFS +#define Vars mccDet9_Vars +{ /* Declarations of SETTING parameters. */ +MCNUM xwidth = mccDet9_xwidth; +MCNUM yheight = mccDet9_yheight; +MCNUM zthick = mccDet9_zthick; +MCNUM xmin = mccDet9_xmin; +MCNUM xmax = mccDet9_xmax; +MCNUM ymin = mccDet9_ymin; +MCNUM ymax = mccDet9_ymax; +MCNUM zmin = mccDet9_zmin; +MCNUM zmax = mccDet9_zmax; +#line 357 "/afs/psi.ch/project/sinq/sl-linux/lib/mcstas/monitors/Monitor_nD.comp" +{ + Monitor_nD_McDisplay(&DEFS, &Vars); +} +#line 8404 "dmcafter.c" +} /* End of SETTING parameter declarations. */ +#undef Vars +#undef DEFS +#undef filename +#undef options +#undef mccompcurname +#undef mccompcurindex + + printf("MCDISPLAY: end\n"); +} +#undef magnify +#undef line +#undef multiline +#undef circle diff --git a/mcstas/dmc/gumibatch.tcl b/mcstas/dmc/gumibatch.tcl new file mode 100644 index 00000000..050ff63c --- /dev/null +++ b/mcstas/dmc/gumibatch.tcl @@ -0,0 +1,174 @@ +#------------------------------------------------------------- +# This is a set of Tcl procedures which try to convert an old +# batch file into a batch file suitable for Mountaingum. +# +# copyright: GPL +# +# Mark Koennecke, February 2008 +#------------------------------------------------------------- +if {[string first tmp $home] < 0} { + set tmppath $home/tmp +} else { + set tmppath $home +} +#------------------------------------------------------------- +proc searchPathForDrivable {name} { + set path [string trim [hmatchprop / sicsdev $name]] + if {[string compare $path NONE] != 0} { + return $path + } + set txt [findalias $name] + if {[string compare $txt NONE] == 0} { + return NONE + } + set l1 [split $txt =] + set l [split [lindex $l1 1] ,] + foreach alias $l { + set alias [string trim $alias] + set path [string trim [hmatchprop / sicsdev $alias]] + if {[string compare $path NONE] != 0} { + return $path + } + } + return NONE +} +#---------------------------------------------------------------- +proc searchForCommand {name} { + return [string trim [hmatchprop / sicscommand $name]] +} +#---------------------------------------------------------------- +proc treatsscan {scanpath command out} { + set l [split $command] + set len [llength $l] + set noVar [expr ($len-2)/3] + set np [lindex $l [expr $len -2]] + set preset [lindex $l [expr $len -1]] + for {set i 0} {$i < $noVar} {incr i} { + set start [expr $i * 3] + set scanVar [lindex $l [expr 1 + $start]] + set scanStart [lindex $l [expr 2 + $start]] + set scanEnd [lindex $l [expr 3 + $start]] + set scanStep [expr ($scanEnd*1. - $scanStart*1.)/$np*1.] + append hdbVar $scanVar , + append hdbStart $scanStart , + append hdbStep $scanStep , + } + set hdbVar [string trim $hdbVar ,] + set hdbStart [string trim $hdbStart ,] + set hdbStep [string trim $hdbStep ,] + puts $out "\#NODE: $scanpath" + puts $out "clientput BatchPos = 1" + puts $out "hdbscan $hdbVar $hdbStart $hdbStep $np monitor $preset" +} +#---------------------------------------------------------------- +proc treatcscan {scanpath command out} { + set l [split $command] + set scanVar [lindex $l 1] + set scanCenter [lindex $l 2] + set scanStep [lindex $l 3] + set np [lindex $l 4] + set preset [lindex $l 5] + set hdbStart [expr $scanCenter - ($np*1.0)/2. * $scanStep*1.0] + puts $out "\#NODE: $scanpath" + puts $out "clientput BatchPos = 1" + puts $out "hdbscan $scanVar $hdbStart $scanStep $np monitor $preset" +} +#---------------------------------------------------------------- +proc translateCommand {command out} { + set drivelist [list drive dr run] + set textList [list for while source if] +# clientput "Translating: $command" + set command [string trim $command] + if {[string length $command] < 2} { + return + } + set l [split $command] + set obj [string trim [lindex $l 0]] +#------- check for drive commands + set idx [lsearch $drivelist $obj] + if {$idx >= 0} { + set dev [lindex $l 1] + set path [searchPathForDrivable $dev] + if {[string compare $path NONE] != 0} { + set realTxt [hgetprop $path sicsdev] + set realL [split $realTxt =] + set realDev [lindex $realL 1] + set mapList [list $dev $realDev] + set newCom [string map $mapList $command] + puts $out "\#NODE: $path" + puts $out "clientput BatchPos = 1" + puts $out $newCom + return + } + } +#------ check for well known broken commands + set idx [lsearch $textList $obj] + if {$idx >= 0} { + puts $out "\#NODE: /batch/commandtext" + puts $out "clientput BatchPos = 1" + set buffer [string map {\n @nl@} $command] + puts $out "hset /batch/commandtext $buffer" + return + } +#--------- check for simple commands + set path [searchForCommand $command] + if {[string compare $path NONE] != 0} { + puts $out "\#NODE: $path" + puts $out "clientput BatchPos = 1" + puts $out $command + return + } + set scancom [searchForCommand hdbscan] +#---------- deal with scans + if {[string first sscan $obj] >= 0} { + if {[catch {treatsscan $scancom $command $out}] == 0} { + return + } + } + if {[string first cscan $obj] >= 0} { + if {[catch {treatsscan $scancom $command $out}] == 0} { + return + } + } +#--------- give up: output as a text node + puts $out "\#NODE: /batch/commandtext" + puts $out "clientput BatchPos = 1" + set buffer [string map {\n @nl@} $command] + puts $out "hset /batch/commandtext $buffer" +} +#---------------------------------------------------------------- +proc mgbatch {filename} { + global tmppath + set f [open $filename r] + gets $f line + close $f + if {[string first MOUNTAINBATCH $line] > 0} { +#--------- This is a mountaingum batch file which does not need +# to be massaged + return $filename + } + set f [open $filename r] + set realfilename [file tail $filename] + set out [open $tmppath/$realfilename w] + puts $out \#MOUNTAINBATCH + while {[gets $f line] >= 0} { + append buffer $line + if {[info complete $buffer] == 1} { + translateCommand $buffer $out + unset buffer + } else { + append buffer \n + } + } + close $out + return $tmppath/$realfilename +} +#---------------------------------------------------------------- +proc loadmgbatch {filename} { + set txt [exe fullpath $filename] + set l [split $txt =] + set realf [lindex $l 1] + set realf [mgbatch $realf] + return [exe print $realf] +} + diff --git a/mcstas/dmc/gumxml.tcl b/mcstas/dmc/gumxml.tcl new file mode 100644 index 00000000..bb9e538b --- /dev/null +++ b/mcstas/dmc/gumxml.tcl @@ -0,0 +1,80 @@ +proc getdataType {path} { + return [lindex [split [hinfo $path] ,] 0] +} + + +proc make_nodes {path result indent} { +set nodename [file tail $path]; +set type [getdataType $path] +set prefix [string repeat " " $indent] +set newIndent [expr $indent + 2] +#array set prop_list [ string trim [join [split [hlistprop $path] =]] ] + set prop_list(control) true + set we_have_control [info exists prop_list(control)] + if {$we_have_control == 0 || $we_have_control && $prop_list(control) == "true"} { + append result "$prefix\n" + foreach p [property_elements $path $newIndent] { + append result $p + } + foreach x [hlist $path] { + set result [make_nodes [string map {// /} "$path/$x"] $result $newIndent] + } + append result "$prefix\n" + } + return $result +} + +proc property_elements_old {path indent} { + set prefix [string repeat " " $indent] + foreach {key value} [string map {= " "} [hlistprop $path]] { + if {[string compare -nocase $key "control"] == 0} {continue} + lappend proplist "$prefix\n" +# foreach v [split $value ,] { +# lappend proplist "$prefix$prefix$v\n" +# } + lappend proplist "$prefix$prefix$value\n" + lappend proplist "$prefix\n" + } + if [info exists proplist] {return $proplist} +} + +proc property_elements {path indent} { + set prefix [string repeat " " $indent] + set data [hlistprop $path] + set propList [split $data \n] + foreach prop $propList { + set pl [split $prop =] + set key [string trim [lindex $pl 0]] + set value [string trim [lindex $pl 1]] + if {[string length $key] < 1} { + continue + } + lappend proplist "$prefix\n" + lappend proplist "$prefix$prefix$value\n" + lappend proplist "$prefix\n" + } + if [info exists proplist] {return $proplist} +} + +proc getgumtreexml {path} { + append result "\n" + append result "\n" + + if {[string compare $path "/" ] == 0} { + foreach n [hlist $path] { + set result [make_nodes $n $result 2] + } + } else { +# set result [make_nodes $path $result 2] + foreach n [hlist $path] { + set result [make_nodes $path/$n $result 2] + } + } + + append result "\n" +} + +if {[info exists guminit] == 0} { + set guminit 1 + Publish getgumtreexml Spy +} diff --git a/mcstas/dmc/mcsupport.tcl b/mcstas/dmc/mcsupport.tcl index 029a2a77..eba8dd15 100644 --- a/mcstas/dmc/mcsupport.tcl +++ b/mcstas/dmc/mcsupport.tcl @@ -30,7 +30,7 @@ proc washsimfile {name} { # dump its data. Otherwise we observe that data reading fails. # mcwaittime is used for this. Increase if you see problems #-------------------------------------------------------------------- -set mcwaittime 7 +set mcwaittime 2 #---------------------------------------------------------------------- proc mcstasdump {pid} { global mcwaittime diff --git a/mcstas/dmc/nxdmc.tcl b/mcstas/dmc/nxdmc.tcl index 272fa0a9..2453deeb 100644 --- a/mcstas/dmc/nxdmc.tcl +++ b/mcstas/dmc/nxdmc.tcl @@ -4,6 +4,7 @@ # This is the scripted version using nxscript # # Mark Koennecke, May 2004 +# This is a special version for virtual DMC on lns00 #--------------------------------------------------------------------------- proc storeMonochromator {} { @@ -75,14 +76,34 @@ proc makeLinks {} { nxscript makelink dana dtnstep nxscript makelink dana mlambda } +#------------------------------------------------------------------------ +proc makeSimFileName args { + global datahome + sicsdatanumber incr + set num [SplitReply [sicsdatanumber]] + return [makeSimForNum $num] +} +#------------------------------------------------------------------------ +proc makeSimForNum {num} { + global datahome + set pre [string trim [SplitReply [sicsdataprefix]]] + set po [string trim [SplitReply [sicsdatapostfix]]] + return [format "%s/%s2006n%6.6d%s" $datahome $pre $num $po] +} #------------------------------------------------------------------------- # store DMC data #------------------------------------------------------------------------- proc storedata {} { - global home - set fil [newFileName] + global home wwwMode + + if {$wwwMode == 1} { + set fil [makeSimFileName] + } else { + set fil [newFileName] + } + lastdatafile $fil clientput "Opening $fil for writing" - nxscript create4 $fil $home/dmc.dic + nxscript createxml $fil $home/dmc.dic writeStandardAttributes $fil writeTextVar etitle title diff --git a/mcstas/dmc/samenv/tomato/07-20.log b/mcstas/dmc/samenv/tomato/07-20.log new file mode 100644 index 00000000..da845dd1 --- /dev/null +++ b/mcstas/dmc/samenv/tomato/07-20.log @@ -0,0 +1,11390 @@ +#2006-07-20 00:00:00 isdst 1 period 5 exact 1 +00:00:00 4.4220 +00:00:05 4.6817 +00:00:10 4.8740 +00:00:15 4.5413 +00:00:20 4.8290 +00:00:25 4.6405 +00:00:30 4.7156 +00:00:35 4.7455 +00:00:40 4.6929 +00:00:45 4.9565 +00:00:50 4.6585 +00:00:55 4.7447 +00:01:00 4.8010 +00:01:05 4.6188 +00:01:10 4.8519 +00:01:15 4.7247 +00:01:20 4.7355 +00:01:25 4.7258 +00:01:30 4.7237 +00:01:35 4.7483 +00:01:40 4.7082 +00:01:45 4.8206 +00:01:50 4.7051 +00:01:55 4.7323 +00:02:00 4.6824 +00:02:05 4.7419 +00:02:10 4.6984 +00:02:15 4.6150 +00:02:20 4.6714 +00:02:25 4.7849 +00:02:30 4.6965 +00:02:35 4.5760 +00:02:40 4.9699 +00:02:45 4.7526 +00:02:50 4.7628 +00:02:55 4.9419 +00:03:00 4.6565 +00:03:05 4.6991 +00:03:10 4.8350 +00:03:15 4.5047 +00:03:20 4.7496 +00:03:25 4.9007 +00:03:30 4.5065 +00:03:35 4.5825 +00:03:40 4.7890 +00:03:45 4.5428 +00:03:50 4.8036 +00:03:55 4.9926 +00:04:00 4.7839 +00:04:05 4.5629 +00:04:10 4.5483 +00:04:15 4.7965 +00:04:20 4.7145 +00:04:25 4.4598 +00:04:30 4.7563 +00:04:35 4.9160 +00:04:40 4.6162 +00:04:45 4.6842 +00:04:50 4.8263 +00:04:55 4.6060 +00:05:00 4.6326 +00:05:05 4.8250 +00:05:10 4.5465 +00:05:15 4.4622 +00:05:20 4.7847 +00:05:25 5.0117 +00:05:30 4.6701 +00:05:35 4.5193 +00:05:40 4.9961 +00:05:45 4.7811 +00:05:50 4.4906 +00:05:55 4.7368 +00:06:00 4.8617 +00:06:05 4.5488 +00:06:10 4.7841 +00:06:15 4.7002 +00:06:20 4.6989 +00:06:25 4.6631 +00:06:30 4.7193 +00:06:35 4.7415 +00:06:40 4.6767 +00:06:45 4.6453 +00:06:50 4.6289 +00:06:55 4.6962 +00:07:00 4.7160 +00:07:05 4.6867 +00:07:10 4.5543 +00:07:15 4.8891 +00:07:20 4.7052 +00:07:25 4.7904 +00:07:30 4.7234 +00:07:35 4.8186 +00:07:40 4.6969 +00:07:45 4.7239 +00:07:50 4.6745 +00:07:55 4.7484 +00:08:00 4.7717 +00:08:05 4.7169 +00:08:10 4.7550 +00:08:15 4.7468 +00:08:20 4.7159 +00:08:25 4.7103 +00:08:30 4.6670 +00:08:35 4.6043 +00:08:40 4.6312 +00:08:45 4.6176 +00:08:50 4.6648 +00:08:55 4.5711 +00:09:00 4.6102 +00:09:05 4.6890 +00:09:10 4.7265 +00:09:15 4.7744 +00:09:20 4.8012 +00:09:25 4.7519 +00:09:30 4.6055 +00:09:35 4.6143 +00:09:40 4.6462 +00:09:45 4.5684 +00:09:50 4.7042 +00:09:55 4.7895 +00:10:00 4.6858 +00:10:05 4.7278 +00:10:10 4.6530 +00:10:15 4.7561 +00:10:20 4.7606 +00:10:25 4.6972 +00:10:30 4.7211 +00:10:35 4.6725 +00:10:40 4.6632 +00:10:45 4.6419 +00:10:50 4.6491 +00:10:55 4.7272 +00:11:00 4.6261 +00:11:05 4.6826 +00:11:10 4.7016 +00:11:15 4.6607 +00:11:20 4.7134 +00:11:25 4.7481 +00:11:30 4.7713 +00:11:35 4.7040 +00:11:40 4.5523 +00:11:45 4.5043 +00:11:50 4.5869 +00:11:55 4.7134 +00:12:00 4.8288 +00:12:05 4.7514 +00:12:10 4.6918 +00:12:15 4.6551 +00:12:20 4.6139 +00:12:25 4.5990 +00:12:30 4.6124 +00:12:35 4.6045 +00:12:40 4.7286 +00:12:45 4.6947 +00:12:50 4.6556 +00:12:55 4.6186 +00:13:00 4.6898 +00:13:05 4.6398 +00:13:10 4.6271 +00:13:15 4.6729 +00:13:20 4.7055 +00:13:25 4.7354 +00:13:30 4.6922 +00:13:35 4.6496 +00:13:40 4.5600 +00:13:45 4.5622 +00:13:50 4.6482 +00:13:55 4.6778 +00:14:00 4.6985 +00:14:05 4.7838 +00:14:10 4.8014 +00:14:15 4.7729 +00:14:20 4.7496 +00:14:25 4.6716 +00:14:30 4.7095 +00:14:35 4.7489 +00:14:40 4.8078 +00:14:45 4.7304 +00:14:50 4.6622 +00:14:55 4.5774 +00:15:00 4.5783 +00:15:05 4.6438 +00:15:10 4.6234 +00:15:15 4.5829 +00:15:20 4.6076 +00:15:25 4.6770 +00:15:30 4.6758 +00:15:35 4.7493 +00:15:40 4.6859 +00:15:45 4.6512 +00:15:50 4.6163 +00:15:55 4.5922 +00:16:00 4.6821 +00:16:05 4.6786 +00:16:10 4.7159 +00:16:15 4.7040 +00:16:20 4.5776 +00:16:25 4.5723 +00:16:30 4.6434 +00:16:35 4.6034 +00:16:40 4.5789 +00:16:45 4.5967 +00:16:50 4.5976 +00:16:55 4.6045 +00:17:00 4.5067 +00:17:05 4.5015 +00:17:10 4.5918 +00:17:15 4.6710 +00:17:20 4.6784 +00:17:25 4.6811 +00:17:30 4.6865 +00:17:35 4.6031 +00:17:40 4.5319 +00:17:45 4.5924 +00:17:50 4.6342 +00:17:55 4.6277 +00:18:00 4.7354 +00:18:05 4.7905 +00:18:10 4.6982 +00:18:15 4.6340 +00:18:20 4.6698 +00:18:25 4.7368 +00:18:30 4.6674 +00:18:35 4.6634 +00:18:40 4.6390 +00:18:45 4.5669 +00:18:50 4.6421 +00:18:55 4.6479 +00:19:00 4.6485 +00:19:05 4.5901 +00:19:10 4.6362 +00:19:15 4.5978 +00:19:20 4.6604 +00:19:25 4.6522 +00:19:30 4.5403 +00:19:35 4.5121 +00:19:40 4.5020 +00:19:45 4.5524 +00:19:50 4.5470 +00:19:55 4.6470 +00:20:00 4.7540 +00:20:05 4.6294 +00:20:10 4.6719 +00:20:15 4.7052 +00:20:20 4.7341 +00:20:25 4.5804 +00:20:30 4.4892 +00:20:35 4.5266 +00:20:40 4.4486 +00:20:45 4.5381 +00:20:50 4.6343 +00:20:55 4.7059 +00:21:00 4.6584 +00:21:05 4.6118 +00:21:10 4.6244 +00:21:15 4.5285 +00:21:20 4.5853 +00:21:25 4.6887 +00:21:30 4.6226 +00:21:35 4.6477 +00:21:40 4.6974 +00:21:45 4.7163 +00:21:50 4.6072 +00:21:55 4.6236 +00:22:00 4.6290 +00:22:05 4.6181 +00:22:10 4.6448 +00:22:15 4.5960 +00:22:20 4.5376 +00:22:25 4.5801 +00:22:30 4.6342 +00:22:35 4.6794 +00:22:40 4.6625 +00:22:45 4.5974 +00:22:50 4.5335 +00:22:55 4.5006 +00:23:00 4.5708 +00:23:05 4.6173 +00:23:10 4.6414 +00:23:15 4.7294 +00:23:20 4.6904 +00:23:25 4.5300 +00:23:30 4.5055 +00:23:35 4.5294 +00:23:40 4.5644 +00:23:45 4.6120 +00:23:50 4.5459 +00:23:55 4.5728 +00:24:00 4.6435 +00:24:05 4.5234 +00:24:10 4.5409 +00:24:15 4.6420 +00:24:20 4.5347 +00:24:25 4.4408 +00:24:30 4.4648 +00:24:35 4.6354 +00:24:40 4.7121 +00:24:45 4.5644 +00:24:50 4.6285 +00:24:55 4.6569 +00:25:00 4.6329 +00:25:05 4.5035 +00:25:10 4.5827 +00:25:15 4.6283 +00:25:20 4.5210 +00:25:25 4.6864 +00:25:30 4.6783 +00:25:35 4.5636 +00:25:40 4.4468 +00:25:45 4.5108 +00:25:50 4.5875 +00:25:55 4.5955 +00:26:00 4.6033 +00:26:05 4.5904 +00:26:10 4.5635 +00:26:15 4.6481 +00:26:20 4.5530 +00:26:25 4.4963 +00:26:30 4.4125 +00:26:35 4.4791 +00:26:40 4.6544 +00:26:45 4.6642 +00:26:50 4.6169 +00:26:55 4.6023 +00:27:00 4.5304 +00:27:05 4.5282 +00:27:10 4.5902 +00:27:15 4.6882 +00:27:20 4.5866 +00:27:25 4.4517 +00:27:30 4.5170 +00:27:35 4.4722 +00:27:40 4.4385 +00:27:45 4.5143 +00:27:50 4.5300 +00:27:55 4.5571 +00:28:00 4.5760 +00:28:05 4.5816 +00:28:10 4.6103 +00:28:15 4.6614 +00:28:20 4.6084 +00:28:25 4.5492 +00:28:30 4.5768 +00:28:35 4.6348 +00:28:40 4.6360 +00:28:45 4.6178 +00:28:50 4.5579 +00:28:55 4.5451 +00:29:00 4.4516 +00:29:05 4.5316 +00:29:10 4.5465 +00:29:15 4.4857 +00:29:20 4.5358 +00:29:25 4.5520 +00:29:30 4.5424 +00:29:35 4.5882 +00:29:40 4.6289 +00:29:45 4.5276 +00:29:50 4.4803 +00:29:55 4.5501 +00:30:00 4.5949 +00:30:05 4.4963 +00:30:10 4.4566 +00:30:15 4.5125 +00:30:20 4.5830 +00:30:25 4.5704 +00:30:30 4.5960 +00:30:35 4.6502 +00:30:40 4.6714 +00:30:45 4.4935 +00:30:50 4.5360 +00:30:55 4.5935 +00:31:00 4.5341 +00:31:05 4.5745 +00:31:10 4.5400 +00:31:15 4.4936 +00:31:20 4.5170 +00:31:25 4.4749 +00:31:30 4.5411 +00:31:35 4.5790 +00:31:40 4.6014 +00:31:45 4.5899 +00:31:50 4.6218 +00:31:55 4.5692 +00:32:00 4.5669 +00:32:05 4.5436 +00:32:10 4.5633 +00:32:15 4.5415 +00:32:20 4.5203 +00:32:25 4.5493 +00:32:30 4.5474 +00:32:35 4.5792 +00:32:40 4.4826 +00:32:45 4.5856 +00:32:50 4.6930 +00:32:55 4.4555 +00:33:00 4.4590 +00:33:05 4.4742 +00:33:10 4.3939 +00:33:15 4.5159 +00:33:20 4.5537 +00:33:25 4.5368 +00:33:30 4.6501 +00:33:35 4.5147 +00:33:40 4.4638 +00:33:45 4.5270 +00:33:50 4.5219 +00:33:55 4.5862 +00:34:00 4.5359 +00:34:05 4.5661 +00:34:10 4.5016 +00:34:15 4.4848 +00:34:20 4.5342 +00:34:25 4.5615 +00:34:30 4.4712 +00:34:35 4.4322 +00:34:40 4.4936 +00:34:45 4.5149 +00:34:50 4.4938 +00:34:55 4.5250 +00:35:00 4.6035 +00:35:05 4.6155 +00:35:10 4.4953 +00:35:15 4.4337 +00:35:20 4.4871 +00:35:25 4.4752 +00:35:30 4.4806 +00:35:35 4.5323 +00:35:40 4.4637 +00:35:45 4.5142 +00:35:50 4.5820 +00:35:55 4.4824 +00:36:00 4.3943 +00:36:05 4.4192 +00:36:10 4.5344 +00:36:15 4.6162 +00:36:20 4.5492 +00:36:25 4.4987 +00:36:30 4.4474 +00:36:35 4.5160 +00:36:40 4.5782 +00:36:45 4.6402 +00:36:50 4.6382 +00:36:55 4.5542 +00:37:00 4.6179 +00:37:05 4.5565 +00:37:10 4.4886 +00:37:15 4.5097 +00:37:20 4.5766 +00:37:25 4.5297 +00:37:30 4.5245 +00:37:35 4.5737 +00:37:40 4.6012 +00:37:45 4.5268 +00:37:50 4.5338 +00:37:55 4.5797 +00:38:00 4.6208 +00:38:05 4.5725 +00:38:10 4.4286 +00:38:15 4.5019 +00:38:20 4.4811 +00:38:25 4.4893 +00:38:30 4.5711 +00:38:35 4.5490 +00:38:40 4.5073 +00:38:45 4.4377 +00:38:50 4.5116 +00:38:55 4.6269 +00:39:00 4.6124 +00:39:05 4.6049 +00:39:10 4.5082 +00:39:15 4.4839 +00:39:20 4.5409 +00:39:25 4.5523 +00:39:30 4.4416 +00:39:35 4.5306 +00:39:40 4.5770 +00:39:45 4.6104 +00:39:50 4.6233 +00:39:55 4.5381 +00:40:00 4.5173 +00:40:05 4.5568 +00:40:10 4.5026 +00:40:15 4.5160 +00:40:20 4.4674 +00:40:25 4.4774 +00:40:30 4.4854 +00:40:35 4.4668 +00:40:40 4.4376 +00:40:45 4.4997 +00:40:50 4.5646 +00:40:55 4.5126 +00:41:00 4.4743 +00:41:05 4.5321 +00:41:10 4.5567 +00:41:15 4.5144 +00:41:20 4.5040 +00:41:25 4.5271 +00:41:30 4.3999 +00:41:35 4.4462 +00:41:40 4.4895 +00:41:45 4.5344 +00:41:50 4.5659 +00:41:55 4.5152 +00:42:00 4.5203 +00:42:05 4.4866 +00:42:10 4.4151 +00:42:15 4.4226 +00:42:20 4.5671 +00:42:25 4.4509 +00:42:30 4.4198 +00:42:35 4.5083 +00:42:40 4.5386 +00:42:45 4.5630 +00:42:50 4.4550 +00:42:55 4.4774 +00:43:00 4.4932 +00:43:05 4.5156 +00:43:10 4.5474 +00:43:15 4.5158 +00:43:20 4.5327 +00:43:25 4.5506 +00:43:30 4.4828 +00:43:35 4.3712 +00:43:40 4.4493 +00:43:45 4.4934 +00:43:50 4.5200 +00:43:55 4.4524 +00:44:00 4.4418 +00:44:05 4.5536 +00:44:10 4.5164 +00:44:15 4.3941 +00:44:20 4.4460 +00:44:25 4.4619 +00:44:30 4.4449 +00:44:35 4.4079 +00:44:40 4.4321 +00:44:45 4.4676 +00:44:50 4.4853 +00:44:55 4.5050 +00:45:00 4.4927 +00:45:05 4.5000 +00:45:10 4.4526 +00:45:15 4.5871 +00:45:20 4.4945 +00:45:25 4.4748 +00:45:30 4.4008 +00:45:35 4.4567 +00:45:40 4.5168 +00:45:45 4.4177 +00:45:50 4.4600 +00:45:55 4.4661 +00:46:00 4.4410 +00:46:05 4.4642 +00:46:10 4.4806 +00:46:15 4.4312 +00:46:20 4.4568 +00:46:25 4.5353 +00:46:30 4.5235 +00:46:35 4.4324 +00:46:40 4.4251 +00:46:45 4.4565 +00:46:50 4.4686 +00:46:55 4.3992 +00:47:00 4.4211 +00:47:05 4.4312 +00:47:10 4.4105 +00:47:15 4.4845 +00:47:20 4.4690 +00:47:25 4.5173 +00:47:30 4.4620 +00:47:35 4.4822 +00:47:40 4.4820 +00:47:45 4.4630 +00:47:50 4.4731 +00:47:55 4.3761 +00:48:00 4.4958 +00:48:05 4.4974 +00:48:10 4.4085 +00:48:15 4.4012 +00:48:20 4.3988 +00:48:25 4.4623 +00:48:30 4.4448 +00:48:35 4.4673 +00:48:40 4.5114 +00:48:45 4.4476 +00:48:50 4.3874 +00:48:55 4.4134 +00:49:00 4.4743 +00:49:05 4.3290 +00:49:10 4.4620 +00:49:15 4.4206 +00:49:20 4.4504 +00:49:25 4.4320 +00:49:30 4.5030 +00:49:35 4.4395 +00:49:40 4.4745 +00:49:45 4.4807 +00:49:50 4.4628 +00:49:55 4.4855 +00:50:00 4.4465 +00:50:05 4.4064 +00:50:10 4.3766 +00:50:15 4.4768 +00:50:20 4.4859 +00:50:25 4.4232 +00:50:30 4.4185 +00:50:35 4.3699 +00:50:40 4.3610 +00:50:45 4.4088 +00:50:50 4.4081 +00:50:55 4.3916 +00:51:00 4.4049 +00:51:05 4.4238 +00:51:10 4.4703 +00:51:15 4.4308 +00:51:20 4.3797 +00:51:25 4.3573 +00:51:30 4.4307 +00:51:35 4.4622 +00:51:40 4.4205 +00:51:45 4.4631 +00:51:50 4.4219 +00:51:55 4.3917 +00:52:00 4.4872 +00:52:05 4.4361 +00:52:10 4.4310 +00:52:15 4.4007 +00:52:20 4.3004 +00:52:25 4.3968 +00:52:30 4.4389 +00:52:35 4.3863 +00:52:40 4.3400 +00:52:45 4.3791 +00:52:50 4.4838 +00:52:55 4.4260 +00:53:00 4.3531 +00:53:05 4.3816 +00:53:10 4.3660 +00:53:15 4.3466 +00:53:20 4.3526 +00:53:25 4.3207 +00:53:30 4.3762 +00:53:35 4.4650 +00:53:40 4.4402 +00:53:45 4.3859 +00:53:50 4.4056 +00:53:55 4.3779 +00:54:00 4.3604 +00:54:05 4.2799 +00:54:10 4.4375 +00:54:15 4.4396 +00:54:20 4.4272 +00:54:25 4.3911 +00:54:30 4.2900 +00:54:35 4.3089 +00:54:40 4.3577 +00:54:45 4.5759 +00:54:50 4.5102 +00:54:55 4.4915 +00:55:00 4.4122 +00:55:05 4.4031 +00:55:10 4.4639 +00:55:15 4.5257 +00:55:20 4.5316 +00:55:25 4.4545 +00:55:30 4.3464 +00:55:35 4.3142 +00:55:40 4.3456 +00:55:45 4.3361 +00:55:50 4.3511 +00:55:55 4.3789 +00:56:00 4.4700 +00:56:05 4.3797 +00:56:10 4.3984 +00:56:15 4.3820 +00:56:20 4.3497 +00:56:25 4.4434 +00:56:30 4.4500 +00:56:35 4.3652 +00:56:40 4.4015 +00:56:45 4.4029 +00:56:50 4.4404 +00:56:55 4.4655 +00:57:00 4.3896 +00:57:05 4.4025 +00:57:10 4.4612 +00:57:15 4.4121 +00:57:20 4.2687 +00:57:25 4.3414 +00:57:30 4.3969 +00:57:35 4.4380 +00:57:40 4.4466 +00:57:45 4.4784 +00:57:50 4.3828 +00:57:55 4.4135 +00:58:00 4.3907 +00:58:05 4.2682 +00:58:10 4.3720 +00:58:15 4.4584 +00:58:20 4.4061 +00:58:25 4.4598 +00:58:30 4.4075 +00:58:35 4.3735 +00:58:40 4.4250 +00:58:45 4.4295 +00:58:50 4.3214 +00:58:55 4.3382 +00:59:00 4.3685 +00:59:05 4.4048 +00:59:10 4.4172 +00:59:15 4.3918 +00:59:20 4.4032 +00:59:25 4.4608 +00:59:30 4.5191 +00:59:35 4.3325 +00:59:40 4.2876 +00:59:45 4.3512 +00:59:50 4.4767 +00:59:55 4.4332 +01:00:00 4.4074 +01:00:05 4.3878 +01:00:10 4.4312 +01:00:15 4.4159 +01:00:20 4.4617 +01:00:25 4.3944 +01:00:30 4.3727 +01:00:35 4.3883 +01:00:40 4.3865 +01:00:45 4.4664 +01:00:50 4.4872 +01:00:55 4.3948 +01:01:00 4.3590 +01:01:05 4.3126 +01:01:10 4.3429 +01:01:15 4.4328 +01:01:20 4.3562 +01:01:25 4.2807 +01:01:30 4.3410 +01:01:35 4.4265 +01:01:40 4.4184 +01:01:45 4.4009 +01:01:50 4.3631 +01:01:55 4.4086 +01:02:00 4.3344 +01:02:05 4.4368 +01:02:10 4.4384 +01:02:15 4.4348 +01:02:20 4.4381 +01:02:25 4.3338 +01:02:30 4.4227 +01:02:35 4.3804 +01:02:40 4.3475 +01:02:45 4.2865 +01:02:50 4.3633 +01:02:55 4.4499 +01:03:00 4.3904 +01:03:05 4.4279 +01:03:10 4.3076 +01:03:15 4.3375 +01:03:20 4.3368 +01:03:25 4.3967 +01:03:30 4.2929 +01:03:35 4.4090 +01:03:40 4.2816 +01:03:45 4.3679 +01:03:50 4.2956 +01:03:55 4.1736 +01:04:00 4.3769 +01:04:05 4.3249 +01:04:10 4.3678 +01:04:15 4.3364 +01:04:20 4.4167 +01:04:25 4.3658 +01:04:30 4.3167 +01:04:35 4.4009 +01:04:40 4.4293 +01:04:45 4.3985 +01:04:50 4.3601 +01:04:55 4.3750 +01:05:00 4.4278 +01:05:05 4.3288 +01:05:10 4.2940 +01:05:15 4.3823 +01:05:20 4.3564 +01:05:25 4.2809 +01:05:30 4.3604 +01:05:35 4.3538 +01:05:40 4.2968 +01:05:45 4.4204 +01:05:50 4.4499 +01:05:55 4.3640 +01:06:00 4.3667 +01:06:05 4.3279 +01:06:10 4.3124 +01:06:15 4.3250 +01:06:20 4.3208 +01:06:25 4.3676 +01:06:30 4.3484 +01:06:35 4.3444 +01:06:40 4.3129 +01:06:45 4.4036 +01:06:50 4.3900 +01:06:55 4.2903 +01:07:00 4.3628 +01:07:05 4.3733 +01:07:10 4.3928 +01:07:15 4.3860 +01:07:20 4.3117 +01:07:25 4.3622 +01:07:30 4.3702 +01:07:35 4.4324 +01:07:40 4.4328 +01:07:45 4.2880 +01:07:50 4.3981 +01:07:55 4.4315 +01:08:00 4.3964 +01:08:05 4.4300 +01:08:10 4.3672 +01:08:15 4.4513 +01:08:20 4.4274 +01:08:25 4.4875 +01:08:30 4.3679 +01:08:35 4.2216 +01:08:40 4.3919 +01:08:45 4.3782 +01:08:50 4.4025 +01:08:55 4.3142 +01:09:00 4.3559 +01:09:05 4.4162 +01:09:10 4.2810 +01:09:15 4.3664 +01:09:20 4.3626 +01:09:25 4.3086 +01:09:30 4.2889 +01:09:35 4.3088 +01:09:40 4.3192 +01:09:45 4.3935 +01:09:50 4.3739 +01:09:55 4.4131 +01:10:00 4.4618 +01:10:05 4.3094 +01:10:10 4.3700 +01:10:15 4.4166 +01:10:20 4.3390 +01:10:25 4.3123 +01:10:30 4.3284 +01:10:35 4.2842 +01:10:40 4.2512 +01:10:45 4.3096 +01:10:50 4.2674 +01:10:55 4.2534 +01:11:00 4.3029 +01:11:05 4.3657 +01:11:10 4.3892 +01:11:15 4.3644 +01:11:20 4.3541 +01:11:25 4.3757 +01:11:30 4.2622 +01:11:35 4.3126 +01:11:40 4.2781 +01:11:45 4.3066 +01:11:50 4.3498 +01:11:55 4.3250 +01:12:00 4.2310 +01:12:05 4.3490 +01:12:10 4.3291 +01:12:15 4.3629 +01:12:20 4.4209 +01:12:25 4.3311 +01:12:30 4.3032 +01:12:35 4.2930 +01:12:40 4.2438 +01:12:45 4.3059 +01:12:50 4.3176 +01:12:55 4.3965 +01:13:00 4.3454 +01:13:05 4.3334 +01:13:10 4.3779 +01:13:15 4.3510 +01:13:20 4.3688 +01:13:25 4.4130 +01:13:30 4.3286 +01:13:35 4.2304 +01:13:40 4.2886 +01:13:45 4.3177 +01:13:50 4.2048 +01:13:55 4.2450 +01:14:00 4.2222 +01:14:05 4.2135 +01:14:10 4.3035 +01:14:15 4.3195 +01:14:20 4.3443 +01:14:25 4.3293 +01:14:30 4.4398 +01:14:35 4.4170 +01:14:40 4.4308 +01:14:45 4.4393 +01:14:50 4.3871 +01:14:55 4.3797 +01:15:00 4.3682 +01:15:05 4.3024 +01:15:10 4.2482 +01:15:15 4.2591 +01:15:20 4.2860 +01:15:25 4.2180 +01:15:30 4.2693 +01:15:35 4.3441 +01:15:40 4.4240 +01:15:45 4.4088 +01:15:50 4.3174 +01:15:55 4.1964 +01:16:00 4.1919 +01:16:05 4.2968 +01:16:10 4.2617 +01:16:15 4.3784 +01:16:20 4.4402 +01:16:25 4.3435 +01:16:30 4.2817 +01:16:35 4.2669 +01:16:40 4.3331 +01:16:45 4.3752 +01:16:50 4.3216 +01:16:55 4.3644 +01:17:00 4.4401 +01:17:05 4.4274 +01:17:10 4.4199 +01:17:15 4.4060 +01:17:20 4.4387 +01:17:25 4.4009 +01:17:30 4.3730 +01:17:35 4.3241 +01:17:40 4.3408 +01:17:45 4.3974 +01:17:50 4.3881 +01:17:55 4.3509 +01:18:00 4.3589 +01:18:05 4.3207 +01:18:10 4.1858 +01:18:15 4.1990 +01:18:20 4.2375 +01:18:25 4.3330 +01:18:30 4.2635 +01:18:35 4.2315 +01:18:40 4.2903 +01:18:45 4.3211 +01:18:50 4.2690 +01:18:55 4.2920 +01:19:00 4.2308 +01:19:05 4.3474 +01:19:10 4.2146 +01:19:15 4.1216 +01:19:20 4.2235 +01:19:25 4.3442 +01:19:30 4.3767 +01:19:35 4.2057 +01:19:40 4.2764 +01:19:45 4.3103 +01:19:50 4.3302 +01:19:55 4.2683 +01:20:00 4.2502 +01:20:05 4.3316 +01:20:10 4.3343 +01:20:15 4.2909 +01:20:20 4.3180 +01:20:25 4.3041 +01:20:30 4.3077 +01:20:35 4.4170 +01:20:40 4.3604 +01:20:45 4.3762 +01:20:50 4.3053 +01:20:55 4.2744 +01:21:00 4.2616 +01:21:05 4.3052 +01:21:10 4.3376 +01:21:15 4.3167 +01:21:20 4.2615 +01:21:25 4.3503 +01:21:30 4.2688 +01:21:35 4.2535 +01:21:40 4.3053 +01:21:45 4.2801 +01:21:50 4.2813 +01:21:55 4.1952 +01:22:00 4.2809 +01:22:05 4.2797 +01:22:10 4.1903 +01:22:15 4.2976 +01:22:20 4.3677 +01:22:25 4.3010 +01:22:30 4.3400 +01:22:35 4.2822 +01:22:40 4.2737 +01:22:45 4.3052 +01:22:50 4.3109 +01:22:55 4.2432 +01:23:00 4.2939 +01:23:05 4.3674 +01:23:10 4.2411 +01:23:15 4.3157 +01:23:20 4.2849 +01:23:25 4.2359 +01:23:30 4.2021 +01:23:35 4.2924 +01:23:40 4.3079 +01:23:45 4.2460 +01:23:50 4.1668 +01:23:55 4.2529 +01:24:00 4.4067 +01:24:05 4.3127 +01:24:10 4.2327 +01:24:15 4.1798 +01:24:20 4.2193 +01:24:25 4.2881 +01:24:30 4.3695 +01:24:35 4.3524 +01:24:40 4.3117 +01:24:45 4.2311 +01:24:50 4.2728 +01:24:55 4.3012 +01:25:00 4.1993 +01:25:05 4.2352 +01:25:10 4.2526 +01:25:15 4.3132 +01:25:20 4.3455 +01:25:25 4.3509 +01:25:30 4.2849 +01:25:35 4.2709 +01:25:40 4.2689 +01:25:45 4.3433 +01:25:50 4.3799 +01:25:55 4.2826 +01:26:00 4.2487 +01:26:05 4.2188 +01:26:10 4.3184 +01:26:15 4.2784 +01:26:20 4.2291 +01:26:25 4.1846 +01:26:30 4.2868 +01:26:35 4.3480 +01:26:40 4.2550 +01:26:45 4.1924 +01:26:50 4.2310 +01:26:55 4.2801 +01:27:00 4.1942 +01:27:05 4.2334 +01:27:10 4.2259 +01:27:15 4.0900 +01:27:20 4.0884 +01:27:25 4.1630 +01:27:30 4.3014 +01:27:35 4.2224 +01:27:40 4.2081 +01:27:45 4.2610 +01:27:50 4.3704 +01:27:55 4.4366 +01:28:00 4.4452 +01:28:05 4.3496 +01:28:10 4.2793 +01:28:15 4.2978 +01:28:20 4.3159 +01:28:25 4.3629 +01:28:30 4.3018 +01:28:35 4.3273 +01:28:40 4.4155 +01:28:45 4.2741 +01:28:50 4.2176 +01:28:55 4.2639 +01:29:00 4.1603 +01:29:05 4.1439 +01:29:10 4.2079 +01:29:15 4.2192 +01:29:20 4.2047 +01:29:25 4.2561 +01:29:30 4.3081 +01:29:35 4.2536 +01:29:40 4.2755 +01:29:45 4.3029 +01:29:50 4.2376 +01:29:55 4.3001 +01:30:00 4.3716 +01:30:05 4.2692 +01:30:10 4.2148 +01:30:15 4.2638 +01:30:20 4.2353 +01:30:25 4.2661 +01:30:30 4.2432 +01:30:35 4.2199 +01:30:40 4.2256 +01:30:45 4.1887 +01:30:50 4.2642 +01:30:55 4.2412 +01:31:00 4.2898 +01:31:05 4.3127 +01:31:10 4.3356 +01:31:15 4.2739 +01:31:20 4.2238 +01:31:25 4.3690 +01:31:30 4.3755 +01:31:35 4.3401 +01:31:40 4.3084 +01:31:45 4.2572 +01:31:50 4.3122 +01:31:55 4.2877 +01:32:00 4.2884 +01:32:05 4.2979 +01:32:10 4.3234 +01:32:15 4.3327 +01:32:20 4.2900 +01:32:25 4.2857 +01:32:30 4.1883 +01:32:35 4.3443 +01:32:40 4.3081 +01:32:45 4.2883 +01:32:50 4.2240 +01:32:55 4.2875 +01:33:00 4.1297 +01:33:05 4.3225 +01:33:10 4.3878 +01:33:15 4.3296 +01:33:20 4.2770 +01:33:25 4.2104 +01:33:30 4.2457 +01:33:35 4.1824 +01:33:40 4.2668 +01:33:45 4.2670 +01:33:50 4.2819 +01:33:55 4.3288 +01:34:00 4.2190 +01:34:05 4.2515 +01:34:10 4.2429 +01:34:15 4.1617 +01:34:20 4.2730 +01:34:25 4.3284 +01:34:30 4.3647 +01:34:35 4.2928 +01:34:40 4.2354 +01:34:45 4.2455 +01:34:50 4.1060 +01:34:55 4.1587 +01:35:00 4.2831 +01:35:05 4.2546 +01:35:10 4.1568 +01:35:15 4.2157 +01:35:20 4.3083 +01:35:25 4.2934 +01:35:30 4.2754 +01:35:35 4.2668 +01:35:40 4.2287 +01:35:45 4.1834 +01:35:50 4.2074 +01:35:55 4.2359 +01:36:00 4.2081 +01:36:05 4.1676 +01:36:10 4.1535 +01:36:15 4.1325 +01:36:20 4.1498 +01:36:25 4.2650 +01:36:30 4.3042 +01:36:35 4.2334 +01:36:40 4.1819 +01:36:45 4.2260 +01:36:50 4.2139 +01:36:55 4.1902 +01:37:00 4.2531 +01:37:05 4.2760 +01:37:10 4.2208 +01:37:15 4.2232 +01:37:20 4.3085 +01:37:25 4.2708 +01:37:30 4.1897 +01:37:35 4.2197 +01:37:40 4.1470 +01:37:45 4.2209 +01:37:50 4.2196 +01:37:55 4.1837 +01:38:00 4.2346 +01:38:05 4.2365 +01:38:10 4.2668 +01:38:15 4.2396 +01:38:20 4.3110 +01:38:25 4.2493 +01:38:30 4.2611 +01:38:35 4.2662 +01:38:40 4.2682 +01:38:45 4.3168 +01:38:50 4.2553 +01:38:55 4.2737 +01:39:00 4.1937 +01:39:05 4.1429 +01:39:10 4.1617 +01:39:15 4.2568 +01:39:20 4.2969 +01:39:25 4.2391 +01:39:30 4.2342 +01:39:35 4.1928 +01:39:40 4.3157 +01:39:45 4.2762 +01:39:50 4.2450 +01:39:55 4.1989 +01:40:00 4.2564 +01:40:05 4.2765 +01:40:10 4.2582 +01:40:15 4.1943 +01:40:20 4.1835 +01:40:25 4.0968 +01:40:30 4.0941 +01:40:35 4.1501 +01:40:40 4.2886 +01:40:45 4.3196 +01:40:50 4.2262 +01:40:55 4.2092 +01:41:00 4.1783 +01:41:05 4.1852 +01:41:10 4.2378 +01:41:15 4.2286 +01:41:20 4.2927 +01:41:25 4.2118 +01:41:30 4.2027 +01:41:35 4.3257 +01:41:40 4.1457 +01:41:45 4.2147 +01:41:50 4.2161 +01:41:55 4.2594 +01:42:00 4.2502 +01:42:05 4.2376 +01:42:10 4.2423 +01:42:15 4.2457 +01:42:20 4.1655 +01:42:25 4.1631 +01:42:30 4.2012 +01:42:35 4.1697 +01:42:40 4.1257 +01:42:45 4.1734 +01:42:50 4.2098 +01:42:55 4.2399 +01:43:00 4.2934 +01:43:05 4.2841 +01:43:10 4.2590 +01:43:15 4.1963 +01:43:20 4.2217 +01:43:25 4.2081 +01:43:30 4.2363 +01:43:35 4.2559 +01:43:40 4.2674 +01:43:45 4.2594 +01:43:50 4.2685 +01:43:55 4.2744 +01:44:00 4.2889 +01:44:05 4.2594 +01:44:10 4.2123 +01:44:15 4.3099 +01:44:20 4.1992 +01:44:25 4.1929 +01:44:30 4.1578 +01:44:35 4.2486 +01:44:40 4.2728 +01:44:45 4.2610 +01:44:50 4.2551 +01:44:55 4.2723 +01:45:00 4.2300 +01:45:05 4.2470 +01:45:10 4.2280 +01:45:15 4.2317 +01:45:20 4.2540 +01:45:25 4.2366 +01:45:30 4.2608 +01:45:35 4.3071 +01:45:40 4.2898 +01:45:45 4.2723 +01:45:50 4.2507 +01:45:55 4.2628 +01:46:00 4.2086 +01:46:05 4.1165 +01:46:10 4.1797 +01:46:15 4.1349 +01:46:20 4.1984 +01:46:25 4.1699 +01:46:30 4.2308 +01:46:35 4.1957 +01:46:40 4.1803 +01:46:45 4.1799 +01:46:50 4.2133 +01:46:55 4.2774 +01:47:00 4.3289 +01:47:05 4.2297 +01:47:10 4.2222 +01:47:15 4.2664 +01:47:20 4.2692 +01:47:25 4.1359 +01:47:30 4.1273 +01:47:35 4.1603 +01:47:40 4.2406 +01:47:45 4.3238 +01:47:50 4.3462 +01:47:55 4.1751 +01:48:00 4.2187 +01:48:05 4.1814 +01:48:10 4.1655 +01:48:15 4.2362 +01:48:20 4.2035 +01:48:25 4.3006 +01:48:30 4.2959 +01:48:35 4.1243 +01:48:40 4.1373 +01:48:45 4.1800 +01:48:50 4.1638 +01:48:55 4.1661 +01:49:00 4.1770 +01:49:05 4.1154 +01:49:10 4.1435 +01:49:15 4.2417 +01:49:20 4.1634 +01:49:25 4.1975 +01:49:30 4.2265 +01:49:35 4.2450 +01:49:40 4.2221 +01:49:45 4.2714 +01:49:50 4.2248 +01:49:55 4.2468 +01:50:00 4.2673 +01:50:05 4.1332 +01:50:10 4.0830 +01:50:15 4.1515 +01:50:20 4.1578 +01:50:25 4.1853 +01:50:30 4.2576 +01:50:35 4.2916 +01:50:40 4.2545 +01:50:45 4.1881 +01:50:50 4.2642 +01:50:55 4.1965 +01:51:00 4.1919 +01:51:05 4.2664 +01:51:10 4.2576 +01:51:15 4.1617 +01:51:20 4.0520 +01:51:25 4.1565 +01:51:30 4.1739 +01:51:35 4.1447 +01:51:40 4.1384 +01:51:45 4.0848 +01:51:50 4.1338 +01:51:55 4.1580 +01:52:00 4.1424 +01:52:05 4.1841 +01:52:10 4.3262 +01:52:15 4.3730 +01:52:20 4.2504 +01:52:25 4.1584 +01:52:30 4.1602 +01:52:35 4.2115 +01:52:40 4.1357 +01:52:45 4.1133 +01:52:50 4.2357 +01:52:55 4.3365 +01:53:00 4.3347 +01:53:05 4.1840 +01:53:10 4.1411 +01:53:15 4.1639 +01:53:20 4.2088 +01:53:25 4.1393 +01:53:30 4.1373 +01:53:35 4.2098 +01:53:40 4.1607 +01:53:45 4.2383 +01:53:50 4.3454 +01:53:55 4.2582 +01:54:00 4.2127 +01:54:05 4.2903 +01:54:10 4.1963 +01:54:15 4.2025 +01:54:20 4.1597 +01:54:25 4.1388 +01:54:30 4.1784 +01:54:35 4.1066 +01:54:40 4.1775 +01:54:45 4.0933 +01:54:50 4.1585 +01:54:55 4.2532 +01:55:00 4.1524 +01:55:05 4.1612 +01:55:10 4.2077 +01:55:15 4.1442 +01:55:20 4.0676 +01:55:25 4.0646 +01:55:30 4.1976 +01:55:35 4.2229 +01:55:40 4.1372 +01:55:45 4.2025 +01:55:50 4.1936 +01:55:55 4.1025 +01:56:00 4.1542 +01:56:05 4.2241 +01:56:10 4.2420 +01:56:15 4.2951 +01:56:20 4.2478 +01:56:25 4.2304 +01:56:30 4.1308 +01:56:35 4.1927 +01:56:40 4.2100 +01:56:45 4.1867 +01:56:50 4.1005 +01:56:55 4.1250 +01:57:00 4.2474 +01:57:05 4.0925 +01:57:10 4.0985 +01:57:15 4.1772 +01:57:20 4.2640 +01:57:25 4.3186 +01:57:30 4.3245 +01:57:35 4.1488 +01:57:40 4.1370 +01:57:45 4.1608 +01:57:50 4.1749 +01:57:55 4.2099 +01:58:00 4.1796 +01:58:05 4.2413 +01:58:10 4.1701 +01:58:15 4.1882 +01:58:20 4.2289 +01:58:25 4.1413 +01:58:30 4.1463 +01:58:35 4.2139 +01:58:40 4.2376 +01:58:45 4.2608 +01:58:50 4.1610 +01:58:55 4.1512 +01:59:00 4.1924 +01:59:05 4.1531 +01:59:10 4.2604 +01:59:15 4.2036 +01:59:20 4.1720 +01:59:25 4.2576 +01:59:30 4.2635 +01:59:35 4.2920 +01:59:40 4.2709 +01:59:45 4.1864 +01:59:50 4.1624 +01:59:55 4.2660 +02:00:00 4.2869 +02:00:05 4.2162 +02:00:10 4.2257 +02:00:15 4.2174 +02:00:20 4.2145 +02:00:25 4.1686 +02:00:30 4.1531 +02:00:35 4.2616 +02:00:40 4.1421 +02:00:45 4.1592 +02:00:50 4.1543 +02:00:57 4.2177 +02:01:00 4.1393 +02:01:05 4.0961 +02:01:10 4.2240 +02:01:15 4.2032 +02:01:20 4.2786 +02:01:26 4.1941 +02:01:33 4.2383 +02:01:35 4.1760 +02:01:40 4.1527 +02:01:45 4.2268 +02:01:50 4.1889 +02:01:55 4.1927 +02:02:04 4.1210 +02:02:05 4.0578 +02:02:10 4.2631 +02:02:17 4.1613 +02:02:20 4.2224 +02:02:25 4.2431 +02:02:30 4.2004 +02:02:38 4.2561 +02:02:40 4.1968 +02:02:54 4.2976 +02:02:55 4.2810 +02:03:04 4.2890 +02:03:05 4.2452 +02:03:11 4.1637 +02:03:15 4.1673 +02:03:23 4.1914 +02:03:25 4.1546 +02:03:30 4.1526 +02:03:35 4.2043 +02:03:40 4.1873 +02:03:45 4.1885 +02:03:50 4.1001 +02:03:55 4.2377 +02:04:00 4.1227 +02:04:05 4.1880 +02:04:10 4.2458 +02:04:15 4.2478 +02:04:20 4.2256 +02:04:25 4.1936 +02:04:30 4.1542 +02:04:35 4.2099 +02:04:40 4.1645 +02:04:45 4.2206 +02:04:50 4.1015 +02:04:55 4.2085 +02:05:00 4.1834 +02:05:05 4.1393 +02:05:10 4.1510 +02:05:15 4.2278 +02:05:20 4.2378 +02:05:25 4.2219 +02:05:30 4.1474 +02:05:35 4.0868 +02:05:40 4.1444 +02:05:45 4.2461 +02:05:50 4.1874 +02:05:55 4.1083 +02:06:00 4.1602 +02:06:05 4.2128 +02:06:10 4.2630 +02:06:15 4.3383 +02:06:20 4.2578 +02:06:25 4.2083 +02:06:30 4.2456 +02:06:35 4.2095 +02:06:40 4.1901 +02:06:45 4.1899 +02:06:50 4.2019 +02:06:55 4.2836 +02:07:00 4.1863 +02:07:05 4.2520 +02:07:10 4.1594 +02:07:15 4.1631 +02:07:20 4.2255 +02:07:25 4.2283 +02:07:30 4.2226 +02:07:35 4.1901 +02:07:40 4.1505 +02:07:45 4.1915 +02:07:50 4.1757 +02:07:55 4.1837 +02:08:00 4.2191 +02:08:05 4.1176 +02:08:10 4.3030 +02:08:15 4.2399 +02:08:20 4.1212 +02:08:25 4.0837 +02:08:30 4.2064 +02:08:35 4.2857 +02:08:40 4.1326 +02:08:45 4.1940 +02:08:50 4.1988 +02:08:55 4.1505 +02:09:00 4.0519 +02:09:05 4.1586 +02:09:10 4.1768 +02:09:15 4.1767 +02:09:20 4.2283 +02:09:25 4.2380 +02:09:30 4.1900 +02:09:35 4.1687 +02:09:40 4.2090 +02:09:45 4.1846 +02:09:50 4.2362 +02:09:55 4.1410 +02:10:00 4.0728 +02:10:05 4.1472 +02:10:10 4.1651 +02:10:15 4.2404 +02:10:20 4.2664 +02:10:25 4.2732 +02:10:30 4.1561 +02:10:35 4.1633 +02:10:40 4.1870 +02:10:45 4.2008 +02:10:50 4.1688 +02:10:55 4.2411 +02:11:00 4.2295 +02:11:05 4.1135 +02:11:10 4.1679 +02:11:15 4.1828 +02:11:20 4.3140 +02:11:25 4.2713 +02:11:30 4.2126 +02:11:35 4.1046 +02:11:40 4.1238 +02:11:45 4.1780 +02:11:50 4.1458 +02:11:55 4.1722 +02:12:00 4.1893 +02:12:05 4.1432 +02:12:10 4.1492 +02:12:15 4.2084 +02:12:20 4.2075 +02:12:25 4.1427 +02:12:30 4.1393 +02:12:35 4.1441 +02:12:40 4.2886 +02:12:45 4.2406 +02:12:50 4.1813 +02:12:55 4.2433 +02:13:00 4.2855 +02:13:05 4.1842 +02:13:10 4.1000 +02:13:15 4.1187 +02:13:20 3.9957 +02:13:25 4.0570 +02:13:30 4.1108 +02:13:35 4.0943 +02:13:40 4.1335 +02:13:45 4.1902 +02:13:50 4.1332 +02:13:55 4.1841 +02:14:00 4.2023 +02:14:05 4.0799 +02:14:10 4.1584 +02:14:15 4.2099 +02:14:20 4.1308 +02:14:25 4.0924 +02:14:30 4.1697 +02:14:35 4.2515 +02:14:40 4.1511 +02:14:45 4.1536 +02:14:50 4.1607 +02:14:55 4.1614 +02:15:00 4.1799 +02:15:05 4.1321 +02:15:10 4.1604 +02:15:15 4.1079 +02:15:20 4.1907 +02:15:25 4.2797 +02:15:30 4.1837 +02:15:35 4.1903 +02:15:40 4.2137 +02:15:45 4.1559 +02:15:50 4.1595 +02:15:55 4.0787 +02:16:00 4.1085 +02:16:05 4.1644 +02:16:10 4.1593 +02:16:15 4.1139 +02:16:20 4.1045 +02:16:25 4.1906 +02:16:30 4.1429 +02:16:35 4.1104 +02:16:40 4.1160 +02:16:45 4.1655 +02:16:50 4.2069 +02:16:55 4.1726 +02:17:00 4.1455 +02:17:05 4.1373 +02:17:10 4.1775 +02:17:15 4.1469 +02:17:20 4.1154 +02:17:25 4.1441 +02:17:30 4.1747 +02:17:35 4.1642 +02:17:40 4.1866 +02:17:45 4.1925 +02:17:50 4.1631 +02:17:55 4.1778 +02:18:00 4.1929 +02:18:05 4.1938 +02:18:10 4.1939 +02:18:15 4.1975 +02:18:20 4.1560 +02:18:25 4.0924 +02:18:30 4.0546 +02:18:35 4.0638 +02:18:40 4.1209 +02:18:45 4.1445 +02:18:50 4.2099 +02:18:55 4.1579 +02:19:00 4.1800 +02:19:05 4.1459 +02:19:10 4.2127 +02:19:15 4.1959 +02:19:20 4.1844 +02:19:25 4.1316 +02:19:30 4.2238 +02:19:35 4.1779 +02:19:40 4.1592 +02:19:45 4.1347 +02:19:50 4.1292 +02:19:55 4.2150 +02:20:00 4.1576 +02:20:05 4.1293 +02:20:10 4.2231 +02:20:15 4.2236 +02:20:20 4.1652 +02:20:25 4.1424 +02:20:30 4.1101 +02:20:35 4.1493 +02:20:40 4.2230 +02:20:45 4.1677 +02:20:50 4.1060 +02:20:55 4.0078 +02:21:00 4.0629 +02:21:05 4.1757 +02:21:10 4.1624 +02:21:15 4.1619 +02:21:20 4.1910 +02:21:25 4.1314 +02:21:30 4.1274 +02:21:35 4.0989 +02:21:40 4.0430 +02:21:45 4.1582 +02:21:50 4.1620 +02:21:55 4.1397 +02:22:00 4.1915 +02:22:05 4.1760 +02:22:10 4.1728 +02:22:15 4.1563 +02:22:20 4.0636 +02:22:25 4.1132 +02:22:30 4.0741 +02:22:35 3.9961 +02:22:40 4.2086 +02:22:45 4.1362 +02:22:50 4.1086 +02:22:55 4.2161 +02:23:00 4.2087 +02:23:05 4.1678 +02:23:10 4.1245 +02:23:15 4.1171 +02:23:20 4.1182 +02:23:25 4.1488 +02:23:30 4.1352 +02:23:35 4.1624 +02:23:40 4.1783 +02:23:45 4.1696 +02:23:50 4.1530 +02:23:55 4.1571 +02:24:00 4.1551 +02:24:05 4.1015 +02:24:10 4.1744 +02:24:15 4.1372 +02:24:20 4.1478 +02:24:25 4.1699 +02:24:30 4.1840 +02:24:35 4.2447 +02:24:40 4.2078 +02:24:45 4.1995 +02:24:50 4.2052 +02:24:55 4.2432 +02:25:00 4.1355 +02:25:05 4.0729 +02:25:10 4.1021 +02:25:15 4.1477 +02:25:20 4.2625 +02:25:25 4.2272 +02:25:30 4.0843 +02:25:35 3.9715 +02:25:40 4.1757 +02:25:45 4.0842 +02:25:50 4.0888 +02:25:55 4.0031 +02:26:00 4.1396 +02:26:05 4.1742 +02:26:10 4.1386 +02:26:15 4.2124 +02:26:20 4.2847 +02:26:25 4.2304 +02:26:30 4.1550 +02:26:35 4.1215 +02:26:40 4.0619 +02:26:45 4.1776 +02:26:50 4.1781 +02:26:55 4.0659 +02:27:00 4.0054 +02:27:05 4.1167 +02:27:10 4.2470 +02:27:15 4.2732 +02:27:20 4.2631 +02:27:25 4.2288 +02:27:30 4.1261 +02:27:35 4.0833 +02:27:40 4.1067 +02:27:45 4.1005 +02:27:50 4.0911 +02:27:55 4.1113 +02:28:00 4.1578 +02:28:05 4.2158 +02:28:10 4.2389 +02:28:15 4.1523 +02:28:20 4.0341 +02:28:25 4.0798 +02:28:30 4.1384 +02:28:35 4.1562 +02:28:40 4.1895 +02:28:45 4.2099 +02:28:50 4.0879 +02:28:55 4.0612 +02:29:00 4.2817 +02:29:05 4.3487 +02:29:10 4.2034 +02:29:15 4.1399 +02:29:20 4.1486 +02:29:25 4.1208 +02:29:30 4.1977 +02:29:35 4.1000 +02:29:40 4.0890 +02:29:45 4.1176 +02:29:50 4.1399 +02:29:55 4.1026 +02:30:00 4.1313 +02:30:05 4.2516 +02:30:10 4.1480 +02:30:15 4.0434 +02:30:20 4.1688 +02:30:25 4.1513 +02:30:30 4.1302 +02:30:35 4.2523 +02:30:40 4.1802 +02:30:45 4.0270 +02:30:50 4.1505 +02:30:55 4.2245 +02:31:00 4.2088 +02:31:05 4.2055 +02:31:10 4.0597 +02:31:15 4.0762 +02:31:20 4.1495 +02:31:25 4.1026 +02:31:30 4.1775 +02:31:35 4.1374 +02:31:40 4.1534 +02:31:45 4.1217 +02:31:50 4.1739 +02:31:55 4.1841 +02:32:00 4.1247 +02:32:05 4.1161 +02:32:10 4.1725 +02:32:15 4.1800 +02:32:20 4.1806 +02:32:25 4.0624 +02:32:30 4.0874 +02:32:35 4.0759 +02:32:40 4.0354 +02:32:45 4.0869 +02:32:50 4.1132 +02:32:55 4.0266 +02:33:00 4.1799 +02:33:05 4.2289 +02:33:10 4.0643 +02:33:15 4.1263 +02:33:20 4.1694 +02:33:25 4.0679 +02:33:30 4.0708 +02:33:35 4.0465 +02:33:40 4.1215 +02:33:45 4.1559 +02:33:50 4.1174 +02:33:55 4.0351 +02:34:00 4.1682 +02:34:05 4.1579 +02:34:10 4.0796 +02:34:15 4.0866 +02:34:20 4.0562 +02:34:25 4.0234 +02:34:30 4.0912 +02:34:35 4.1336 +02:34:40 4.1380 +02:34:45 4.2108 +02:34:50 4.1740 +02:34:55 4.2215 +02:35:00 4.2123 +02:35:05 4.1473 +02:35:10 4.2102 +02:35:15 4.1829 +02:35:20 4.1742 +02:35:25 4.1526 +02:35:30 4.1790 +02:35:35 4.1885 +02:35:40 4.0966 +02:35:45 4.1016 +02:35:50 4.0770 +02:35:55 4.1873 +02:36:00 4.1292 +02:36:05 4.0955 +02:36:10 4.0837 +02:36:15 4.1253 +02:36:20 4.2201 +02:36:25 4.1908 +02:36:30 4.1791 +02:36:35 4.1767 +02:36:40 4.0042 +02:36:45 4.0460 +02:36:50 4.2050 +02:36:55 4.2273 +02:37:00 4.2083 +02:37:05 4.1263 +02:37:10 4.1522 +02:37:15 4.1717 +02:37:20 4.2156 +02:37:25 4.1146 +02:37:30 4.1627 +02:37:35 4.2009 +02:37:40 4.1631 +02:37:45 4.1166 +02:37:50 4.1653 +02:37:55 4.2368 +02:38:00 4.1604 +02:38:05 4.1990 +02:38:10 4.2026 +02:38:15 4.1110 +02:38:20 4.0789 +02:38:25 3.9994 +02:38:30 4.0533 +02:38:35 4.1610 +02:38:40 4.1382 +02:38:45 4.1700 +02:38:50 4.1390 +02:38:55 4.1865 +02:39:00 4.1247 +02:39:05 4.0953 +02:39:10 4.1854 +02:39:15 4.1128 +02:39:20 4.1533 +02:39:25 4.1766 +02:39:30 4.1894 +02:39:35 4.0903 +02:39:40 4.1487 +02:39:45 4.1484 +02:39:50 4.1423 +02:39:55 4.1871 +02:40:00 4.1722 +02:40:05 4.1034 +02:40:10 4.1258 +02:40:15 4.1105 +02:40:20 4.1927 +02:40:25 4.1886 +02:40:30 4.1579 +02:40:35 4.1461 +02:40:40 4.1335 +02:40:45 4.1607 +02:40:50 4.1729 +02:40:55 4.1343 +02:41:00 4.1075 +02:41:05 4.0258 +02:41:10 4.1284 +02:41:15 4.1438 +02:41:20 4.1218 +02:41:25 4.1181 +02:41:30 4.0841 +02:41:35 4.1964 +02:41:40 4.1918 +02:41:45 4.1610 +02:41:50 4.1043 +02:41:55 4.0863 +02:42:00 4.1567 +02:42:05 4.0846 +02:42:10 4.1127 +02:42:15 4.1260 +02:42:20 4.1477 +02:42:25 4.2246 +02:42:30 4.2126 +02:42:35 4.1910 +02:42:40 4.0283 +02:42:45 4.1561 +02:42:50 4.1828 +02:42:55 4.0757 +02:43:00 4.0900 +02:43:05 4.1173 +02:43:10 4.1026 +02:43:15 4.0553 +02:43:20 4.1566 +02:43:25 4.2606 +02:43:30 4.2622 +02:43:35 4.2466 +02:43:40 4.1505 +02:43:45 4.1419 +02:43:50 4.0965 +02:43:55 4.1334 +02:44:00 4.1435 +02:44:05 4.1135 +02:44:10 4.1799 +02:44:15 4.1538 +02:44:20 4.1534 +02:44:25 4.0787 +02:44:30 4.0327 +02:44:35 4.0828 +02:44:40 4.2327 +02:44:45 4.2298 +02:44:50 4.1914 +02:44:55 4.1753 +02:45:00 4.2077 +02:45:05 4.1789 +02:45:10 4.1985 +02:45:15 4.2293 +02:45:20 4.1592 +02:45:25 4.1241 +02:45:30 4.1730 +02:45:35 4.1365 +02:45:40 4.0295 +02:45:45 4.1047 +02:45:50 4.1216 +02:45:55 4.1105 +02:46:00 4.0771 +02:46:05 4.0411 +02:46:10 4.0778 +02:46:15 3.9794 +02:46:20 4.0359 +02:46:25 4.0793 +02:46:30 4.0933 +02:46:35 4.1006 +02:46:40 4.1307 +02:46:45 4.1044 +02:46:50 4.1122 +02:46:55 4.0169 +02:47:00 4.1364 +02:47:05 4.1860 +02:47:10 4.1452 +02:47:15 4.1383 +02:47:20 4.1360 +02:47:25 4.1525 +02:47:30 4.1283 +02:47:35 4.1275 +02:47:40 4.0927 +02:47:45 4.1161 +02:47:50 4.0778 +02:47:55 4.0021 +02:48:00 4.0826 +02:48:05 4.1117 +02:48:10 4.0457 +02:48:15 4.0271 +02:48:20 4.0872 +02:48:25 4.0760 +02:48:30 4.0597 +02:48:35 4.0439 +02:48:40 4.1604 +02:48:45 4.1231 +02:48:50 4.1584 +02:48:55 4.0520 +02:49:00 4.0423 +02:49:05 4.0744 +02:49:10 4.0702 +02:49:15 4.1211 +02:49:20 4.0647 +02:49:25 4.0743 +02:49:30 4.1030 +02:49:35 4.0490 +02:49:40 4.0653 +02:49:45 4.1020 +02:49:50 4.1297 +02:49:55 4.2229 +02:50:00 4.2171 +02:50:05 4.0967 +02:50:10 4.1476 +02:50:15 4.1656 +02:50:20 4.1595 +02:50:25 4.1204 +02:50:30 4.1451 +02:50:35 4.2053 +02:50:40 4.2125 +02:50:45 4.0964 +02:50:50 4.1665 +02:50:55 4.1531 +02:51:00 4.0733 +02:51:05 4.1222 +02:51:10 4.1475 +02:51:15 4.1120 +02:51:20 4.1071 +02:51:25 4.0775 +02:51:30 4.1331 +02:51:35 4.2072 +02:51:40 4.1793 +02:51:45 4.1334 +02:51:50 4.0925 +02:51:55 4.1463 +02:52:00 4.1764 +02:52:05 4.0773 +02:52:10 4.1061 +02:52:15 4.0837 +02:52:20 3.9810 +02:52:25 4.0003 +02:52:30 4.1147 +02:52:35 4.1598 +02:52:40 4.1805 +02:52:45 4.1585 +02:52:50 4.0546 +02:52:55 4.0306 +02:53:00 4.0552 +02:53:05 4.1020 +02:53:10 4.2909 +02:53:15 4.2427 +02:53:20 4.1641 +02:53:25 4.1636 +02:53:30 4.1516 +02:53:35 4.1762 +02:53:40 4.2587 +02:53:45 4.2502 +02:53:50 4.2213 +02:53:55 4.2318 +02:54:00 4.1470 +02:54:05 4.1614 +02:54:10 4.1091 +02:54:15 4.0696 +02:54:20 4.0259 +02:54:25 3.9875 +02:54:30 4.0774 +02:54:35 4.0842 +02:54:40 4.0771 +02:54:45 4.1425 +02:54:50 4.1238 +02:54:55 4.0883 +02:55:00 4.0167 +02:55:05 4.0868 +02:55:10 4.1583 +02:55:15 4.2231 +02:55:20 4.1593 +02:55:25 4.1239 +02:55:30 4.0983 +02:55:35 4.1409 +02:55:40 4.1461 +02:55:45 4.0387 +02:55:50 4.1010 +02:55:55 4.2396 +02:56:00 4.1935 +02:56:05 4.0276 +02:56:10 4.0077 +02:56:15 4.0500 +02:56:20 4.2064 +02:56:25 4.0581 +02:56:30 4.1043 +02:56:35 4.2815 +02:56:40 4.2406 +02:56:45 4.1377 +02:56:50 3.9394 +02:56:55 3.9447 +02:57:00 4.0772 +02:57:05 4.1259 +02:57:10 4.2053 +02:57:15 4.1411 +02:57:20 4.0934 +02:57:25 4.2224 +02:57:30 4.2731 +02:57:35 4.1185 +02:57:40 4.1182 +02:57:45 4.0903 +02:57:50 4.0693 +02:57:55 4.0738 +02:58:00 4.0514 +02:58:05 4.1283 +02:58:10 4.1135 +02:58:15 4.0932 +02:58:20 4.1098 +02:58:25 4.1166 +02:58:30 4.1771 +02:58:35 4.1442 +02:58:40 4.1452 +02:58:45 4.1107 +02:58:50 4.0279 +02:58:55 4.0332 +02:59:00 4.0770 +02:59:05 4.1350 +02:59:10 4.2132 +02:59:15 4.1704 +02:59:20 4.1093 +02:59:25 4.1111 +02:59:30 4.0772 +02:59:35 4.0856 +02:59:40 4.1295 +02:59:45 4.1113 +02:59:50 4.0839 +02:59:55 4.1054 +03:00:00 4.0688 +03:00:05 4.1299 +03:00:10 4.0491 +03:00:15 4.0669 +03:00:20 4.1296 +03:00:25 4.0701 +03:00:30 4.1557 +03:00:35 4.0725 +03:00:40 4.1471 +03:00:45 4.1281 +03:00:50 4.1193 +03:00:55 4.0028 +03:01:00 4.1313 +03:01:05 4.0617 +03:01:10 4.0163 +03:01:15 4.0496 +03:01:20 4.1076 +03:01:25 4.0784 +03:01:30 4.0805 +03:01:35 4.1099 +03:01:40 4.0485 +03:01:45 4.1234 +03:01:50 4.1263 +03:01:55 4.1518 +03:02:00 4.1629 +03:02:05 4.1010 +03:02:10 4.0419 +03:02:15 4.0679 +03:02:20 4.0246 +03:02:25 4.0989 +03:02:30 4.0984 +03:02:35 4.1170 +03:02:40 4.1115 +03:02:45 4.1782 +03:02:50 4.1394 +03:02:55 4.0243 +03:03:00 4.1398 +03:03:05 4.1272 +03:03:10 4.1218 +03:03:15 4.1797 +03:03:20 4.0746 +03:03:25 4.0411 +03:03:30 4.0641 +03:03:35 4.0930 +03:03:40 4.1379 +03:03:45 4.0847 +03:03:50 4.0956 +03:03:55 4.1175 +03:04:00 4.1251 +03:04:05 4.1154 +03:04:10 4.1951 +03:04:15 4.1457 +03:04:20 4.0859 +03:04:25 4.1019 +03:04:30 4.2113 +03:04:35 4.1909 +03:04:40 4.0402 +03:04:45 4.0572 +03:04:50 4.0732 +03:04:55 4.0113 +03:05:00 4.0661 +03:05:05 4.2068 +03:05:10 4.1588 +03:05:15 4.1435 +03:05:20 4.1476 +03:05:25 4.0680 +03:05:30 4.0687 +03:05:35 4.1901 +03:05:40 4.2462 +03:05:45 4.1328 +03:05:50 4.1404 +03:05:55 4.1439 +03:06:00 4.1157 +03:06:05 4.1096 +03:06:10 4.1543 +03:06:15 4.1000 +03:06:20 4.1197 +03:06:25 4.1629 +03:06:30 4.1778 +03:06:35 4.1347 +03:06:40 4.1694 +03:06:45 4.0586 +03:06:50 4.1128 +03:06:55 4.1279 +03:07:00 4.1506 +03:07:05 4.1491 +03:07:10 4.1245 +03:07:15 4.1581 +03:07:20 4.1156 +03:07:25 4.0766 +03:07:30 4.1468 +03:07:35 4.1760 +03:07:40 4.1077 +03:07:45 4.1175 +03:07:50 4.1451 +03:07:55 4.0797 +03:08:00 4.1240 +03:08:05 4.1027 +03:08:10 4.1440 +03:08:15 4.1360 +03:08:20 4.0976 +03:08:25 4.0771 +03:08:30 4.0802 +03:08:35 4.1210 +03:08:40 4.1056 +03:08:45 4.1336 +03:08:50 4.1100 +03:08:55 4.0467 +03:09:00 4.0225 +03:09:05 4.0678 +03:09:10 4.0075 +03:09:15 4.0357 +03:09:20 4.0428 +03:09:25 4.0406 +03:09:30 4.0600 +03:09:35 4.0433 +03:09:40 4.1408 +03:09:45 4.1136 +03:09:50 4.1204 +03:09:55 4.0246 +03:10:00 4.0828 +03:10:05 4.1390 +03:10:10 4.0488 +03:10:15 4.0668 +03:10:20 4.0832 +03:10:25 4.1146 +03:10:30 4.0666 +03:10:35 4.0692 +03:10:40 4.1054 +03:10:45 4.0989 +03:10:50 4.0424 +03:10:55 4.0947 +03:11:00 4.0327 +03:11:05 3.9934 +03:11:10 4.0688 +03:11:15 4.0088 +03:11:20 4.1014 +03:11:25 4.0932 +03:11:30 4.0402 +03:11:35 4.1291 +03:11:40 4.1372 +03:11:45 4.0839 +03:11:50 4.0783 +03:11:55 4.1791 +03:12:00 4.1564 +03:12:05 4.0737 +03:12:10 4.1292 +03:12:15 4.1274 +03:12:20 4.1224 +03:12:25 4.0596 +03:12:30 4.0650 +03:12:35 4.0860 +03:12:40 4.0893 +03:12:45 4.1373 +03:12:50 4.1264 +03:12:55 4.1000 +03:13:00 4.1198 +03:13:05 4.0719 +03:13:10 4.0167 +03:13:15 4.1536 +03:13:20 4.1055 +03:13:25 4.0760 +03:13:30 4.0403 +03:13:35 4.0570 +03:13:40 4.0695 +03:13:45 4.0837 +03:13:50 4.1057 +03:13:55 4.1097 +03:14:00 4.1634 +03:14:05 4.0933 +03:14:10 4.0823 +03:14:15 4.0529 +03:14:20 4.1354 +03:14:25 4.1684 +03:14:30 4.2188 +03:14:35 4.1617 +03:14:40 4.0973 +03:14:45 4.0808 +03:14:50 4.0971 +03:14:55 4.1060 +03:15:00 4.1402 +03:15:05 4.0381 +03:15:10 4.0710 +03:15:15 4.1055 +03:15:20 4.1075 +03:15:25 4.1232 +03:15:30 4.0364 +03:15:35 4.0091 +03:15:40 3.9933 +03:15:45 3.8853 +03:15:50 4.0392 +03:15:55 4.0995 +03:16:00 4.1262 +03:16:05 4.0573 +03:16:10 4.0465 +03:16:15 3.9943 +03:16:20 4.0505 +03:16:25 4.1423 +03:16:30 4.2078 +03:16:35 4.1614 +03:16:40 4.1824 +03:16:45 4.1035 +03:16:50 4.0936 +03:16:55 4.0976 +03:17:00 4.0418 +03:17:05 4.1097 +03:17:10 4.1738 +03:17:15 4.1959 +03:17:20 4.1691 +03:17:25 4.1501 +03:17:30 4.0586 +03:17:35 4.0942 +03:17:40 4.1271 +03:17:45 4.0632 +03:17:50 4.0544 +03:17:55 4.1258 +03:18:00 4.1190 +03:18:05 4.0506 +03:18:10 4.0809 +03:18:15 4.1472 +03:18:20 4.1121 +03:18:25 4.1344 +03:18:30 4.1406 +03:18:35 4.1596 +03:18:40 4.1191 +03:18:45 4.1143 +03:18:50 4.1009 +03:18:55 4.1065 +03:19:00 4.0991 +03:19:05 4.0660 +03:19:10 4.1063 +03:19:15 4.1200 +03:19:20 4.0719 +03:19:25 4.1635 +03:19:30 4.1574 +03:19:35 4.1018 +03:19:40 4.1351 +03:19:45 4.1640 +03:19:50 4.2062 +03:19:55 4.0675 +03:20:00 4.0413 +03:20:05 4.1516 +03:20:10 4.1455 +03:20:15 4.0872 +03:20:20 4.0921 +03:20:25 4.1274 +03:20:30 4.1323 +03:20:35 4.0566 +03:20:40 4.0706 +03:20:45 4.0695 +03:20:50 4.0917 +03:20:55 4.1306 +03:21:00 4.0980 +03:21:05 4.0703 +03:21:10 4.0007 +03:21:15 4.0700 +03:21:20 4.1399 +03:21:25 4.1356 +03:21:30 4.1054 +03:21:35 4.1969 +03:21:40 4.1810 +03:21:45 4.1381 +03:21:50 4.0862 +03:21:55 4.0516 +03:22:00 4.0927 +03:22:05 4.1543 +03:22:10 4.1429 +03:22:15 4.0967 +03:22:20 4.1220 +03:22:25 4.1505 +03:22:30 4.0861 +03:22:35 4.0619 +03:22:40 4.1568 +03:22:45 4.0920 +03:22:50 4.0637 +03:22:55 4.0182 +03:23:00 4.0144 +03:23:05 4.0843 +03:23:10 4.0707 +03:23:15 4.1107 +03:23:20 4.0430 +03:23:25 4.0661 +03:23:30 4.0051 +03:23:35 3.9915 +03:23:40 4.0533 +03:23:45 4.0918 +03:23:50 4.1738 +03:23:55 4.1084 +03:24:00 4.0252 +03:24:05 4.1564 +03:24:10 4.1298 +03:24:15 4.1455 +03:24:20 4.1364 +03:24:25 4.0543 +03:24:30 4.0216 +03:24:35 4.1261 +03:24:40 4.1728 +03:24:45 4.0437 +03:24:50 4.0671 +03:24:55 4.1263 +03:25:00 4.0551 +03:25:05 4.0718 +03:25:10 4.1349 +03:25:15 4.0825 +03:25:20 4.0279 +03:25:25 4.0257 +03:25:30 4.1480 +03:25:35 4.1636 +03:25:40 4.1250 +03:25:45 4.1213 +03:25:50 4.0553 +03:25:55 4.1455 +03:26:00 4.2220 +03:26:05 4.1360 +03:26:10 4.0771 +03:26:15 4.0674 +03:26:20 4.1989 +03:26:25 4.1933 +03:26:30 4.2114 +03:26:35 4.0949 +03:26:40 4.0010 +03:26:45 4.0408 +03:26:50 3.9995 +03:26:55 4.0116 +03:27:00 4.0847 +03:27:05 4.0871 +03:27:10 4.0559 +03:27:15 4.0309 +03:27:20 4.0139 +03:27:25 4.0344 +03:27:30 4.0061 +03:27:35 3.9770 +03:27:40 4.0691 +03:27:45 4.0890 +03:27:50 4.1624 +03:27:55 4.1714 +03:28:00 4.1638 +03:28:05 4.0514 +03:28:10 3.9973 +03:28:15 4.0042 +03:28:20 4.0733 +03:28:25 4.1493 +03:28:30 4.2131 +03:28:35 4.1347 +03:28:40 4.0391 +03:28:45 3.9889 +03:28:50 4.0210 +03:28:55 4.0736 +03:29:00 4.1372 +03:29:05 4.1210 +03:29:10 4.1327 +03:29:15 4.1271 +03:29:20 4.1268 +03:29:25 4.0642 +03:29:30 4.0466 +03:29:35 4.1035 +03:29:40 4.1575 +03:29:45 4.1185 +03:29:50 4.1214 +03:29:55 4.1195 +03:30:00 4.1219 +03:30:05 4.1849 +03:30:10 4.2003 +03:30:15 4.1411 +03:30:20 4.1188 +03:30:25 4.1439 +03:30:30 4.1558 +03:30:35 4.1287 +03:30:40 4.0971 +03:30:45 4.1250 +03:30:50 4.0095 +03:30:55 4.0387 +03:31:00 4.0435 +03:31:05 4.0927 +03:31:10 4.0469 +03:31:15 4.1214 +03:31:20 4.1057 +03:31:25 4.0247 +03:31:30 4.0024 +03:31:35 4.0644 +03:31:40 4.1388 +03:31:45 4.0509 +03:31:50 4.0903 +03:31:55 4.0752 +03:32:00 4.1232 +03:32:05 4.2227 +03:32:10 4.0351 +03:32:15 4.0138 +03:32:20 4.0174 +03:32:25 4.0164 +03:32:30 4.0900 +03:32:35 4.0515 +03:32:40 4.0767 +03:32:45 4.1111 +03:32:50 4.0863 +03:32:55 4.1281 +03:33:00 4.1409 +03:33:05 4.1926 +03:33:10 4.1241 +03:33:15 4.0211 +03:33:20 4.1523 +03:33:25 4.0626 +03:33:30 4.0456 +03:33:35 4.1214 +03:33:40 4.0988 +03:33:45 4.1106 +03:33:50 4.0771 +03:33:55 4.1017 +03:34:00 4.0340 +03:34:05 4.1042 +03:34:10 4.1357 +03:34:15 4.1079 +03:34:20 4.0983 +03:34:25 4.1115 +03:34:30 4.0105 +03:34:35 4.0296 +03:34:40 4.0804 +03:34:45 4.1351 +03:34:50 4.1214 +03:34:55 4.0661 +03:35:00 4.0082 +03:35:05 4.0327 +03:35:10 4.0934 +03:35:15 4.0779 +03:35:20 4.0803 +03:35:25 4.1371 +03:35:30 4.1480 +03:35:35 4.1340 +03:35:40 4.0830 +03:35:45 4.1148 +03:35:50 4.1593 +03:35:55 4.0933 +03:36:00 4.0524 +03:36:05 4.0881 +03:36:10 4.1333 +03:36:15 4.1515 +03:36:20 4.0873 +03:36:25 4.1275 +03:36:30 4.1389 +03:36:35 4.0835 +03:36:40 4.0352 +03:36:45 4.0414 +03:36:50 4.0521 +03:36:55 4.0319 +03:37:00 4.1085 +03:37:05 4.1735 +03:37:10 4.1726 +03:37:15 4.0721 +03:37:20 4.0644 +03:37:25 4.0078 +03:37:30 4.0888 +03:37:35 4.0549 +03:37:40 3.9830 +03:37:45 3.9840 +03:37:50 4.1003 +03:37:55 4.1493 +03:38:00 4.1249 +03:38:05 4.1075 +03:38:10 4.1733 +03:38:15 4.1136 +03:38:20 4.0827 +03:38:25 4.1236 +03:38:30 4.1258 +03:38:35 4.0725 +03:38:40 4.0357 +03:38:45 3.9690 +03:38:50 4.1165 +03:38:55 4.1759 +03:39:00 4.1197 +03:39:05 4.1025 +03:39:10 4.0569 +03:39:15 4.0400 +03:39:20 3.9677 +03:39:25 3.9899 +03:39:30 4.0343 +03:39:35 4.0667 +03:39:40 4.1553 +03:39:45 4.1993 +03:39:50 4.0512 +03:39:55 4.0495 +03:40:00 4.2104 +03:40:05 4.1452 +03:40:10 4.0911 +03:40:15 4.1267 +03:40:20 4.0580 +03:40:25 4.0931 +03:40:30 4.2129 +03:40:35 4.1885 +03:40:40 4.1646 +03:40:45 4.1272 +03:40:50 4.0508 +03:40:55 4.0526 +03:41:00 4.0093 +03:41:05 3.9395 +03:41:10 4.0097 +03:41:15 4.1250 +03:41:20 4.0749 +03:41:25 4.0373 +03:41:30 4.0265 +03:41:35 3.9805 +03:41:40 4.0195 +03:41:45 3.9868 +03:41:50 4.0456 +03:41:55 4.1267 +03:42:00 4.1139 +03:42:05 4.1279 +03:42:10 4.1334 +03:42:15 4.0094 +03:42:20 4.0214 +03:42:25 4.0687 +03:42:30 3.9961 +03:42:35 4.0201 +03:42:40 4.0599 +03:42:45 4.0127 +03:42:50 3.9634 +03:42:55 4.0015 +03:43:00 4.0272 +03:43:05 4.1380 +03:43:10 4.2216 +03:43:15 4.1309 +03:43:20 4.0389 +03:43:25 3.9899 +03:43:30 3.9616 +03:43:35 4.0915 +03:43:40 4.0850 +03:43:45 4.0882 +03:43:50 4.0155 +03:43:55 4.0403 +03:44:00 4.0989 +03:44:05 4.0829 +03:44:10 4.0665 +03:44:15 4.0139 +03:44:20 3.9861 +03:44:25 4.0388 +03:44:30 4.0862 +03:44:35 4.0795 +03:44:40 4.1346 +03:44:45 4.1377 +03:44:50 4.0644 +03:44:55 4.1428 +03:45:00 4.1881 +03:45:05 4.1589 +03:45:10 4.0610 +03:45:15 4.0326 +03:45:20 4.1169 +03:45:25 4.1220 +03:45:30 4.1094 +03:45:35 4.0821 +03:45:40 4.1222 +03:45:45 4.1364 +03:45:50 4.1695 +03:45:55 4.0808 +03:46:00 4.0312 +03:46:05 4.0693 +03:46:10 4.0875 +03:46:15 4.1168 +03:46:20 4.1181 +03:46:25 4.0864 +03:46:30 4.0701 +03:46:35 4.1188 +03:46:40 4.0679 +03:46:45 4.0386 +03:46:50 4.0968 +03:46:55 4.0646 +03:47:00 4.0605 +03:47:05 4.1020 +03:47:10 4.0454 +03:47:15 4.0461 +03:47:20 4.0385 +03:47:25 4.1900 +03:47:30 4.1727 +03:47:35 4.0939 +03:47:40 4.0743 +03:47:45 4.0681 +03:47:50 3.9906 +03:47:55 4.0196 +03:48:00 4.0161 +03:48:05 4.0575 +03:48:10 4.1365 +03:48:15 4.1526 +03:48:20 4.1624 +03:48:25 4.2395 +03:48:30 4.1461 +03:48:35 4.0544 +03:48:40 4.1201 +03:48:45 4.1805 +03:48:50 4.0824 +03:48:55 4.0525 +03:49:00 3.9447 +03:49:05 3.9959 +03:49:10 4.0445 +03:49:15 4.0889 +03:49:20 4.0726 +03:49:25 4.1557 +03:49:30 4.1940 +03:49:35 4.1565 +03:49:40 4.0872 +03:49:45 4.0207 +03:49:50 4.0388 +03:49:55 4.0634 +03:50:00 4.1249 +03:50:05 4.0759 +03:50:10 4.0690 +03:50:15 4.0278 +03:50:20 4.0300 +03:50:25 4.0809 +03:50:30 4.0543 +03:50:35 3.9684 +03:50:40 3.9701 +03:50:45 4.0408 +03:50:50 4.0582 +03:50:55 3.9921 +03:51:00 3.9987 +03:51:05 4.0121 +03:51:10 4.0869 +03:51:15 4.0599 +03:51:20 3.9479 +03:51:25 3.9299 +03:51:30 3.9944 +03:51:35 4.0353 +03:51:40 4.1287 +03:51:45 4.2029 +03:51:50 4.1889 +03:51:55 4.0635 +03:52:00 4.0499 +03:52:05 4.0087 +03:52:10 4.0939 +03:52:15 4.0038 +03:52:20 4.1144 +03:52:25 4.1320 +03:52:30 4.0804 +03:52:35 4.0936 +03:52:40 4.1385 +03:52:45 4.1975 +03:52:50 4.1150 +03:52:55 4.0685 +03:53:00 4.0558 +03:53:05 4.0661 +03:53:10 4.1773 +03:53:15 4.1220 +03:53:20 4.0951 +03:53:25 4.1330 +03:53:30 3.9785 +03:53:35 4.0393 +03:53:40 4.0464 +03:53:45 4.0887 +03:53:50 4.0785 +03:53:55 4.0773 +03:54:00 4.0992 +03:54:05 4.0371 +03:54:10 4.0279 +03:54:15 4.0739 +03:54:20 4.0866 +03:54:25 4.1586 +03:54:30 4.1835 +03:54:35 4.0854 +03:54:40 4.0849 +03:54:45 4.1371 +03:54:50 4.1244 +03:54:55 4.0534 +03:55:00 4.0000 +03:55:05 4.1373 +03:55:10 4.1107 +03:55:15 4.1157 +03:55:20 4.2205 +03:55:25 4.0826 +03:55:30 4.0982 +03:55:35 4.0992 +03:55:40 4.0231 +03:55:45 4.1023 +03:55:50 4.0665 +03:55:55 4.1014 +03:56:00 4.1579 +03:56:05 4.0374 +03:56:10 3.9536 +03:56:15 3.9601 +03:56:20 4.0342 +03:56:25 4.0892 +03:56:30 4.1104 +03:56:35 4.0786 +03:56:40 4.0338 +03:56:45 4.0734 +03:56:50 4.1287 +03:56:55 4.1460 +03:57:00 4.1107 +03:57:05 3.9202 +03:57:10 3.9638 +03:57:15 4.0306 +03:57:20 4.0301 +03:57:25 3.9349 +03:57:30 3.8937 +03:57:35 3.9744 +03:57:40 4.0193 +03:57:45 4.0096 +03:57:50 4.1072 +03:57:55 4.1041 +03:58:00 4.0964 +03:58:05 4.0788 +03:58:10 4.1031 +03:58:15 4.1508 +03:58:20 4.1031 +03:58:25 4.0651 +03:58:30 4.0691 +03:58:35 4.0602 +03:58:40 4.0623 +03:58:45 4.0295 +03:58:50 4.0300 +03:58:55 4.1210 +03:59:00 4.1525 +03:59:05 4.1610 +03:59:10 4.0985 +03:59:15 4.1171 +03:59:20 4.1181 +03:59:25 4.0968 +03:59:30 4.0856 +03:59:35 4.0566 +03:59:40 4.0068 +03:59:45 4.0150 +03:59:50 4.0226 +03:59:55 4.0169 +04:00:00 4.0679 +04:00:05 4.1281 +04:00:10 4.0493 +04:00:15 4.0775 +04:00:20 4.0697 +04:00:25 4.0661 +04:00:30 4.0754 +04:00:35 4.0200 +04:00:40 4.0925 +04:00:45 4.0704 +04:00:50 4.1350 +04:00:55 4.1498 +04:01:00 4.0286 +04:01:05 4.0097 +04:01:10 4.0457 +04:01:15 4.0688 +04:01:20 4.0411 +04:01:25 4.0013 +04:01:30 4.0320 +04:01:35 4.0575 +04:01:40 4.0417 +04:01:45 4.0236 +04:01:50 4.0083 +04:01:55 4.0328 +04:02:00 4.0877 +04:02:05 4.0233 +04:02:10 4.0447 +04:02:15 4.0790 +04:02:20 4.0779 +04:02:25 4.1271 +04:02:30 4.0494 +04:02:35 4.0827 +04:02:40 4.0855 +04:02:45 3.9795 +04:02:50 3.9926 +04:02:55 3.9872 +04:03:00 3.9898 +04:03:15 4.1434 +04:03:20 4.0862 +04:03:25 4.0710 +04:03:30 4.1321 +04:03:35 4.0485 +04:03:40 4.0912 +04:03:45 4.1749 +04:03:50 4.1120 +04:03:55 4.0377 +04:04:00 4.0652 +04:04:05 4.1590 +04:04:10 4.1452 +04:04:15 4.0786 +04:04:20 4.0665 +04:04:25 4.0450 +04:04:30 4.0794 +04:04:35 4.1068 +04:04:40 4.0494 +04:04:45 4.1125 +04:04:50 4.1474 +04:04:55 4.0877 +04:05:00 4.0910 +04:05:05 4.1541 +04:05:10 4.0742 +04:05:15 4.0808 +04:05:20 4.1614 +04:05:25 4.0797 +04:05:30 4.0938 +04:05:35 4.0854 +04:05:40 4.0572 +04:05:45 4.1223 +04:05:50 4.0129 +04:05:55 4.0117 +04:06:00 4.0416 +04:06:05 4.0288 +04:06:10 4.0170 +04:06:15 4.0264 +04:06:20 3.9962 +04:06:25 4.0271 +04:06:30 4.0211 +04:06:35 3.9995 +04:06:40 4.0868 +04:06:45 4.0743 +04:06:50 4.1167 +04:06:55 4.0344 +04:07:00 4.0152 +04:07:05 4.0559 +04:07:10 4.0426 +04:07:15 4.0358 +04:07:20 4.0760 +04:07:25 4.0081 +04:07:30 3.9883 +04:07:35 4.0747 +04:07:40 4.1201 +04:07:45 4.1002 +04:07:50 3.9981 +04:07:55 4.0132 +04:08:00 3.9810 +04:08:05 3.9970 +04:08:10 4.0591 +04:08:15 4.0192 +04:08:20 4.0776 +04:08:25 3.9858 +04:08:30 3.9701 +04:08:35 4.0298 +04:08:40 4.0461 +04:08:45 4.1026 +04:08:50 4.0422 +04:08:55 4.0782 +04:09:00 4.1013 +04:09:05 4.0557 +04:09:10 4.0287 +04:09:15 4.0880 +04:09:20 4.0791 +04:09:25 4.0986 +04:09:30 4.1017 +04:09:35 4.0603 +04:09:40 4.0401 +04:09:45 4.0433 +04:09:50 4.0454 +04:09:55 4.0437 +04:10:00 4.0338 +04:10:05 4.1308 +04:10:10 4.0189 +04:10:15 4.0419 +04:10:20 4.0833 +04:10:25 4.0988 +04:10:30 4.1208 +04:10:35 4.0149 +04:10:40 4.0414 +04:10:45 4.0626 +04:10:50 4.0934 +04:10:55 3.9615 +04:11:00 4.0348 +04:11:05 4.0945 +04:11:10 4.0446 +04:11:15 4.0064 +04:11:20 4.0353 +04:11:25 4.0910 +04:11:30 4.0711 +04:11:35 4.0977 +04:11:40 4.0508 +04:11:45 4.0511 +04:11:50 4.0279 +04:11:55 4.0479 +04:12:00 4.0377 +04:12:05 4.0103 +04:12:10 3.9387 +04:12:15 3.9166 +04:12:20 3.9891 +04:12:25 4.0158 +04:12:30 4.0790 +04:12:35 4.1182 +04:12:40 4.0019 +04:12:45 3.8491 +04:12:50 4.0451 +04:12:55 4.1162 +04:13:00 4.0950 +04:13:05 4.1231 +04:13:10 4.0735 +04:13:15 4.0462 +04:13:20 4.0216 +04:13:25 4.0860 +04:13:30 4.0824 +04:13:35 4.0969 +04:13:40 4.0873 +04:13:45 3.9946 +04:13:50 4.0223 +04:13:55 3.9728 +04:14:00 4.0428 +04:14:05 4.0623 +04:14:10 4.0626 +04:14:15 4.1187 +04:14:20 4.0752 +04:14:25 4.0912 +04:14:30 4.0005 +04:14:35 4.0054 +04:14:40 4.0669 +04:14:45 4.1372 +04:14:50 4.0446 +04:14:55 4.0436 +04:15:00 4.0354 +04:15:05 4.0954 +04:15:10 4.0800 +04:15:15 4.0128 +04:15:20 4.0240 +04:15:25 4.0094 +04:15:30 4.1446 +04:15:35 4.1474 +04:15:40 4.0492 +04:15:45 4.0400 +04:15:50 3.9749 +04:15:55 4.0986 +04:16:00 4.0816 +04:16:05 4.0116 +04:16:10 4.1562 +04:16:15 4.0844 +04:16:20 4.0762 +04:16:25 4.0181 +04:16:30 4.0866 +04:16:35 4.0120 +04:16:40 3.9740 +04:16:45 4.0272 +04:16:50 4.0410 +04:16:55 4.0270 +04:17:00 3.9885 +04:17:05 3.8586 +04:17:10 3.8953 +04:17:15 4.0647 +04:17:20 4.0308 +04:17:25 4.1195 +04:17:30 4.1192 +04:17:35 4.0678 +04:17:40 4.0904 +04:17:45 4.0457 +04:17:50 4.0033 +04:17:55 4.0700 +04:18:00 4.0485 +04:18:05 4.0782 +04:18:10 3.9996 +04:18:15 4.0400 +04:18:20 4.0871 +04:18:25 4.1521 +04:18:30 4.1135 +04:18:35 4.0407 +04:18:40 4.0883 +04:18:45 4.1141 +04:18:50 4.0726 +04:18:55 4.1482 +04:19:00 4.0820 +04:19:05 4.0976 +04:19:10 4.0710 +04:19:15 4.0883 +04:19:20 4.0456 +04:19:25 3.9887 +04:19:30 4.0172 +04:19:35 4.1133 +04:19:40 4.1168 +04:19:45 4.0405 +04:19:50 3.9421 +04:19:55 3.9902 +04:20:00 4.0676 +04:20:05 4.0501 +04:20:10 4.0215 +04:20:15 4.0471 +04:20:20 4.1573 +04:20:25 4.1254 +04:20:30 4.1137 +04:20:35 4.0218 +04:20:40 4.0644 +04:20:45 4.1014 +04:20:50 4.0293 +04:20:55 4.0704 +04:21:00 4.0560 +04:21:05 3.9641 +04:21:10 4.1124 +04:21:15 4.1308 +04:21:20 4.0865 +04:21:25 4.1106 +04:21:30 4.0873 +04:21:35 4.0260 +04:21:40 4.1024 +04:21:45 4.1577 +04:21:50 4.1546 +04:21:55 4.0419 +04:22:00 3.9838 +04:22:05 3.9895 +04:22:10 4.0644 +04:22:15 4.0941 +04:22:20 4.0800 +04:22:25 4.1094 +04:22:30 4.0694 +04:22:35 4.0247 +04:22:40 4.0559 +04:22:45 4.0296 +04:22:50 3.9198 +04:22:55 3.9093 +04:23:00 4.0556 +04:23:05 4.0296 +04:23:10 4.0937 +04:23:15 4.0862 +04:23:20 4.1121 +04:23:25 4.0896 +04:23:30 4.1232 +04:23:35 4.0697 +04:23:40 4.0028 +04:23:45 4.0742 +04:23:50 4.1085 +04:23:55 4.1121 +04:24:00 4.0926 +04:24:05 4.0860 +04:24:10 4.1171 +04:24:15 4.0818 +04:24:20 4.0521 +04:24:25 4.1181 +04:24:30 4.1291 +04:24:35 4.1055 +04:24:40 4.0621 +04:24:45 4.0248 +04:24:50 4.0452 +04:24:55 4.0003 +04:25:00 3.9861 +04:25:05 4.0202 +04:25:10 4.0330 +04:25:15 4.1033 +04:25:20 4.0639 +04:25:25 4.0326 +04:25:30 4.0411 +04:25:35 4.0748 +04:25:40 4.0267 +04:25:45 3.8942 +04:25:50 3.9230 +04:25:55 3.9923 +04:26:00 4.0184 +04:26:05 4.1140 +04:26:10 4.1631 +04:26:15 4.1512 +04:26:20 4.1172 +04:26:25 3.9456 +04:26:30 3.9539 +04:26:35 3.9670 +04:26:40 3.9803 +04:26:45 4.0176 +04:26:50 4.0582 +04:26:55 4.0807 +04:27:00 4.0090 +04:27:05 3.9903 +04:27:10 4.0705 +04:27:15 4.1370 +04:27:20 4.1659 +04:27:25 4.1442 +04:27:30 4.0738 +04:27:35 3.9483 +04:27:40 4.0582 +04:27:45 4.0499 +04:27:50 4.0891 +04:27:55 4.1162 +04:28:00 4.0254 +04:28:05 4.0193 +04:28:10 4.0301 +04:28:15 4.0661 +04:28:20 4.0938 +04:28:25 4.1089 +04:28:30 4.1480 +04:28:35 4.1721 +04:28:40 4.1785 +04:28:45 4.2213 +04:28:50 4.0574 +04:28:55 3.9775 +04:29:00 3.9938 +04:29:05 4.0620 +04:29:10 4.0487 +04:29:15 3.9717 +04:29:20 3.9880 +04:29:25 4.0630 +04:29:30 4.0240 +04:29:35 3.9478 +04:29:40 4.0095 +04:29:45 4.0468 +04:29:50 4.0174 +04:29:55 3.9363 +04:30:00 3.9969 +04:30:05 3.9682 +04:30:10 4.0608 +04:30:15 4.0622 +04:30:20 3.9997 +04:30:25 4.1280 +04:30:30 4.1360 +04:30:35 4.0142 +04:30:40 4.0664 +04:30:45 4.1031 +04:30:50 4.0027 +04:30:55 3.9513 +04:31:00 4.1246 +04:31:05 4.0150 +04:31:10 3.9790 +04:31:15 4.1175 +04:31:20 4.1218 +04:31:25 4.1292 +04:31:30 4.0749 +04:31:35 3.9309 +04:31:40 3.9193 +04:31:45 3.9333 +04:31:50 3.9845 +04:31:55 4.0423 +04:32:00 3.9989 +04:32:05 3.9556 +04:32:10 4.0899 +04:32:15 4.1073 +04:32:20 4.0402 +04:32:25 4.0683 +04:32:30 4.1364 +04:32:35 4.1568 +04:32:40 4.0797 +04:32:45 4.0491 +04:32:50 4.0471 +04:32:55 3.9937 +04:33:00 4.0290 +04:33:05 4.0669 +04:33:10 4.0924 +04:33:15 3.9666 +04:33:20 4.0217 +04:33:25 4.1640 +04:33:30 4.1051 +04:33:35 4.0035 +04:33:40 4.0447 +04:33:45 4.0698 +04:33:50 4.0754 +04:33:55 4.0518 +04:34:00 4.0559 +04:34:05 4.0504 +04:34:10 3.9753 +04:34:15 4.0496 +04:34:20 4.0948 +04:34:25 4.0597 +04:34:30 3.9724 +04:34:35 3.9955 +04:34:40 3.9453 +04:34:45 3.9633 +04:34:50 4.0974 +04:34:55 4.0312 +04:35:00 3.9932 +04:35:05 3.9990 +04:35:10 4.0152 +04:35:15 4.0381 +04:35:20 4.0324 +04:35:25 4.0169 +04:35:30 3.9831 +04:35:35 4.0750 +04:35:40 4.1812 +04:35:45 4.0404 +04:35:50 3.9617 +04:35:55 4.1232 +04:36:00 4.1599 +04:36:05 4.0862 +04:36:10 4.0233 +04:36:15 3.9876 +04:36:20 3.9762 +04:36:25 3.9575 +04:36:30 4.0419 +04:36:35 4.0650 +04:36:40 4.0659 +04:36:45 4.0091 +04:36:50 4.0967 +04:36:55 4.1526 +04:37:00 4.1363 +04:37:05 4.1243 +04:37:10 4.0998 +04:37:15 4.0470 +04:37:20 4.0278 +04:37:25 4.0642 +04:37:30 4.0673 +04:37:35 4.0763 +04:37:40 4.0777 +04:37:45 4.0406 +04:37:50 4.0179 +04:37:55 4.0860 +04:38:00 4.0051 +04:38:05 4.0037 +04:38:10 3.9706 +04:38:15 4.0890 +04:38:20 4.1843 +04:38:25 4.0746 +04:38:30 4.0455 +04:38:35 4.1092 +04:38:40 4.1336 +04:38:45 4.1179 +04:38:50 4.0065 +04:38:55 4.1217 +04:39:00 4.1688 +04:39:05 4.0801 +04:39:10 4.1168 +04:39:15 4.1245 +04:39:20 4.0489 +04:39:25 4.0563 +04:39:30 4.0822 +04:39:35 4.0695 +04:39:40 4.0185 +04:39:45 4.0599 +04:39:50 4.0696 +04:39:55 3.9724 +04:40:00 3.9328 +04:40:05 4.0173 +04:40:10 3.9281 +04:40:15 3.9980 +04:40:20 4.0663 +04:40:25 4.0474 +04:40:30 4.0927 +04:40:35 4.1530 +04:40:40 4.0857 +04:40:45 3.9822 +04:40:50 3.9937 +04:40:55 4.0347 +04:41:00 3.9632 +04:41:05 3.9870 +04:41:10 4.0363 +04:41:15 4.0968 +04:41:20 4.0706 +04:41:25 4.0496 +04:41:30 4.0511 +04:41:35 4.0849 +04:41:40 4.0673 +04:41:45 4.0049 +04:41:50 3.9664 +04:41:55 4.0196 +04:42:00 4.1047 +04:42:05 4.1748 +04:42:10 4.0686 +04:42:15 4.0325 +04:42:20 4.1578 +04:42:25 4.0263 +04:42:30 4.0201 +04:42:35 4.0448 +04:42:40 4.1217 +04:42:45 4.1234 +04:42:50 4.0319 +04:42:55 3.9412 +04:43:00 4.0178 +04:43:05 4.0216 +04:43:10 3.9699 +04:43:15 4.0257 +04:43:20 3.9940 +04:43:25 3.9998 +04:43:30 3.9991 +04:43:35 4.0872 +04:43:40 4.0749 +04:43:45 3.9980 +04:43:50 4.0289 +04:43:55 4.1160 +04:44:00 4.0755 +04:44:05 3.9337 +04:44:10 3.9097 +04:44:15 3.9304 +04:44:20 3.9577 +04:44:25 4.0328 +04:44:30 4.0767 +04:44:35 4.0620 +04:44:40 4.0071 +04:44:45 3.9375 +04:44:50 3.9684 +04:44:55 3.9330 +04:45:00 4.0307 +04:45:05 4.0880 +04:45:10 4.1111 +04:45:15 4.0458 +04:45:20 4.1208 +04:45:25 4.0728 +04:45:30 4.0772 +04:45:35 4.0650 +04:45:40 4.0691 +04:45:45 4.0325 +04:45:50 4.0589 +04:45:55 3.9878 +04:46:00 3.9400 +04:46:05 4.0024 +04:46:10 4.0577 +04:46:15 4.0792 +04:46:20 4.0263 +04:46:25 4.0433 +04:46:30 4.0115 +04:46:35 4.0174 +04:46:40 3.9451 +04:46:45 4.0304 +04:46:50 4.1312 +04:46:55 4.0724 +04:47:00 4.0202 +04:47:05 4.0692 +04:47:10 4.0563 +04:47:15 4.0069 +04:47:20 3.9528 +04:47:25 4.0735 +04:47:30 4.1346 +04:47:35 4.0450 +04:47:40 4.0102 +04:47:45 4.0361 +04:47:50 4.0086 +04:47:55 3.9418 +04:48:00 3.9217 +04:48:05 3.9529 +04:48:10 3.9716 +04:48:15 3.9808 +04:48:20 3.9298 +04:48:25 3.9863 +04:48:30 3.9945 +04:48:35 4.0286 +04:48:40 4.0448 +04:48:45 3.9649 +04:48:50 3.9530 +04:48:55 4.0075 +04:49:00 4.0545 +04:49:05 4.0567 +04:49:10 4.0356 +04:49:15 3.9127 +04:49:20 3.9304 +04:49:25 4.0614 +04:49:30 4.1176 +04:49:35 4.0574 +04:49:40 4.1142 +04:49:45 3.9921 +04:49:50 4.0908 +04:49:55 4.0824 +04:50:00 4.0464 +04:50:05 4.0131 +04:50:10 4.0662 +04:50:15 4.0134 +04:50:20 3.9700 +04:50:25 3.9606 +04:50:30 4.0021 +04:50:35 4.0559 +04:50:40 4.0254 +04:50:45 4.0097 +04:50:50 4.0800 +04:50:55 4.0495 +04:51:00 3.9839 +04:51:05 3.9539 +04:51:10 3.9653 +04:51:15 3.9804 +04:51:20 3.9662 +04:51:25 4.0419 +04:51:30 4.0276 +04:51:35 3.9820 +04:51:40 3.9505 +04:51:45 4.0233 +04:51:50 4.0825 +04:51:55 4.0070 +04:52:00 3.9917 +04:52:05 4.0483 +04:52:10 4.0865 +04:52:15 4.1142 +04:52:20 4.0097 +04:52:25 4.0742 +04:52:30 4.0780 +04:52:35 4.0742 +04:52:40 4.0592 +04:52:45 3.9919 +04:52:50 4.0200 +04:52:55 4.0358 +04:53:00 3.9553 +04:53:05 4.1021 +04:53:10 4.1821 +04:53:15 4.0040 +04:53:20 4.0133 +04:53:25 4.0050 +04:53:30 4.0187 +04:53:35 4.0102 +04:53:40 4.0105 +04:53:45 4.1021 +04:53:50 4.2023 +04:53:55 4.1127 +04:54:00 4.0408 +04:54:05 4.0162 +04:54:10 3.9951 +04:54:15 4.0203 +04:54:20 4.0174 +04:54:25 4.0912 +04:54:30 4.0568 +04:54:35 4.1196 +04:54:40 4.0730 +04:54:45 4.0207 +04:54:50 4.0810 +04:54:55 4.0254 +04:55:00 4.0795 +04:55:05 4.0982 +04:55:10 4.0606 +04:55:15 4.0646 +04:55:20 4.0625 +04:55:25 3.9119 +04:55:30 3.9269 +04:55:35 4.0132 +04:55:40 4.0285 +04:55:45 4.0033 +04:55:50 4.0155 +04:55:55 4.1137 +04:56:00 4.0598 +04:56:05 3.9974 +04:56:10 3.9730 +04:56:15 3.9831 +04:56:20 4.0056 +04:56:25 4.0557 +04:56:30 4.1451 +04:56:35 4.0730 +04:56:40 4.1052 +04:56:45 4.0564 +04:56:50 4.0280 +04:56:55 3.9777 +04:57:00 3.9634 +04:57:05 4.0319 +04:57:10 3.9843 +04:57:15 3.9357 +04:57:20 4.1220 +04:57:25 4.1077 +04:57:30 4.1096 +04:57:35 4.0185 +04:57:40 3.9911 +04:57:45 4.0613 +04:57:50 3.9361 +04:57:55 3.9882 +04:58:00 3.9713 +04:58:05 3.9836 +04:58:10 4.0431 +04:58:15 4.0119 +04:58:20 3.9807 +04:58:25 4.0440 +04:58:30 4.0461 +04:58:35 3.9829 +04:58:40 3.9985 +04:58:45 4.0120 +04:58:50 3.9697 +04:58:55 4.0349 +04:59:00 3.9237 +04:59:05 3.9496 +04:59:10 3.9637 +04:59:15 3.9592 +04:59:20 3.9284 +04:59:25 4.0911 +04:59:30 4.1203 +04:59:35 4.0178 +04:59:40 4.0563 +04:59:45 4.0422 +04:59:50 4.1392 +04:59:55 4.0839 +05:00:00 4.1005 +05:00:05 4.0823 +05:00:10 4.0330 +05:00:15 3.9789 +05:00:20 4.1093 +05:00:25 4.1192 +05:00:30 4.0097 +05:00:35 3.9476 +05:00:40 3.9867 +05:00:45 4.0762 +05:00:50 4.1117 +05:00:55 4.1008 +05:01:00 4.1118 +05:01:05 4.0646 +05:01:10 4.0719 +05:01:15 3.9779 +05:01:20 3.9895 +05:01:25 3.9001 +05:01:30 3.9116 +05:01:35 4.1276 +05:01:40 4.0535 +05:01:45 4.0223 +05:01:50 4.0789 +05:01:55 4.0723 +05:02:00 4.0670 +05:02:05 4.0946 +05:02:10 3.9596 +05:02:15 3.9062 +05:02:20 4.0096 +05:02:25 3.9794 +05:02:30 3.9969 +05:02:35 4.0688 +05:02:40 4.1149 +05:02:45 4.0991 +05:02:50 4.0563 +05:02:55 4.0717 +05:03:00 3.9942 +05:03:05 4.0824 +05:03:10 4.1180 +05:03:15 4.1147 +05:03:20 4.0025 +05:03:25 4.0557 +05:03:30 4.0616 +05:03:35 3.9961 +05:03:40 3.9924 +05:03:45 4.0248 +05:03:50 3.9910 +05:03:55 4.0633 +05:04:00 3.9836 +05:04:05 3.9526 +05:04:10 4.0025 +05:04:15 4.0381 +05:04:20 4.0244 +05:04:25 4.0230 +05:04:30 3.9620 +05:04:35 3.9785 +05:04:40 4.0251 +05:04:45 4.0341 +05:04:50 4.0953 +05:04:55 4.0845 +05:05:00 4.0608 +05:05:05 3.9270 +05:05:10 3.9282 +05:05:15 3.9512 +05:05:20 3.9260 +05:05:25 3.9832 +05:05:30 3.9713 +05:05:35 3.9984 +05:05:40 3.9281 +05:05:45 3.9784 +05:05:50 3.9317 +05:05:55 3.9634 +05:06:00 4.0001 +05:06:05 4.0738 +05:06:10 4.1557 +05:06:15 4.1455 +05:06:20 4.1086 +05:06:25 4.1060 +05:06:30 4.2315 +05:06:35 4.1571 +05:06:40 3.9597 +05:06:45 4.0078 +05:06:50 4.0029 +05:06:55 3.9877 +05:07:00 3.9628 +05:07:05 3.9551 +05:07:10 4.0480 +05:07:15 4.1010 +05:07:20 4.0584 +05:07:25 4.0790 +05:07:30 4.0826 +05:07:35 4.0263 +05:07:40 4.0443 +05:07:45 4.0782 +05:07:50 4.0182 +05:07:55 4.0530 +05:08:00 4.0537 +05:08:05 3.9777 +05:08:10 3.9180 +05:08:15 3.9743 +05:08:20 3.9598 +05:08:25 3.9713 +05:08:30 4.0212 +05:08:35 4.0200 +05:08:40 3.9919 +05:08:45 3.9922 +05:08:50 3.9178 +05:08:55 3.9291 +05:09:00 4.0094 +05:09:05 3.9609 +05:09:10 3.9438 +05:09:15 4.0234 +05:09:20 4.1200 +05:09:25 4.1329 +05:09:30 4.1652 +05:09:35 4.1415 +05:09:40 4.0762 +05:09:45 4.1329 +05:09:50 4.0953 +05:09:55 3.8990 +05:10:00 3.8846 +05:10:05 4.0111 +05:10:10 3.9337 +05:10:15 4.0101 +05:10:20 4.0262 +05:10:25 3.9887 +05:10:30 4.0008 +05:10:35 4.0902 +05:10:40 4.1082 +05:10:45 4.0730 +05:10:50 4.0820 +05:10:55 4.0312 +05:11:00 4.1161 +05:11:05 4.1682 +05:11:10 4.0720 +05:11:15 4.0744 +05:11:20 4.1188 +05:11:25 4.1617 +05:11:30 4.1923 +05:11:35 4.0848 +05:11:40 4.0537 +05:11:45 4.0036 +05:11:50 4.0755 +05:11:55 4.1602 +05:12:00 4.1596 +05:12:05 4.0492 +05:12:10 3.9942 +05:12:15 4.0317 +05:12:20 3.9840 +05:12:25 3.9928 +05:12:30 4.0829 +05:12:35 4.2336 +05:12:40 4.1728 +05:12:45 3.9226 +05:12:50 3.9484 +05:12:55 4.1295 +05:13:00 4.0784 +05:13:05 3.9632 +05:13:10 4.0279 +05:13:15 3.9690 +05:13:20 4.0670 +05:13:25 4.0654 +05:13:30 4.1157 +05:13:35 4.0596 +05:13:40 4.0237 +05:13:45 3.9491 +05:13:50 3.9518 +05:13:55 3.8894 +05:14:00 3.8565 +05:14:05 3.9942 +05:14:10 4.1227 +05:14:15 4.1769 +05:14:20 4.1755 +05:14:25 4.1414 +05:14:30 3.9659 +05:14:35 4.0244 +05:14:40 4.0729 +05:14:45 3.9686 +05:14:50 4.0504 +05:14:55 4.0340 +05:15:00 4.0612 +05:15:05 4.0373 +05:15:10 4.1597 +05:15:15 4.1397 +05:15:20 4.2113 +05:15:25 4.0832 +05:15:30 3.9224 +05:15:35 3.9244 +05:15:40 3.9591 +05:15:45 4.0134 +05:15:50 4.1359 +05:15:55 4.1176 +05:16:00 4.0698 +05:16:05 4.0724 +05:16:10 4.0516 +05:16:15 3.9557 +05:16:20 3.9562 +05:16:25 4.0732 +05:16:30 4.1650 +05:16:35 4.1153 +05:16:40 3.9765 +05:16:45 3.9362 +05:16:50 3.9534 +05:16:55 3.9735 +05:17:00 4.0370 +05:17:05 3.9799 +05:17:10 4.0066 +05:17:15 4.0314 +05:17:20 4.0204 +05:17:25 3.9923 +05:17:30 4.0754 +05:17:35 4.1158 +05:17:40 4.0750 +05:17:45 3.9841 +05:17:50 3.9588 +05:17:55 4.0754 +05:18:00 4.0409 +05:18:05 4.0354 +05:18:10 4.0434 +05:18:15 4.0971 +05:18:20 4.0848 +05:18:25 4.0453 +05:18:30 4.0234 +05:18:35 4.0434 +05:18:40 3.9350 +05:18:45 3.9637 +05:18:50 4.0434 +05:18:55 4.0813 +05:19:00 4.0989 +05:19:05 4.0604 +05:19:10 4.0595 +05:19:15 3.9402 +05:19:20 3.9722 +05:19:25 4.0588 +05:19:30 3.8899 +05:19:35 3.9005 +05:19:40 4.0755 +05:19:45 4.0624 +05:19:50 4.0787 +05:19:55 4.0361 +05:20:00 3.9414 +05:20:05 3.8973 +05:20:10 4.0548 +05:20:15 4.1327 +05:20:20 4.0757 +05:20:25 4.0434 +05:20:30 4.0666 +05:20:35 4.1163 +05:20:40 4.0652 +05:20:45 4.0386 +05:20:50 4.1065 +05:20:55 4.0857 +05:21:00 4.0233 +05:21:05 4.0483 +05:21:10 3.9654 +05:21:15 3.9281 +05:21:20 4.0686 +05:21:25 4.1152 +05:21:30 4.0880 +05:21:35 4.0649 +05:21:40 4.0110 +05:21:45 4.0514 +05:21:50 4.1078 +05:21:55 4.0576 +05:22:00 4.0856 +05:22:05 4.0073 +05:22:10 3.8719 +05:22:15 3.9039 +05:22:20 4.0179 +05:22:25 4.0596 +05:22:30 3.9878 +05:22:35 3.9312 +05:22:40 4.0146 +05:22:45 4.1367 +05:22:50 4.1125 +05:22:55 4.0238 +05:23:00 4.0405 +05:23:05 4.0863 +05:23:10 4.0653 +05:23:15 3.9480 +05:23:20 3.9509 +05:23:25 4.0156 +05:23:30 4.1546 +05:23:35 4.0237 +05:23:40 3.9349 +05:23:45 3.9310 +05:23:50 4.0000 +05:23:55 4.1101 +05:24:00 4.0802 +05:24:05 4.0400 +05:24:10 3.9909 +05:24:15 4.0744 +05:24:20 4.1465 +05:24:25 4.0202 +05:24:30 4.0184 +05:24:35 4.1569 +05:24:40 4.1560 +05:24:45 4.0571 +05:24:50 4.0274 +05:24:55 3.9574 +05:25:00 4.0683 +05:25:05 4.0198 +05:25:10 4.0666 +05:25:15 4.0840 +05:25:20 4.0627 +05:25:25 4.0795 +05:25:30 4.0956 +05:25:35 4.0872 +05:25:40 4.0169 +05:25:45 4.0092 +05:25:50 3.9868 +05:25:55 4.1247 +05:26:00 3.9962 +05:26:05 4.0233 +05:26:10 4.0705 +05:26:15 4.0153 +05:26:20 4.0387 +05:26:25 4.0720 +05:26:30 4.0567 +05:26:35 4.0355 +05:26:40 4.0126 +05:26:45 4.0193 +05:26:50 4.0335 +05:26:55 4.0248 +05:27:00 4.0045 +05:27:05 3.9814 +05:27:10 4.0175 +05:27:15 4.0857 +05:27:20 3.9838 +05:27:25 3.9323 +05:27:30 3.9683 +05:27:35 4.0118 +05:27:40 4.0353 +05:27:45 4.0634 +05:27:50 3.9777 +05:27:55 4.0892 +05:28:00 4.0480 +05:28:05 3.9904 +05:28:10 3.9814 +05:28:15 4.0214 +05:28:20 3.9256 +05:28:25 3.9723 +05:28:30 3.9705 +05:28:35 3.9578 +05:28:40 3.9923 +05:28:45 4.0150 +05:28:50 4.0643 +05:28:55 4.0808 +05:29:00 4.0285 +05:29:05 4.1046 +05:29:10 4.1040 +05:29:15 4.0242 +05:29:20 4.0321 +05:29:25 4.0391 +05:29:30 4.0092 +05:29:35 4.0503 +05:29:40 4.1086 +05:29:45 4.1444 +05:29:50 4.0605 +05:29:55 4.0231 +05:30:00 3.9905 +05:30:05 4.0091 +05:30:10 3.9994 +05:30:15 4.0265 +05:30:20 4.0502 +05:30:25 4.0520 +05:30:30 3.9881 +05:30:35 4.0149 +05:30:40 3.9681 +05:30:45 4.0283 +05:30:50 3.9902 +05:30:55 4.1023 +05:31:00 4.1816 +05:31:05 3.9594 +05:31:10 4.0310 +05:31:15 4.0230 +05:31:20 4.0737 +05:31:25 3.9949 +05:31:30 4.0291 +05:31:35 3.9467 +05:31:40 4.0530 +05:31:45 4.0110 +05:31:50 4.0133 +05:31:55 4.0921 +05:32:00 3.9730 +05:32:05 3.9922 +05:32:10 3.9897 +05:32:15 3.9340 +05:32:20 3.9980 +05:32:25 3.9750 +05:32:30 4.0471 +05:32:35 4.0378 +05:32:40 4.0781 +05:32:45 4.0486 +05:32:50 3.9735 +05:32:55 4.0491 +05:33:00 4.0852 +05:33:05 4.0811 +05:33:10 4.0820 +05:33:15 4.0845 +05:33:20 4.0546 +05:33:25 4.1458 +05:33:30 3.9829 +05:33:35 3.9757 +05:33:40 4.0239 +05:33:45 3.9886 +05:33:50 4.0253 +05:33:55 4.0371 +05:34:00 3.9402 +05:34:05 4.0037 +05:34:10 4.0686 +05:34:15 4.0809 +05:34:20 4.0600 +05:34:25 4.0044 +05:34:30 4.0283 +05:34:35 4.0178 +05:34:40 4.0708 +05:34:45 4.1020 +05:34:50 4.0662 +05:34:55 4.0643 +05:35:00 4.0689 +05:35:05 4.0659 +05:35:10 3.9862 +05:35:15 4.0853 +05:35:20 4.0073 +05:35:25 3.9596 +05:35:30 4.0488 +05:35:35 4.0862 +05:35:40 4.0254 +05:35:45 4.0315 +05:35:50 4.0298 +05:35:55 4.0169 +05:36:00 4.0667 +05:36:05 4.1020 +05:36:10 4.1282 +05:36:15 4.1660 +05:36:20 4.1609 +05:36:25 4.0417 +05:36:30 4.0510 +05:36:35 4.0294 +05:36:40 3.9828 +05:36:45 4.0479 +05:36:50 4.0842 +05:36:55 4.0757 +05:37:00 4.0737 +05:37:05 3.9804 +05:37:10 4.0315 +05:37:15 4.0639 +05:37:20 4.0562 +05:37:25 3.9678 +05:37:30 4.0074 +05:37:35 3.9024 +05:37:40 3.9283 +05:37:45 3.9981 +05:37:50 3.9695 +05:37:55 4.0160 +05:38:00 4.0826 +05:38:05 3.9864 +05:38:10 3.8747 +05:38:15 3.9072 +05:38:20 3.9908 +05:38:25 3.9917 +05:38:30 3.9780 +05:38:35 3.9940 +05:38:40 3.9975 +05:38:45 3.9652 +05:38:50 3.9325 +05:38:55 4.0795 +05:39:00 4.0939 +05:39:05 4.0404 +05:39:10 3.9968 +05:39:15 3.9670 +05:39:20 4.0181 +05:39:25 3.9806 +05:39:30 3.9411 +05:39:35 3.9074 +05:39:40 3.9909 +05:39:45 4.0188 +05:39:50 4.0338 +05:39:55 4.1087 +05:40:00 4.1136 +05:40:05 4.0293 +05:40:10 3.9057 +05:40:15 3.8556 +05:40:20 3.9532 +05:40:25 4.0547 +05:40:30 4.0134 +05:40:35 4.0241 +05:40:40 4.0298 +05:40:45 4.0863 +05:40:50 4.0795 +05:40:55 4.0180 +05:41:00 3.9222 +05:41:05 4.0064 +05:41:10 4.0968 +05:41:15 4.0550 +05:41:20 4.0259 +05:41:25 4.0079 +05:41:30 4.0487 +05:41:35 4.0482 +05:41:40 4.0365 +05:41:45 4.0293 +05:41:50 4.0333 +05:41:55 3.9853 +05:42:00 3.9389 +05:42:05 3.8535 +05:42:10 3.8987 +05:42:15 3.9676 +05:42:20 3.9482 +05:42:25 3.9365 +05:42:30 4.0422 +05:42:35 4.1327 +05:42:40 4.0429 +05:42:45 4.0438 +05:42:50 4.0842 +05:42:55 4.0562 +05:43:00 3.9240 +05:43:05 3.9118 +05:43:10 4.1380 +05:43:15 4.0341 +05:43:20 3.9054 +05:43:25 3.9613 +05:43:30 4.1454 +05:43:35 4.0313 +05:43:40 4.0300 +05:43:45 4.0106 +05:43:50 4.0083 +05:43:55 4.0324 +05:44:00 4.0789 +05:44:05 4.1212 +05:44:10 4.0296 +05:44:15 4.0418 +05:44:20 4.0538 +05:44:25 4.0184 +05:44:30 4.0524 +05:44:35 4.0374 +05:44:40 4.0485 +05:44:45 4.0335 +05:44:50 4.0416 +05:44:55 3.9450 +05:45:00 3.9253 +05:45:05 3.9846 +05:45:10 4.0010 +05:45:15 4.0776 +05:45:20 3.9868 +05:45:25 3.9960 +05:45:30 4.0521 +05:45:35 4.0802 +05:45:40 4.0759 +05:45:45 3.9252 +05:45:50 4.0595 +05:45:55 4.1202 +05:46:00 4.0560 +05:46:05 4.0504 +05:46:10 3.9597 +05:46:15 3.9817 +05:46:20 4.0705 +05:46:25 4.1000 +05:46:30 4.0613 +05:46:35 3.9375 +05:46:40 3.9097 +05:46:45 3.9326 +05:46:50 4.0195 +05:46:55 3.9969 +05:47:00 4.0152 +05:47:05 4.0234 +05:47:10 4.0862 +05:47:15 4.1406 +05:47:20 3.9848 +05:47:25 3.9162 +05:47:30 3.9939 +05:47:35 3.9839 +05:47:40 3.9383 +05:47:45 3.9617 +05:47:50 3.9759 +05:47:55 4.0162 +05:48:00 4.1114 +05:48:05 4.1442 +05:48:10 4.0080 +05:48:15 4.0580 +05:48:20 4.0782 +05:48:25 3.9457 +05:48:30 3.8404 +05:48:35 3.9785 +05:48:40 4.0268 +05:48:45 4.0039 +05:48:50 3.9964 +05:48:55 4.0408 +05:49:00 4.0219 +05:49:05 3.9825 +05:49:10 4.0371 +05:49:15 4.0372 +05:49:20 3.9748 +05:49:25 3.9560 +05:49:30 4.1484 +05:49:35 4.0574 +05:49:40 4.0345 +05:49:45 4.1272 +05:49:50 4.0986 +05:49:55 4.0086 +05:50:00 4.0381 +05:50:05 4.0692 +05:50:10 3.9905 +05:50:15 3.9711 +05:50:20 4.0155 +05:50:25 4.1440 +05:50:30 3.9857 +05:50:35 4.0101 +05:50:40 3.8332 +05:50:45 4.0081 +05:50:50 4.0484 +05:50:55 3.9693 +05:51:00 4.0239 +05:51:05 4.0665 +05:51:10 3.9896 +05:51:15 3.9873 +05:51:20 4.0120 +05:51:25 3.9885 +05:51:30 4.0110 +05:51:35 4.0517 +05:51:40 4.0226 +05:51:45 3.9928 +05:51:50 4.0513 +05:51:55 3.9864 +05:52:00 4.0096 +05:52:05 3.9893 +05:52:10 4.0864 +05:52:15 3.9491 +05:52:20 4.0037 +05:52:25 3.9779 +05:52:30 4.0903 +05:52:35 4.0606 +05:52:40 4.0247 +05:52:45 4.0076 +05:52:50 4.1525 +05:52:55 4.0896 +05:53:00 4.0238 +05:53:05 4.0584 +05:53:10 4.0828 +05:53:15 4.0653 +05:53:20 4.0665 +05:53:25 4.0082 +05:53:30 4.0509 +05:53:35 4.1037 +05:53:40 4.1212 +05:53:45 4.1162 +05:53:50 4.0823 +05:53:55 4.0813 +05:54:00 4.1238 +05:54:05 4.1565 +05:54:10 4.1289 +05:54:15 4.0658 +05:54:20 3.9914 +05:54:25 3.9883 +05:54:30 3.9539 +05:54:35 3.9636 +05:54:40 3.9748 +05:54:45 4.0288 +05:54:50 4.0021 +05:54:55 4.0125 +05:55:00 4.0139 +05:55:05 3.9366 +05:55:10 3.9871 +05:55:15 4.0757 +05:55:20 4.1867 +05:55:25 4.0775 +05:55:30 4.0470 +05:55:35 4.0716 +05:55:40 3.9972 +05:55:45 3.9263 +05:55:50 4.0035 +05:55:55 4.0283 +05:56:00 4.0104 +05:56:05 4.0255 +05:56:10 3.9939 +05:56:15 4.0421 +05:56:20 3.9902 +05:56:25 4.1271 +05:56:30 4.0322 +05:56:35 3.9803 +05:56:40 3.9694 +05:56:45 3.9638 +05:56:50 3.9507 +05:56:55 3.9549 +05:57:00 4.0321 +05:57:05 4.0143 +05:57:10 4.0883 +05:57:15 3.9813 +05:57:20 4.0304 +05:57:25 3.8573 +05:57:30 4.0400 +05:57:35 3.9688 +05:57:40 4.1615 +05:57:45 3.9676 +05:57:50 4.0420 +05:57:55 4.0141 +05:58:00 4.0613 +05:58:05 3.9247 +05:58:10 3.9690 +05:58:15 4.0550 +05:58:20 3.9891 +05:58:25 4.1317 +05:58:30 4.0304 +05:58:35 4.2440 +05:58:40 4.1693 +05:58:45 3.8674 +05:58:50 4.1601 +05:58:55 4.3482 +05:59:00 4.3380 +05:59:05 4.2261 +05:59:10 4.0161 +05:59:15 3.7786 +05:59:20 4.0456 +05:59:25 4.1072 +05:59:30 3.8229 +05:59:35 4.1354 +05:59:40 3.9949 +05:59:45 3.7738 +05:59:50 4.0424 +05:59:55 4.1347 +06:00:00 3.8138 +06:00:05 4.0091 +06:00:10 3.8842 +06:00:15 4.0465 +06:00:20 3.8646 +06:00:25 3.9424 +06:00:30 4.0879 +06:00:35 3.7941 +06:00:40 3.9625 +06:00:45 4.1684 +06:00:50 3.8858 +06:00:55 3.8894 +06:01:00 4.1800 +06:01:05 4.1976 +06:01:10 3.9712 +06:01:15 3.7711 +06:01:20 3.9812 +06:01:25 4.2501 +06:01:30 4.0721 +06:01:35 3.7665 +06:01:40 3.9385 +06:01:45 4.2517 +06:01:50 4.3196 +06:01:55 4.1565 +06:02:00 3.8765 +06:02:05 3.7777 +06:02:10 4.0914 +06:02:15 4.0697 +06:02:20 3.8624 +06:02:25 4.1952 +06:02:30 4.0316 +06:02:35 3.9369 +06:02:40 4.2495 +06:02:45 3.9883 +06:02:50 4.1001 +06:02:55 3.9869 +06:03:00 4.1206 +06:03:05 4.0776 +06:03:10 4.0767 +06:03:15 4.0731 +06:03:20 4.0797 +06:03:25 3.9095 +06:03:30 4.1020 +06:03:35 4.0972 +06:03:40 4.0048 +06:03:45 4.0338 +06:03:50 3.9782 +06:03:55 3.9568 +06:04:00 4.0195 +06:04:05 4.0047 +06:04:10 3.9893 +06:04:15 3.9731 +06:04:20 4.0429 +06:04:25 3.9806 +06:04:30 3.9913 +06:04:35 4.0624 +06:04:40 4.1403 +06:04:45 4.0663 +06:04:50 4.1456 +06:04:55 4.0777 +06:05:00 3.9606 +06:05:05 4.0521 +06:05:10 4.0187 +06:05:15 3.8738 +06:05:20 3.9367 +06:05:25 4.0593 +06:05:30 4.0309 +06:05:35 4.0096 +06:05:40 3.9774 +06:05:45 4.0143 +06:05:50 4.0059 +06:05:55 3.9602 +06:06:00 3.9879 +06:06:05 4.0893 +06:06:10 4.1146 +06:06:15 4.0646 +06:06:20 3.9981 +06:06:25 4.0240 +06:06:30 4.0364 +06:06:35 4.0008 +06:06:40 3.9995 +06:06:45 4.1008 +06:06:50 4.1579 +06:06:55 4.0629 +06:07:00 3.9330 +06:07:05 4.1331 +06:07:10 4.0083 +06:07:15 3.8909 +06:07:20 3.9585 +06:07:25 4.0487 +06:07:30 4.0272 +06:07:35 4.1226 +06:07:40 4.0669 +06:07:45 3.9678 +06:07:50 4.0382 +06:07:55 4.0324 +06:08:00 4.0213 +06:08:05 3.9802 +06:08:10 3.9809 +06:08:15 4.0578 +06:08:20 3.9545 +06:08:25 4.0086 +06:08:30 4.0553 +06:08:35 3.9466 +06:08:40 4.0394 +06:08:45 4.1302 +06:08:50 4.1209 +06:08:55 3.9321 +06:09:00 4.0294 +06:09:05 3.9726 +06:09:10 3.9885 +06:09:15 4.0039 +06:09:20 4.0628 +06:09:25 4.0929 +06:09:30 4.1167 +06:09:35 4.0593 +06:09:40 4.0418 +06:09:45 3.9812 +06:09:50 3.9876 +06:09:55 3.9265 +06:10:00 4.0799 +06:10:05 3.9188 +06:10:10 3.9254 +06:10:15 4.1133 +06:10:20 4.1515 +06:10:25 4.1022 +06:10:30 4.0136 +06:10:35 4.0075 +06:10:40 3.9264 +06:10:45 3.9674 +06:10:50 4.0547 +06:10:55 4.1296 +06:11:00 4.0505 +06:11:05 3.8903 +06:11:10 3.9230 +06:11:15 4.0097 +06:11:20 3.9845 +06:11:25 3.9805 +06:11:30 4.0357 +06:11:35 4.0182 +06:11:40 4.0370 +06:11:45 3.9872 +06:11:50 3.9934 +06:11:55 4.0155 +06:12:00 4.0425 +06:12:05 3.9948 +06:12:10 4.0033 +06:12:15 4.0513 +06:12:20 4.0196 +06:12:25 4.0342 +06:12:30 4.0350 +06:12:35 4.0833 +06:12:40 4.0915 +06:12:45 4.0796 +06:12:50 3.9381 +06:12:55 3.9408 +06:13:00 3.9292 +06:13:05 3.9622 +06:13:10 4.0420 +06:13:15 3.9674 +06:13:20 3.9889 +06:13:25 4.0358 +06:13:30 4.0834 +06:13:35 3.9950 +06:13:40 3.9830 +06:13:45 4.0502 +06:13:50 4.0534 +06:13:55 4.0837 +06:14:00 4.0505 +06:14:05 4.0335 +06:14:10 3.9288 +06:14:15 3.9730 +06:14:20 3.9804 +06:14:25 4.0327 +06:14:30 4.0537 +06:14:35 4.0352 +06:14:40 4.0054 +06:14:45 4.0995 +06:14:50 4.0636 +06:14:55 4.1006 +06:15:00 4.0644 +06:15:05 3.9299 +06:15:10 4.0545 +06:15:15 4.0474 +06:15:20 3.9872 +06:15:25 4.0708 +06:15:30 3.9799 +06:15:35 4.0184 +06:15:40 3.9290 +06:15:45 3.8605 +06:15:50 3.9508 +06:15:55 4.0805 +06:16:00 4.0116 +06:16:05 3.8933 +06:16:10 4.0197 +06:16:15 4.0638 +06:16:20 4.0323 +06:16:25 4.0504 +06:16:30 4.0453 +06:16:35 3.9988 +06:16:40 3.9216 +06:16:45 3.9930 +06:16:50 4.0450 +06:16:55 3.9851 +06:17:00 3.9977 +06:17:05 4.0020 +06:17:10 4.0208 +06:17:15 3.9797 +06:17:20 3.9730 +06:17:25 3.9775 +06:17:30 3.9990 +06:17:35 4.0467 +06:17:40 3.9976 +06:17:45 4.0432 +06:17:50 4.1148 +06:17:55 4.1437 +06:18:00 4.0544 +06:18:05 4.0317 +06:18:10 4.0332 +06:18:15 4.0739 +06:18:20 4.0135 +06:18:25 3.9700 +06:18:30 3.9524 +06:18:35 4.0156 +06:18:40 4.0926 +06:18:45 3.9357 +06:18:50 3.9813 +06:18:55 3.9703 +06:19:00 4.1161 +06:19:05 4.0710 +06:19:10 4.0411 +06:19:15 4.1033 +06:19:20 4.1593 +06:19:25 3.9931 +06:19:30 3.9826 +06:19:35 3.9897 +06:19:40 3.9919 +06:19:45 4.0720 +06:19:50 4.1227 +06:19:55 4.0834 +06:20:00 4.0475 +06:20:05 3.9274 +06:20:10 3.9668 +06:20:15 4.0320 +06:20:20 4.0473 +06:20:25 4.0008 +06:20:30 4.0679 +06:20:35 4.1393 +06:20:40 4.0428 +06:20:45 3.9793 +06:20:50 4.0347 +06:20:55 4.0680 +06:21:00 4.0613 +06:21:05 4.0313 +06:21:10 4.0692 +06:21:15 4.0599 +06:21:20 4.0042 +06:21:25 3.8952 +06:21:30 3.9207 +06:21:35 3.9797 +06:21:40 3.9868 +06:21:45 4.0658 +06:21:50 4.1330 +06:21:55 4.0715 +06:22:00 4.0715 +06:22:05 4.0936 +06:22:10 4.0112 +06:22:15 4.1013 +06:22:20 4.1534 +06:22:25 4.1277 +06:22:30 4.0573 +06:22:35 4.0131 +06:22:40 4.1352 +06:22:45 4.0525 +06:22:50 4.0031 +06:22:55 3.9824 +06:23:00 3.9629 +06:23:05 3.9879 +06:23:10 4.0359 +06:23:15 4.0483 +06:23:20 4.0434 +06:23:25 3.9926 +06:23:30 4.0077 +06:23:35 4.0834 +06:23:40 4.0062 +06:23:45 3.9782 +06:23:50 4.0037 +06:23:55 3.9103 +06:24:00 3.8524 +06:24:05 3.9231 +06:24:10 3.9172 +06:24:15 3.8818 +06:24:20 3.8964 +06:24:25 3.9317 +06:24:30 4.0464 +06:24:35 4.1698 +06:24:40 3.9657 +06:24:45 3.8814 +06:24:50 4.0269 +06:24:55 3.9641 +06:25:00 3.9458 +06:25:05 3.9963 +06:25:10 4.1203 +06:25:15 4.0289 +06:25:20 3.9439 +06:25:25 3.9701 +06:25:30 4.0381 +06:25:35 3.9808 +06:25:40 3.9464 +06:25:45 4.0152 +06:25:50 4.0383 +06:25:55 4.0020 +06:26:00 3.9840 +06:26:05 3.9859 +06:26:10 3.9492 +06:26:15 4.0026 +06:26:20 4.0466 +06:26:25 4.0205 +06:26:30 3.9759 +06:26:35 4.0363 +06:26:40 4.0835 +06:26:45 4.0650 +06:26:50 3.9495 +06:26:55 4.0020 +06:27:00 4.0378 +06:27:05 3.9929 +06:27:10 3.9894 +06:27:15 4.0142 +06:27:20 3.9368 +06:27:25 3.9272 +06:27:30 3.9602 +06:27:35 3.9395 +06:27:40 4.0158 +06:27:45 3.9678 +06:27:50 4.0151 +06:27:55 4.0905 +06:28:00 4.0269 +06:28:05 4.0996 +06:28:10 4.1586 +06:28:15 4.1555 +06:28:20 4.0571 +06:28:25 4.0996 +06:28:30 4.0343 +06:28:35 3.9772 +06:28:40 3.9756 +06:28:45 3.9715 +06:28:50 3.9966 +06:28:55 4.1019 +06:29:00 3.9966 +06:29:05 4.0712 +06:29:10 4.0458 +06:29:15 3.9927 +06:29:20 3.9587 +06:29:25 4.0979 +06:29:30 4.0072 +06:29:35 4.0082 +06:29:40 4.0524 +06:29:45 4.0697 +06:29:50 4.0533 +06:29:55 4.0725 +06:30:00 4.1108 +06:30:05 4.0082 +06:30:10 3.9808 +06:30:15 4.0699 +06:30:20 3.9749 +06:30:25 3.9944 +06:30:30 4.0641 +06:30:35 4.1012 +06:30:40 4.0091 +06:30:45 4.0115 +06:30:50 3.9490 +06:30:55 3.9773 +06:31:00 4.0031 +06:31:05 3.9464 +06:31:10 4.0065 +06:31:15 4.0236 +06:31:20 3.9699 +06:31:25 4.0255 +06:31:30 4.0504 +06:31:35 4.0139 +06:31:40 3.9802 +06:31:45 3.9900 +06:31:50 3.9958 +06:31:55 3.9918 +06:32:00 3.9307 +06:32:05 4.0192 +06:32:10 4.0102 +06:32:15 3.9907 +06:32:20 4.0055 +06:32:25 3.9888 +06:32:30 4.0779 +06:32:35 4.1182 +06:32:40 4.0371 +06:32:45 3.9783 +06:32:50 4.0427 +06:32:55 4.0590 +06:33:00 4.0814 +06:33:05 4.1308 +06:33:10 4.0686 +06:33:15 4.0238 +06:33:20 4.0449 +06:33:25 4.0385 +06:33:30 3.9259 +06:33:35 3.8935 +06:33:40 3.9582 +06:33:45 4.0289 +06:33:50 4.0976 +06:33:55 4.0776 +06:34:00 4.0640 +06:34:05 4.0025 +06:34:10 3.9537 +06:34:15 4.0068 +06:34:20 4.0388 +06:34:25 4.1455 +06:34:30 4.0271 +06:34:35 3.9976 +06:34:40 3.9965 +06:34:45 4.0808 +06:34:50 4.1517 +06:34:55 4.0567 +06:35:00 4.0359 +06:35:05 4.0113 +06:35:10 3.9532 +06:35:15 4.0300 +06:35:20 4.0281 +06:35:25 4.0480 +06:35:30 4.0662 +06:35:35 4.0586 +06:35:40 4.0679 +06:35:45 3.9804 +06:35:50 4.0005 +06:35:55 4.0137 +06:36:00 4.1174 +06:36:05 4.0850 +06:36:10 3.9586 +06:36:15 3.9372 +06:36:20 3.9295 +06:36:25 4.0010 +06:36:30 4.0006 +06:36:35 3.9443 +06:36:40 4.0495 +06:36:45 3.9798 +06:36:50 4.0384 +06:36:55 4.0338 +06:37:00 4.0468 +06:37:05 4.0395 +06:37:10 3.9745 +06:37:15 4.1172 +06:37:20 4.0751 +06:37:25 3.9681 +06:37:30 3.9864 +06:37:35 4.0490 +06:37:40 4.0410 +06:37:45 3.9037 +06:37:50 3.9332 +06:37:55 3.9749 +06:38:00 4.0355 +06:38:05 4.0879 +06:38:10 4.0711 +06:38:15 4.0833 +06:38:20 4.0838 +06:38:25 4.1429 +06:38:30 4.1355 +06:38:35 3.9725 +06:38:40 4.0009 +06:38:45 4.0159 +06:38:50 4.0639 +06:38:55 4.0691 +06:39:00 3.9039 +06:39:05 3.9397 +06:39:10 3.9629 +06:39:15 4.0702 +06:39:20 4.0202 +06:39:25 4.0870 +06:39:30 4.0470 +06:39:35 3.9035 +06:39:40 3.9427 +06:39:45 4.0009 +06:39:50 3.9188 +06:39:55 3.9683 +06:40:00 3.9317 +06:40:05 3.8776 +06:40:10 3.9272 +06:40:15 3.9511 +06:40:20 3.9787 +06:40:25 4.0614 +06:40:30 4.0323 +06:40:35 4.0462 +06:40:40 4.0632 +06:40:45 4.0148 +06:40:50 3.9354 +06:40:55 3.8587 +06:41:00 3.9297 +06:41:05 4.0154 +06:41:10 4.0309 +06:41:15 3.9076 +06:41:20 3.8425 +06:41:25 3.9678 +06:41:30 3.9994 +06:41:35 3.9514 +06:41:40 3.8752 +06:41:45 3.9188 +06:41:50 4.0335 +06:41:55 3.9698 +06:42:00 3.8869 +06:42:05 3.9570 +06:42:10 3.9939 +06:42:15 4.0065 +06:42:20 3.9378 +06:42:25 3.9644 +06:42:30 4.1126 +06:42:35 4.1333 +06:42:40 4.1117 +06:42:45 4.0763 +06:42:50 3.9416 +06:42:55 4.1359 +06:43:00 3.9159 +06:43:05 3.9935 +06:43:10 4.0022 +06:43:15 3.9571 +06:43:20 3.9977 +06:43:25 4.1070 +06:43:30 3.9932 +06:43:35 3.9097 +06:43:40 3.9839 +06:43:45 4.0938 +06:43:50 4.0176 +06:43:55 4.0039 +06:44:00 4.0405 +06:44:05 4.0810 +06:44:10 3.9846 +06:44:15 3.9213 +06:44:20 4.0476 +06:44:25 4.0070 +06:44:30 4.0001 +06:44:35 3.9969 +06:44:40 4.0824 +06:44:45 3.9960 +06:44:50 3.9373 +06:44:55 3.9570 +06:45:00 3.9755 +06:45:05 3.9827 +06:45:10 4.0924 +06:45:15 4.0019 +06:45:20 3.9615 +06:45:25 3.8977 +06:45:30 3.9703 +06:45:35 3.9808 +06:45:40 4.0556 +06:45:45 4.0945 +06:45:50 4.0766 +06:45:55 3.9836 +06:46:00 4.0565 +06:46:05 4.0331 +06:46:10 3.9955 +06:46:15 4.0104 +06:46:20 4.0255 +06:46:25 3.9976 +06:46:30 3.9362 +06:46:35 3.9559 +06:46:40 3.9848 +06:46:45 4.0349 +06:46:50 4.1207 +06:46:55 4.0099 +06:47:00 4.0102 +06:47:05 4.1017 +06:47:10 4.0967 +06:47:15 3.9915 +06:47:20 4.0579 +06:47:25 4.0007 +06:47:30 3.9833 +06:47:35 4.0522 +06:47:40 4.0015 +06:47:45 3.9836 +06:47:50 3.9660 +06:47:55 4.0128 +06:48:00 4.0877 +06:48:05 4.0973 +06:48:10 3.9909 +06:48:15 3.9149 +06:48:20 3.8896 +06:48:25 3.9363 +06:48:30 3.9740 +06:48:35 4.0081 +06:48:40 3.9416 +06:48:45 3.9349 +06:48:50 3.9669 +06:48:55 4.0102 +06:49:00 3.9837 +06:49:05 4.0245 +06:49:10 4.0829 +06:49:15 3.9924 +06:49:20 3.9646 +06:49:25 4.0084 +06:49:30 4.0494 +06:49:35 4.1198 +06:49:40 4.1607 +06:49:45 4.1463 +06:49:50 4.0824 +06:49:55 4.0940 +06:50:00 4.0684 +06:50:05 3.9402 +06:50:10 3.9368 +06:50:15 3.9505 +06:50:20 3.9956 +06:50:25 3.9391 +06:50:30 3.9975 +06:50:35 4.0344 +06:50:40 3.9680 +06:50:45 3.9830 +06:50:50 4.0807 +06:50:55 4.0149 +06:51:00 3.9959 +06:51:05 3.9729 +06:51:10 3.9622 +06:51:15 3.9055 +06:51:20 3.9722 +06:51:25 4.0455 +06:51:30 4.0666 +06:51:35 4.0557 +06:51:40 4.1153 +06:51:45 4.1206 +06:51:50 4.0225 +06:51:55 4.1170 +06:52:00 4.0962 +06:52:05 4.0770 +06:52:10 4.0262 +06:52:15 3.9813 +06:52:20 3.9521 +06:52:25 4.0269 +06:52:30 3.9999 +06:52:35 3.9486 +06:52:40 4.0238 +06:52:45 4.0770 +06:52:50 4.1414 +06:52:55 4.0131 +06:53:00 4.0697 +06:53:05 4.1102 +06:53:10 3.9915 +06:53:15 3.9090 +06:53:20 4.0242 +06:53:25 4.0915 +06:53:30 4.0556 +06:53:35 4.0612 +06:53:40 4.0513 +06:53:45 3.9826 +06:53:50 4.0115 +06:53:55 3.9892 +06:54:00 3.9874 +06:54:05 4.0737 +06:54:10 4.0885 +06:54:15 4.0771 +06:54:20 4.0311 +06:54:25 3.9413 +06:54:30 3.9437 +06:54:35 4.0125 +06:54:40 4.0390 +06:54:45 4.0535 +06:54:50 3.9141 +06:54:55 3.9560 +06:55:00 3.9341 +06:55:05 3.8642 +06:55:10 4.0236 +06:55:15 4.1296 +06:55:20 4.0704 +06:55:25 3.9924 +06:55:30 3.9740 +06:55:35 3.9602 +06:55:40 3.9654 +06:55:45 3.9701 +06:55:50 4.0623 +06:55:55 4.0142 +06:56:00 4.0448 +06:56:05 4.0820 +06:56:10 4.0365 +06:56:15 3.9266 +06:56:20 3.9595 +06:56:25 3.9843 +06:56:30 3.9427 +06:56:35 4.0404 +06:56:40 4.0518 +06:56:45 3.9705 +06:56:50 3.9764 +06:56:55 3.9667 +06:57:00 3.9202 +06:57:05 3.9962 +06:57:10 3.9640 +06:57:15 4.0297 +06:57:20 4.0456 +06:57:25 4.0210 +06:57:30 4.0652 +06:57:35 4.0877 +06:57:40 4.0350 +06:57:45 4.0749 +06:57:50 4.0401 +06:57:55 4.0858 +06:58:00 3.8732 +06:58:05 4.1284 +06:58:10 3.9439 +06:58:15 4.1414 +06:58:20 4.1725 +06:58:25 3.8822 +06:58:30 4.1414 +06:58:35 4.0606 +06:58:40 3.8751 +06:58:45 4.1245 +06:58:50 3.9912 +06:58:55 4.0360 +06:59:00 3.8316 +06:59:05 4.0849 +06:59:10 4.0422 +06:59:15 3.9732 +06:59:20 3.9548 +06:59:25 3.9863 +06:59:30 3.9323 +06:59:35 4.1113 +06:59:40 3.9223 +06:59:45 4.0607 +06:59:50 3.9225 +06:59:55 4.0887 +07:00:00 4.0021 +07:00:05 3.8956 +07:00:10 3.8939 +07:00:15 4.0065 +07:00:20 3.9460 +07:00:25 4.0282 +07:00:30 3.9262 +07:00:35 4.0558 +07:00:40 3.8442 +07:00:45 4.1191 +07:00:50 4.0505 +07:00:55 3.8881 +07:01:00 4.2165 +07:01:05 4.4364 +07:01:10 4.2761 +07:01:15 3.9200 +07:01:20 3.7595 +07:01:25 4.0905 +07:01:30 4.2338 +07:01:35 3.8811 +07:01:40 3.7836 +07:01:45 4.1002 +07:01:50 4.2713 +07:01:55 4.3623 +07:02:00 4.1961 +07:02:05 4.0100 +07:02:10 3.9146 +07:02:15 3.9197 +07:02:20 4.1280 +07:02:25 4.2488 +07:02:30 4.2734 +07:02:35 4.1033 +07:02:40 3.8098 +07:02:45 3.8033 +07:02:50 4.0034 +07:02:55 4.1943 +07:03:00 4.2435 +07:03:05 4.0413 +07:03:10 3.7552 +07:03:15 3.6299 +07:03:20 3.8943 +07:03:25 4.1199 +07:03:30 4.2890 +07:03:35 4.2126 +07:03:40 3.9081 +07:03:45 3.7447 +07:03:50 3.9934 +07:03:55 4.2337 +07:04:00 3.9429 +07:04:05 3.9860 +07:04:10 4.2142 +07:04:15 3.9811 +07:04:20 3.9730 +07:04:25 4.0290 +07:04:30 3.8894 +07:04:35 4.1036 +07:04:40 3.8654 +07:04:45 3.9687 +07:04:50 4.0000 +07:04:55 3.9777 +07:05:00 4.1081 +07:05:05 3.8545 +07:05:10 4.0181 +07:05:15 3.9000 +07:05:20 4.0466 +07:05:25 4.1665 +07:05:30 4.0166 +07:05:35 4.0035 +07:05:40 4.0258 +07:05:45 4.0089 +07:05:50 4.0075 +07:05:55 3.9764 +07:06:00 3.9795 +07:06:05 4.0110 +07:06:10 3.9717 +07:06:15 4.0161 +07:06:20 3.9992 +07:06:25 3.9963 +07:06:30 3.8992 +07:06:35 3.9401 +07:06:40 3.8914 +07:06:45 3.8475 +07:06:50 3.9455 +07:06:55 4.0914 +07:07:00 4.0761 +07:07:05 3.9602 +07:07:10 3.9436 +07:07:15 4.0284 +07:07:20 4.0401 +07:07:25 4.0354 +07:07:30 4.0235 +07:07:35 4.0186 +07:07:40 4.1100 +07:07:45 4.0619 +07:07:50 3.9841 +07:07:55 3.9825 +07:08:00 4.0345 +07:08:05 4.0511 +07:08:10 3.9097 +07:08:15 3.9040 +07:08:20 4.0121 +07:08:25 4.0235 +07:08:30 4.1118 +07:08:35 3.9372 +07:08:40 3.9651 +07:08:45 3.9828 +07:08:50 3.9190 +07:08:55 4.0002 +07:09:00 3.9916 +07:09:05 4.0053 +07:09:10 4.0468 +07:09:15 4.0359 +07:09:20 4.0615 +07:09:25 4.1190 +07:09:30 4.0953 +07:09:35 4.0763 +07:09:40 4.0802 +07:09:45 4.0419 +07:09:50 3.9986 +07:09:55 4.0180 +07:10:00 3.9805 +07:10:05 4.0203 +07:10:10 3.9882 +07:10:15 4.0479 +07:10:20 4.0775 +07:10:25 3.9537 +07:10:30 3.9204 +07:10:35 3.9866 +07:10:40 4.0419 +07:10:45 4.0032 +07:10:50 4.0119 +07:10:55 4.0037 +07:11:00 3.9049 +07:11:05 4.0066 +07:11:10 4.0241 +07:11:15 4.0357 +07:11:20 3.9752 +07:11:25 3.9674 +07:11:30 4.0493 +07:11:35 4.0752 +07:11:40 4.0649 +07:11:45 4.1014 +07:11:50 4.0570 +07:11:55 3.9770 +07:12:00 3.8638 +07:12:05 3.9677 +07:12:10 3.9955 +07:12:15 4.0086 +07:12:20 4.0094 +07:12:25 4.0401 +07:12:30 3.9904 +07:12:35 3.9253 +07:12:40 3.9437 +07:12:45 3.9038 +07:12:50 3.9281 +07:12:55 3.9605 +07:13:00 4.0345 +07:13:05 3.9894 +07:13:10 3.9407 +07:13:15 4.0352 +07:13:20 4.0161 +07:13:25 3.9214 +07:13:30 3.9612 +07:13:35 4.0088 +07:13:40 3.9789 +07:13:45 3.8100 +07:13:50 3.9050 +07:13:55 3.9802 +07:14:00 3.9545 +07:14:05 3.9441 +07:14:10 4.0335 +07:14:15 4.0708 +07:14:20 3.9522 +07:14:25 3.9250 +07:14:30 3.9487 +07:14:35 3.9084 +07:14:40 3.9318 +07:14:45 4.0237 +07:14:50 3.9868 +07:14:55 3.9472 +07:15:00 3.9503 +07:15:05 3.9841 +07:15:10 4.0961 +07:15:15 3.9755 +07:15:20 3.9384 +07:15:25 4.0561 +07:15:30 4.0838 +07:15:35 4.0970 +07:15:40 4.0984 +07:15:45 3.9954 +07:15:50 3.9506 +07:15:55 3.9681 +07:16:00 4.0432 +07:16:05 3.9754 +07:16:10 3.8893 +07:16:15 4.0057 +07:16:20 3.9681 +07:16:25 3.9913 +07:16:30 4.0296 +07:16:35 3.9910 +07:16:40 3.9067 +07:16:45 3.9687 +07:16:50 4.0883 +07:16:55 4.0373 +07:17:00 4.0075 +07:17:05 4.0365 +07:17:10 3.9870 +07:17:15 3.9532 +07:17:20 3.9243 +07:17:25 3.9507 +07:17:30 3.9186 +07:17:35 3.9382 +07:17:40 3.9989 +07:17:45 4.0520 +07:17:50 4.0157 +07:17:55 3.9938 +07:18:00 3.9427 +07:18:05 3.9664 +07:18:10 3.9941 +07:18:15 4.0214 +07:18:20 3.9435 +07:18:25 3.9988 +07:18:30 4.0244 +07:18:35 4.0577 +07:18:40 3.9294 +07:18:45 4.0254 +07:18:50 4.0947 +07:18:55 3.9741 +07:19:00 3.9534 +07:19:05 3.9919 +07:19:10 3.9832 +07:19:15 4.0292 +07:19:20 4.1013 +07:19:25 4.1093 +07:19:30 4.1770 +07:19:35 4.1204 +07:19:40 4.1057 +07:19:45 4.0171 +07:19:50 4.0160 +07:19:55 4.0301 +07:20:00 3.9289 +07:20:05 4.0411 +07:20:10 4.0901 +07:20:15 4.0731 +07:20:20 4.0908 +07:20:25 4.0364 +07:20:30 3.9304 +07:20:35 3.9396 +07:20:40 4.0353 +07:20:45 4.0732 +07:20:50 3.9827 +07:20:55 3.9217 +07:21:00 4.0017 +07:21:05 4.0271 +07:21:10 3.9928 +07:21:15 3.9567 +07:21:20 3.9800 +07:21:25 4.0148 +07:21:30 3.8643 +07:21:35 3.8715 +07:21:40 3.9855 +07:21:45 4.0287 +07:21:50 3.9610 +07:21:55 3.9697 +07:22:00 3.9684 +07:22:05 4.0167 +07:22:10 4.0327 +07:22:15 3.9339 +07:22:20 3.9977 +07:22:25 4.0495 +07:22:30 3.9838 +07:22:35 4.0027 +07:22:40 4.0191 +07:22:45 3.9655 +07:22:50 4.0148 +07:22:55 4.0439 +07:23:00 4.0178 +07:23:05 4.0526 +07:23:10 4.1629 +07:23:15 4.0272 +07:23:20 4.0832 +07:23:25 4.0104 +07:23:30 4.0908 +07:23:35 4.0209 +07:23:40 3.9428 +07:23:45 3.9996 +07:23:50 4.0410 +07:23:55 3.9502 +07:24:00 4.0052 +07:24:05 3.9415 +07:24:10 4.0241 +07:24:15 4.0320 +07:24:20 3.9461 +07:24:25 4.0098 +07:24:30 3.9996 +07:24:35 4.0095 +07:24:40 4.0557 +07:24:45 4.0464 +07:24:50 4.1141 +07:24:55 4.0095 +07:25:00 4.0552 +07:25:05 3.9365 +07:25:10 4.0027 +07:25:15 3.9614 +07:25:20 3.8990 +07:25:25 3.9918 +07:25:30 3.9295 +07:25:35 3.9185 +07:25:40 3.8429 +07:25:45 3.9452 +07:25:50 3.9674 +07:25:55 4.0543 +07:26:00 4.0635 +07:26:05 3.9589 +07:26:10 3.9988 +07:26:15 4.0060 +07:26:20 3.9787 +07:26:25 3.9225 +07:26:30 3.9579 +07:26:35 3.9296 +07:26:40 3.9466 +07:26:45 4.0616 +07:26:50 4.0153 +07:26:55 4.0670 +07:27:00 3.9542 +07:27:05 3.9738 +07:27:10 3.9815 +07:27:15 3.9423 +07:27:20 3.9826 +07:27:25 4.0080 +07:27:30 3.9068 +07:27:35 3.9858 +07:27:40 4.0180 +07:27:45 4.0122 +07:27:50 3.9957 +07:27:55 3.9369 +07:28:00 3.9561 +07:28:05 4.0006 +07:28:10 3.9932 +07:28:15 4.0848 +07:28:20 4.0206 +07:28:25 4.1064 +07:28:30 4.0370 +07:28:35 3.9362 +07:28:40 3.9839 +07:28:45 4.1243 +07:28:50 4.0571 +07:28:55 4.0604 +07:29:00 3.9417 +07:29:05 3.9615 +07:29:10 3.9935 +07:29:15 4.1409 +07:29:20 4.0063 +07:29:25 4.0181 +07:29:30 4.0890 +07:29:35 4.0001 +07:29:40 3.8651 +07:29:45 3.9902 +07:29:50 4.0165 +07:29:55 4.0559 +07:30:00 4.0068 +07:30:05 4.0026 +07:30:10 3.9786 +07:30:15 4.0030 +07:30:20 3.9984 +07:30:25 4.0523 +07:30:30 4.0215 +07:30:35 4.0036 +07:30:40 4.0055 +07:30:45 3.9802 +07:30:50 4.0121 +07:30:55 3.9387 +07:31:00 4.0273 +07:31:05 4.0339 +07:31:10 4.1256 +07:31:15 4.0952 +07:31:20 4.0309 +07:31:25 3.9858 +07:31:30 3.9388 +07:31:35 3.9843 +07:31:40 3.9860 +07:31:45 3.9648 +07:31:50 3.9311 +07:31:55 3.9954 +07:32:00 3.9828 +07:32:05 3.9345 +07:32:10 3.9774 +07:32:15 3.9972 +07:32:20 3.9881 +07:32:25 3.9946 +07:32:30 3.9638 +07:32:35 3.9475 +07:32:40 3.9115 +07:32:45 3.9717 +07:32:50 3.9925 +07:32:55 3.9819 +07:33:00 3.9361 +07:33:05 3.9941 +07:33:10 4.1109 +07:33:15 4.0931 +07:33:20 4.0483 +07:33:25 4.0308 +07:33:30 4.0170 +07:33:35 3.9972 +07:33:40 3.9874 +07:33:45 4.0318 +07:33:50 4.0051 +07:33:55 4.0405 +07:34:00 4.0419 +07:34:05 4.0084 +07:34:10 3.9410 +07:34:15 3.9341 +07:34:20 3.9440 +07:34:25 3.9001 +07:34:30 3.9862 +07:34:35 3.9862 +07:34:40 3.9692 +07:34:45 3.9566 +07:34:50 4.0924 +07:34:55 4.0334 +07:35:00 4.0595 +07:35:05 4.0333 +07:35:10 4.0112 +07:35:15 4.0107 +07:35:20 4.0381 +07:35:25 4.0007 +07:35:30 4.0876 +07:35:35 4.1263 +07:35:40 4.0388 +07:35:45 3.9396 +07:35:50 3.9247 +07:35:55 3.9195 +07:36:00 3.9774 +07:36:05 4.0169 +07:36:10 3.9980 +07:36:15 3.9550 +07:36:20 3.9281 +07:36:25 3.9087 +07:36:30 4.0222 +07:36:35 3.9692 +07:36:40 3.9929 +07:36:45 4.0782 +07:36:50 4.0314 +07:36:55 4.0021 +07:37:00 3.9934 +07:37:05 3.9621 +07:37:10 3.9977 +07:37:15 4.0528 +07:37:20 3.9871 +07:37:25 3.9783 +07:37:30 3.9491 +07:37:35 4.0215 +07:37:40 4.0339 +07:37:45 3.9985 +07:37:50 3.9271 +07:37:55 4.0020 +07:38:00 3.9708 +07:38:05 3.9732 +07:38:10 4.0268 +07:38:15 4.0644 +07:38:20 3.9611 +07:38:25 3.9839 +07:38:30 4.0518 +07:38:35 3.9964 +07:38:40 3.9341 +07:38:45 3.9913 +07:38:50 4.0619 +07:38:55 4.0054 +07:39:00 3.9688 +07:39:05 3.9823 +07:39:10 3.9432 +07:39:15 4.0172 +07:39:20 4.0745 +07:39:25 4.0627 +07:39:30 4.0147 +07:39:35 4.0344 +07:39:40 4.1459 +07:39:45 4.0060 +07:39:50 3.9854 +07:39:55 3.9388 +07:40:00 4.0417 +07:40:05 3.9545 +07:40:10 4.0443 +07:40:15 3.9669 +07:40:20 3.9868 +07:40:25 4.0808 +07:40:30 4.0782 +07:40:35 4.0118 +07:40:40 3.8667 +07:40:45 3.8985 +07:40:50 3.9561 +07:40:55 3.9345 +07:41:00 3.9363 +07:41:05 3.9341 +07:41:10 3.9846 +07:41:15 3.9419 +07:41:20 4.0330 +07:41:25 4.0802 +07:41:30 4.0557 +07:41:35 3.9986 +07:41:40 4.0281 +07:41:45 3.9957 +07:41:50 3.9433 +07:41:55 4.0746 +07:42:00 3.9418 +07:42:05 3.9718 +07:42:10 3.9917 +07:42:15 3.9682 +07:42:20 3.9667 +07:42:25 4.0337 +07:42:30 4.0806 +07:42:35 4.0179 +07:42:40 4.0310 +07:42:45 4.0512 +07:42:50 4.0269 +07:42:55 4.0163 +07:43:00 4.0475 +07:43:05 3.9709 +07:43:10 3.9100 +07:43:15 3.9426 +07:43:20 3.9498 +07:43:25 3.9755 +07:43:30 4.0342 +07:43:35 4.0040 +07:43:40 3.9569 +07:43:45 3.9138 +07:43:50 3.9876 +07:43:55 3.8970 +07:44:00 3.9047 +07:44:05 3.9595 +07:44:10 4.1133 +07:44:15 4.1495 +07:44:20 4.0210 +07:44:25 3.9984 +07:44:30 4.0075 +07:44:35 3.9621 +07:44:40 3.9409 +07:44:45 3.9544 +07:44:50 3.9906 +07:44:55 4.0273 +07:45:00 4.0417 +07:45:05 3.9591 +07:45:10 4.0132 +07:45:15 4.0553 +07:45:20 3.9374 +07:45:25 3.8852 +07:45:30 3.9169 +07:45:35 3.9788 +07:45:40 3.9683 +07:45:45 3.9594 +07:45:50 3.9428 +07:45:55 4.0012 +07:46:00 3.9315 +07:46:05 4.0128 +07:46:10 4.0311 +07:46:15 4.0231 +07:46:20 3.9435 +07:46:25 3.9887 +07:46:30 3.9711 +07:46:35 3.9699 +07:46:40 4.0276 +07:46:45 4.0440 +07:46:50 4.0292 +07:46:55 4.0175 +07:47:00 4.0378 +07:47:05 3.9671 +07:47:10 3.9567 +07:47:15 3.9553 +07:47:20 3.9799 +07:47:25 3.9634 +07:47:30 3.9780 +07:47:35 3.9921 +07:47:40 3.9732 +07:47:45 3.9689 +07:47:50 4.0112 +07:47:55 3.9738 +07:48:00 3.9429 +07:48:05 3.9912 +07:48:10 4.0891 +07:48:15 4.1150 +07:48:20 3.9857 +07:48:25 4.0010 +07:48:30 3.9883 +07:48:35 3.9777 +07:48:40 4.0747 +07:48:45 4.0345 +07:48:50 3.9811 +07:48:55 3.9662 +07:49:00 3.9252 +07:49:05 3.8850 +07:49:10 3.9957 +07:49:15 4.0248 +07:49:20 3.9773 +07:49:25 4.1332 +07:49:30 3.9942 +07:49:35 3.9339 +07:49:40 4.0356 +07:49:45 4.0539 +07:49:50 4.0836 +07:49:55 4.0437 +07:50:00 3.9342 +07:50:05 3.9470 +07:50:10 3.9601 +07:50:15 4.0006 +07:50:20 4.0182 +07:50:25 3.9467 +07:50:30 4.0238 +07:50:35 4.0169 +07:50:40 4.0735 +07:50:45 4.0969 +07:50:50 3.9874 +07:50:55 3.9897 +07:51:00 4.0284 +07:51:05 4.0492 +07:51:10 4.0634 +07:51:15 3.9503 +07:51:20 4.0127 +07:51:25 3.9289 +07:51:30 4.0229 +07:51:35 4.0740 +07:51:40 4.0605 +07:51:45 4.0872 +07:51:50 3.9452 +07:51:55 4.0306 +07:52:00 4.0054 +07:52:05 3.9970 +07:52:10 3.9915 +07:52:15 4.0410 +07:52:20 4.0350 +07:52:25 4.0162 +07:52:30 3.9289 +07:52:35 3.8351 +07:52:40 3.9203 +07:52:45 4.0370 +07:52:50 4.0870 +07:52:55 4.0008 +07:53:00 4.0982 +07:53:05 4.0375 +07:53:10 3.9045 +07:53:15 3.9823 +07:53:20 3.9681 +07:53:25 4.0044 +07:53:30 3.9139 +07:53:35 4.0631 +07:53:40 3.9580 +07:53:45 3.9385 +07:53:50 3.9858 +07:53:55 4.0183 +07:54:00 4.0330 +07:54:05 4.0419 +07:54:10 4.0432 +07:54:15 4.0137 +07:54:20 4.0013 +07:54:25 3.9467 +07:54:30 3.9752 +07:54:35 3.8770 +07:54:40 3.9133 +07:54:45 3.9232 +07:54:50 4.0269 +07:54:55 3.9772 +07:55:00 4.0000 +07:55:05 3.9313 +07:55:10 4.0334 +07:55:15 4.0384 +07:55:20 4.0974 +07:55:25 3.8880 +07:55:30 3.8505 +07:55:35 3.9453 +07:55:40 3.9472 +07:55:45 4.0646 +07:55:50 3.9383 +07:55:55 3.9044 +07:56:00 3.8710 +07:56:05 4.0180 +07:56:10 3.9656 +07:56:15 3.9579 +07:56:20 3.8838 +07:56:25 3.9843 +07:56:30 3.9417 +07:56:35 4.0287 +07:56:40 4.1304 +07:56:45 4.1495 +07:56:50 4.0465 +07:56:55 4.0552 +07:57:00 4.0157 +07:57:05 3.9889 +07:57:10 4.0326 +07:57:15 4.0182 +07:57:20 3.9516 +07:57:25 3.9318 +07:57:30 3.9783 +07:57:35 3.9596 +07:57:40 4.0230 +07:57:45 3.9788 +07:57:50 3.9844 +07:57:55 3.9553 +07:58:00 3.9792 +07:58:05 4.0551 +07:58:10 4.0531 +07:58:15 4.0933 +07:58:20 4.0642 +07:58:25 4.1144 +07:58:30 3.9737 +07:58:35 4.0294 +07:58:40 4.1012 +07:58:45 4.0561 +07:58:50 3.9797 +07:58:55 4.0391 +07:59:00 4.0830 +07:59:05 3.9325 +07:59:10 4.0061 +07:59:15 4.0500 +07:59:20 4.0985 +07:59:25 4.0463 +07:59:30 3.9722 +07:59:35 3.9789 +07:59:40 4.0397 +07:59:45 4.0641 +07:59:50 4.0586 +07:59:55 4.0023 +08:00:00 3.8813 +08:00:05 4.0437 +08:00:10 4.0438 +08:00:15 4.0893 +08:00:20 4.1045 +08:00:25 3.9860 +08:00:30 3.9458 +08:00:35 4.0401 +08:00:40 4.0191 +08:00:45 3.9372 +08:00:50 3.9189 +08:00:55 3.8979 +08:01:00 4.0677 +08:01:05 4.0129 +08:01:10 3.9805 +08:01:15 4.0664 +08:01:20 4.0720 +08:01:25 4.0299 +08:01:30 4.0319 +08:01:35 4.0259 +08:01:40 4.0582 +08:01:45 3.9801 +08:01:50 3.9505 +08:01:55 4.0454 +08:02:00 4.0843 +08:02:05 3.9298 +08:02:10 3.8879 +08:02:15 4.0280 +08:02:20 4.0118 +08:02:25 4.0533 +08:02:30 3.9817 +08:02:35 3.9722 +08:02:40 3.9455 +08:02:45 3.9026 +08:02:50 3.8904 +08:02:55 3.9078 +08:03:00 3.9973 +08:03:05 4.0690 +08:03:10 4.0057 +08:03:15 4.0057 +08:03:20 3.9278 +08:03:25 3.9287 +08:03:30 3.9052 +08:03:35 3.9262 +08:03:40 3.9814 +08:03:45 3.9305 +08:03:50 3.9532 +08:03:55 3.9337 +08:04:00 3.8757 +08:04:05 3.9445 +08:04:10 4.0168 +08:04:15 3.9895 +08:04:20 3.9064 +08:04:25 3.8872 +08:04:30 3.9197 +08:04:35 3.9890 +08:04:40 3.9414 +08:04:45 3.9903 +08:04:50 3.9909 +08:04:55 3.9885 +08:05:00 3.9885 +08:05:05 3.9878 +08:05:10 3.9903 +08:05:15 3.9920 +08:05:20 3.9892 +08:05:25 3.9900 +08:05:30 3.9900 +08:05:35 3.9860 +08:05:40 3.9877 +08:05:45 3.9956 +08:05:50 3.9975 +08:05:55 3.9936 +08:06:00 3.9936 +08:06:05 3.9916 +08:06:10 3.9911 +08:06:15 3.9936 +08:06:20 3.9901 +08:06:25 3.9901 +08:06:30 3.9934 +08:06:35 4.0010 +08:06:40 3.9971 +08:06:45 3.9971 +08:06:50 3.9976 +08:06:55 3.9976 +08:07:00 3.9994 +08:07:05 4.0004 +08:07:10 4.0061 +08:07:15 4.0107 +08:07:20 4.0111 +08:07:25 4.0111 +08:07:30 4.0168 +08:07:35 4.0111 +08:07:40 4.0101 +08:07:45 4.0144 +08:07:50 4.0209 +08:07:55 4.0209 +08:08:00 4.0272 +08:08:05 4.0423 +08:08:10 4.0500 +08:08:15 4.0545 +08:08:20 4.0647 +08:08:25 4.0647 +08:08:30 4.0650 +08:08:35 4.0661 +08:08:40 4.0722 +08:08:45 4.0664 +08:08:50 4.0706 +08:08:55 4.0688 +08:09:00 4.0688 +08:09:05 4.0661 +08:09:10 4.0713 +08:09:15 4.0666 +08:09:20 3.9059 +08:09:25 3.9409 +08:09:30 3.9834 +08:09:35 3.9559 +08:09:40 3.8940 +08:09:45 3.8839 +08:09:50 3.8736 +08:09:55 3.9604 +08:10:00 4.0262 +08:10:05 4.0232 +08:10:10 3.9741 +08:10:15 3.9944 +08:10:20 3.9905 +08:10:25 3.8958 +08:10:30 3.8878 +08:10:35 4.0318 +08:10:40 3.9985 +08:10:45 3.9562 +08:10:50 4.0179 +08:10:55 3.9810 +08:11:00 3.9610 +08:11:05 4.0197 +08:11:10 4.0233 +08:11:15 4.0069 +08:11:20 4.0248 +08:11:25 3.9454 +08:11:30 4.0151 +08:11:35 4.0178 +08:11:40 4.0001 +08:11:45 4.0719 +08:11:50 4.0412 +08:11:55 4.0093 +08:12:00 3.9623 +08:12:05 3.9329 +08:12:10 3.9418 +08:12:15 3.9776 +08:12:20 3.9534 +08:12:25 3.9433 +08:12:30 4.0519 +08:12:35 4.0630 +08:12:40 4.0486 +08:12:45 4.0100 +08:12:50 3.9226 +08:12:55 3.9947 +08:13:00 3.9913 +08:13:05 3.9335 +08:13:10 3.9834 +08:13:15 4.0252 +08:13:20 4.0229 +08:13:25 4.0330 +08:13:30 3.9917 +08:13:35 3.9063 +08:13:40 3.9863 +08:13:45 3.9766 +08:13:50 4.0003 +08:13:55 4.0513 +08:14:00 4.0584 +08:14:05 3.9683 +08:14:10 4.0158 +08:14:15 4.0569 +08:14:20 3.8917 +08:14:25 3.9474 +08:14:30 4.0335 +08:14:35 3.9504 +08:14:40 3.9493 +08:14:45 3.9673 +08:14:50 3.9485 +08:14:55 3.9409 +08:15:00 4.0484 +08:15:05 3.9996 +08:15:10 3.9073 +08:15:15 3.9972 +08:15:20 4.0568 +08:15:25 3.9789 +08:15:30 4.0165 +08:15:35 4.0831 +08:15:40 4.0674 +08:15:45 4.0077 +08:15:50 4.0914 +08:15:55 4.0109 +08:16:00 3.9813 +08:16:05 3.9983 +08:16:10 4.0353 +08:16:15 3.9615 +08:16:20 3.9998 +08:16:25 4.0267 +08:16:30 4.0030 +08:16:35 4.0033 +08:16:40 3.9525 +08:16:45 4.0112 +08:16:50 4.0112 +08:16:55 4.0565 +08:17:00 4.0473 +08:17:05 4.0309 +08:17:10 3.9956 +08:17:15 3.9218 +08:17:20 3.9845 +08:17:25 4.0239 +08:17:30 4.0454 +08:17:35 4.0000 +08:17:40 4.0090 +08:17:45 3.9324 +08:17:50 3.8911 +08:17:55 3.9708 +08:18:00 3.9704 +08:18:05 3.9098 +08:18:10 4.0112 +08:18:15 4.0170 +08:18:20 3.9810 +08:18:25 3.9344 +08:18:30 4.0801 +08:18:35 3.9548 +08:18:40 3.9841 +08:18:45 4.0611 +08:18:50 3.9899 +08:18:55 4.0117 +08:19:00 4.1174 +08:19:05 4.0336 +08:19:10 3.9787 +08:19:15 4.0188 +08:19:20 3.9898 +08:19:25 3.8395 +08:19:30 3.9379 +08:19:35 3.9463 +08:19:40 3.9283 +08:19:45 3.9650 +08:19:50 3.9734 +08:19:55 4.0571 +08:20:00 3.9920 +08:20:05 4.0088 +08:20:10 4.0406 +08:20:15 4.0886 +08:20:20 3.9662 +08:20:25 4.0271 +08:20:30 4.0010 +08:20:35 4.0220 +08:20:40 3.9988 +08:20:45 4.0391 +08:20:50 4.0271 +08:20:55 3.9926 +08:21:00 3.9645 +08:21:05 3.9987 +08:21:10 4.0486 +08:21:15 3.9711 +08:21:20 4.0100 +08:21:25 3.9903 +08:21:30 4.0626 +08:21:35 3.9436 +08:21:40 4.0508 +08:21:45 4.0194 +08:21:50 3.9379 +08:21:55 3.9143 +08:22:00 3.9572 +08:22:05 3.8625 +08:22:10 4.0040 +08:22:15 3.9890 +08:22:20 4.0783 +08:22:25 4.0251 +08:22:30 4.1519 +08:22:35 4.0467 +08:22:40 4.0457 +08:22:45 4.0522 +08:22:50 3.9937 +08:22:55 3.9889 +08:23:00 3.9866 +08:23:05 4.1136 +08:23:10 4.0970 +08:23:15 3.9813 +08:23:20 3.9880 +08:23:25 4.0045 +08:23:30 3.8291 +08:23:35 3.9054 +08:23:40 3.9824 +08:23:45 4.0406 +08:23:50 4.0859 +08:23:55 4.1527 +08:24:00 4.0190 +08:24:05 3.9886 +08:24:10 3.9411 +08:24:15 3.9661 +08:24:20 4.0354 +08:24:25 3.9175 +08:24:30 3.9984 +08:24:35 4.0452 +08:24:40 4.0369 +08:24:45 4.0266 +08:24:50 3.9061 +08:24:55 3.9084 +08:25:00 3.9583 +08:25:05 3.9560 +08:25:10 3.9405 +08:25:15 3.9877 +08:25:20 4.0423 +08:25:25 3.9790 +08:25:30 4.0421 +08:25:35 3.9568 +08:25:40 3.9529 +08:25:45 3.9750 +08:25:50 3.9340 +08:25:55 4.0732 +08:26:00 4.0032 +08:26:05 3.9355 +08:26:10 4.0352 +08:26:15 4.0157 +08:26:20 3.9340 +08:26:25 4.0755 +08:26:30 3.9820 +08:26:35 3.9605 +08:26:40 3.9558 +08:26:45 3.9521 +08:26:50 4.0223 +08:26:55 3.9900 +08:27:00 3.9845 +08:27:05 4.0780 +08:27:10 3.9558 +08:27:15 3.9726 +08:27:20 4.0192 +08:27:25 3.9686 +08:27:30 3.9911 +08:27:35 3.9766 +08:27:40 4.0285 +08:27:45 4.0070 +08:27:50 4.0941 +08:27:55 4.1368 +08:28:00 4.0655 +08:28:05 4.0341 +08:28:10 4.0018 +08:28:15 3.9572 +08:28:20 3.9705 +08:28:25 4.0494 +08:28:30 4.1193 +08:28:35 4.0454 +08:28:40 4.0497 +08:28:45 3.9941 +08:28:50 3.9807 +08:28:55 3.9538 +08:29:00 4.0346 +08:29:05 3.9442 +08:29:10 3.8731 +08:29:15 3.9171 +08:29:20 3.9704 +08:29:25 3.9611 +08:29:30 3.9287 +08:29:35 3.9558 +08:29:40 3.9734 +08:29:45 3.9336 +08:29:50 3.9309 +08:29:55 3.9706 +08:30:00 3.9000 +08:30:05 3.9439 +08:30:10 3.9667 +08:30:15 4.0107 +08:30:20 4.0336 +08:30:25 3.9975 +08:30:30 4.0185 +08:30:35 4.0235 +08:30:40 4.0291 +08:30:45 4.0685 +08:30:50 4.0343 +08:30:55 4.0733 +08:31:00 4.0495 +08:31:05 3.9309 +08:31:10 3.8886 +08:31:15 3.8842 +08:31:20 3.8765 +08:31:25 3.9649 +08:31:30 4.0185 +08:31:35 3.9216 +08:31:40 3.9444 +08:31:45 4.0470 +08:31:50 4.0551 +08:31:55 3.9941 +08:32:00 3.9735 +08:32:05 4.0055 +08:32:10 4.0072 +08:32:15 3.9674 +08:32:20 3.9799 +08:32:25 3.9734 +08:32:30 4.0448 +08:32:35 3.9855 +08:32:40 3.9453 +08:32:45 3.9345 +08:32:50 3.9903 +08:32:55 3.9872 +08:33:00 3.8902 +08:33:05 3.9411 +08:33:10 3.9458 +08:33:15 3.9231 +08:33:20 3.9494 +08:33:25 3.9503 +08:33:30 3.8982 +08:33:35 4.0473 +08:33:40 3.9778 +08:33:45 3.9569 +08:33:50 3.9582 +08:33:55 3.9874 +08:34:00 3.9983 +08:34:05 4.0212 +08:34:10 3.9492 +08:34:15 3.9464 +08:34:20 3.9542 +08:34:25 3.9336 +08:34:30 3.9511 +08:34:35 3.8728 +08:34:40 3.8914 +08:34:45 4.0097 +08:34:50 4.0167 +08:34:55 3.9419 +08:35:00 3.9504 +08:35:05 3.9485 +08:35:10 4.0029 +08:35:15 4.0663 +08:35:20 3.9734 +08:35:25 3.9258 +08:35:30 3.9680 +08:35:35 3.9818 +08:35:40 3.9747 +08:35:45 3.9243 +08:35:50 3.9350 +08:35:55 4.0503 +08:36:00 4.0678 +08:36:05 4.0310 +08:36:10 3.9243 +08:36:15 3.9879 +08:36:20 4.0485 +08:36:25 4.0174 +08:36:30 3.8832 +08:36:35 3.8986 +08:36:40 3.9783 +08:36:45 3.8902 +08:36:50 3.8330 +08:36:55 3.9425 +08:37:00 4.0228 +08:37:05 3.9824 +08:37:10 4.0141 +08:37:15 4.0773 +08:37:20 4.0423 +08:37:25 3.9658 +08:37:30 3.9688 +08:37:35 4.0131 +08:37:40 3.9095 +08:37:45 3.9185 +08:37:50 3.9448 +08:37:55 3.9790 +08:38:00 3.9711 +08:38:05 4.0244 +08:38:10 3.9948 +08:38:15 3.9369 +08:38:20 3.9243 +08:38:25 3.9489 +08:38:30 4.0292 +08:38:35 3.9508 +08:38:40 3.9296 +08:38:45 3.9449 +08:38:50 4.0867 +08:38:55 4.0995 +08:39:00 4.0276 +08:39:05 3.9588 +08:39:10 3.9257 +08:39:15 3.8554 +08:39:20 3.9168 +08:39:25 3.9684 +08:39:30 4.1025 +08:39:35 4.0942 +08:39:40 4.1029 +08:39:45 4.0091 +08:39:50 3.9430 +08:39:55 3.9655 +08:40:00 3.9637 +08:40:05 3.9163 +08:40:10 3.8848 +08:40:15 3.9872 +08:40:20 3.9584 +08:40:25 3.8781 +08:40:30 3.9980 +08:40:35 3.9627 +08:40:40 3.9602 +08:40:45 3.9543 +08:40:50 3.9331 +08:40:55 3.9159 +08:41:00 3.9642 +08:41:05 3.9644 +08:41:10 3.9690 +08:41:15 3.9787 +08:41:20 4.0381 +08:41:25 4.0643 +08:41:30 3.9557 +08:41:35 3.9533 +08:41:40 3.9828 +08:41:45 3.9858 +08:41:50 4.0183 +08:41:55 3.9128 +08:42:00 3.8540 +08:42:05 3.9747 +08:42:10 4.0202 +08:42:15 4.0068 +08:42:20 4.0302 +08:42:25 3.9536 +08:42:30 3.9500 +08:42:35 3.9892 +08:42:40 3.8927 +08:42:45 3.9421 +08:42:50 4.0618 +08:42:55 4.1163 +08:43:00 4.0682 +08:43:05 4.1158 +08:43:10 4.0106 +08:43:15 3.9641 +08:43:20 4.0354 +08:43:25 3.9817 +08:43:30 3.9486 +08:43:35 4.0596 +08:43:40 3.9953 +08:43:45 3.9774 +08:43:50 4.0091 +08:43:55 4.0125 +08:44:00 4.0138 +08:44:05 3.9728 +08:44:10 3.9039 +08:44:15 3.9659 +08:44:20 4.0629 +08:44:25 3.9732 +08:44:30 3.9012 +08:44:35 3.9800 +08:44:40 4.0467 +08:44:45 3.9895 +08:44:50 3.9373 +08:44:55 3.9511 +08:45:00 4.0204 +08:45:05 3.9427 +08:45:10 4.0281 +08:45:15 4.0489 +08:45:20 3.9987 +08:45:25 3.9167 +08:45:30 3.9390 +08:45:35 3.9641 +08:45:40 3.8772 +08:45:45 3.9964 +08:45:50 4.0169 +08:45:55 3.9931 +08:46:00 4.0114 +08:46:05 4.0286 +08:46:10 4.0444 +08:46:15 3.9846 +08:46:20 3.9911 +08:46:25 4.0030 +08:46:30 4.0082 +08:46:35 4.0295 +08:46:40 3.9617 +08:46:45 3.8579 +08:46:50 4.0388 +08:46:55 4.0432 +08:47:00 4.0729 +08:47:05 3.9685 +08:47:10 3.9426 +08:47:15 3.9606 +08:47:20 4.0833 +08:47:25 4.1376 +08:47:30 4.0061 +08:47:35 4.0050 +08:47:40 3.9474 +08:47:45 3.9360 +08:47:50 4.0148 +08:47:55 3.9876 +08:48:00 3.9914 +08:48:05 3.9491 +08:48:10 4.0126 +08:48:15 4.0167 +08:48:20 3.9897 +08:48:25 3.9792 +08:48:30 3.9671 +08:48:35 3.8923 +08:48:40 3.8938 +08:48:45 3.9834 +08:48:50 3.9586 +08:48:55 3.9602 +08:49:00 4.0370 +08:49:05 4.0033 +08:49:10 4.0371 +08:49:15 4.0473 +08:49:20 3.9490 +08:49:25 4.0169 +08:49:30 4.0128 +08:49:35 4.0164 +08:49:40 3.9989 +08:49:45 4.0173 +08:49:50 4.0526 +08:49:55 4.0508 +08:50:00 4.0035 +08:50:05 4.0408 +08:50:10 4.0266 +08:50:15 3.9400 +08:50:20 3.9809 +08:50:25 3.9025 +08:50:31 3.9583 +08:50:35 3.9959 +08:50:40 3.9316 +08:50:45 4.0148 +08:50:50 4.1073 +08:50:55 4.1205 +08:51:00 4.0011 +08:51:05 3.9443 +08:51:10 3.9353 +08:51:15 3.9095 +08:51:20 3.9325 +08:51:25 3.9605 +08:51:30 3.9631 +08:51:35 4.0037 +08:51:40 3.9625 +08:51:45 4.0819 +08:51:50 3.9921 +08:51:55 3.9810 +08:52:00 3.9810 +08:52:05 3.9726 +08:52:10 3.9964 +08:52:15 3.9987 +08:52:20 3.9812 +08:52:25 3.9676 +08:52:30 3.9660 +08:52:35 3.9372 +08:52:40 4.0171 +08:52:45 3.9645 +08:52:50 3.9657 +08:52:55 3.9790 +08:53:00 3.9521 +08:53:05 3.9928 +08:53:10 4.0833 +08:53:15 4.0450 +08:53:20 3.9958 +08:53:25 3.9552 +08:53:30 3.8727 +08:53:35 3.9622 +08:53:40 3.8636 +08:53:45 3.9463 +08:53:50 3.9404 +08:53:55 3.9863 +08:54:00 3.9678 +08:54:05 4.0179 +08:54:10 4.0234 +08:54:15 3.9915 +08:54:20 3.9294 +08:54:25 3.9806 +08:54:30 4.0041 +08:54:35 4.0001 +08:54:40 3.9512 +08:54:45 3.9294 +08:54:50 3.9819 +08:54:55 4.0536 +08:55:00 4.0273 +08:55:05 4.0399 +08:55:10 4.0048 +08:55:15 4.0371 +08:55:20 4.0576 +08:55:25 4.0198 +08:55:30 3.8868 +08:55:35 3.8492 +08:55:40 3.9570 +08:55:45 4.0255 +08:55:50 3.9984 +08:55:55 3.9594 +08:56:00 3.9647 +08:56:05 4.0360 +08:56:10 3.9813 +08:56:15 3.9297 +08:56:20 3.9753 +08:56:25 4.0072 +08:56:30 4.0432 +08:56:35 4.0500 +08:56:40 3.9434 +08:56:45 3.9787 +08:56:50 3.9634 +08:56:55 3.9082 +08:57:00 3.7568 +08:57:05 3.7184 +08:57:10 3.8546 +08:57:15 3.9252 +08:57:20 3.9070 +08:57:25 3.9095 +08:57:30 3.9184 +08:57:35 3.9696 +08:57:40 4.0113 +08:57:45 3.9432 +08:57:50 3.9121 +08:57:55 4.0103 +08:58:00 3.9635 +08:58:05 3.9885 +08:58:10 3.9599 +08:58:15 3.9407 +08:58:20 3.9283 +08:58:25 3.9582 +08:58:30 4.0212 +08:58:35 4.0127 +08:58:40 4.0150 +08:58:45 4.0532 +08:58:50 4.0701 +08:58:55 3.9462 +08:59:00 3.9007 +08:59:05 3.9557 +08:59:10 3.9085 +08:59:15 3.9706 +08:59:20 4.0174 +08:59:25 4.0459 +08:59:30 3.9561 +08:59:35 4.0658 +08:59:40 4.1289 +08:59:45 3.9320 +08:59:50 3.8703 +08:59:55 3.9320 +09:00:00 3.9026 +09:00:05 3.9094 +09:00:10 3.9181 +09:00:15 3.9281 +09:00:20 4.0082 +09:00:25 3.9618 +09:00:30 4.1944 +09:00:35 3.8815 +09:00:40 4.0046 +09:00:45 3.9718 +09:00:50 3.8848 +09:00:55 4.0679 +09:01:00 3.8829 +09:01:05 4.0181 +09:01:10 3.8955 +09:01:15 4.1476 +09:01:20 4.0425 +09:01:25 3.8633 +09:01:30 4.2034 +09:01:35 3.9111 +09:01:40 3.9196 +09:01:45 4.1620 +09:01:50 3.9057 +09:01:55 4.0862 +09:02:00 3.8982 +09:02:05 3.9585 +09:02:10 4.0054 +09:02:15 4.0036 +09:02:20 3.9482 +09:02:25 3.9609 +09:02:30 4.0482 +09:02:35 3.9767 +09:02:40 4.0038 +09:02:45 3.9352 +09:02:50 3.9016 +09:02:55 3.9504 +09:03:00 3.9335 +09:03:05 3.9099 +09:03:10 3.9586 +09:03:15 3.9906 +09:03:20 3.9705 +09:03:25 3.9033 +09:03:30 3.9194 +09:03:35 3.9398 +09:03:40 4.0247 +09:03:45 3.9545 +09:03:50 3.9992 +09:03:55 3.9596 +09:04:00 3.9864 +09:04:05 3.9992 +09:04:10 4.0276 +09:04:15 4.0089 +09:04:20 3.9199 +09:04:25 3.9587 +09:04:30 4.0258 +09:04:35 3.9671 +09:04:40 3.9195 +09:04:45 3.9699 +09:04:50 4.0437 +09:04:55 4.1013 +09:05:00 4.0913 +09:05:05 3.9621 +09:05:10 3.9603 +09:05:15 4.0663 +09:05:20 4.1009 +09:05:25 4.0497 +09:05:30 4.0423 +09:05:35 4.0485 +09:05:40 4.0168 +09:05:45 3.9509 +09:05:50 3.9718 +09:05:55 3.9458 +09:06:00 3.9538 +09:06:05 3.9652 +09:06:10 4.0019 +09:06:15 3.9861 +09:06:20 4.0160 +09:06:25 4.0185 +09:06:30 3.9952 +09:06:35 4.0694 +09:06:40 3.9237 +09:06:45 3.9054 +09:06:50 3.9866 +09:06:55 3.9762 +09:07:00 3.8969 +09:07:05 3.9303 +09:07:10 3.9698 +09:07:15 3.9757 +09:07:20 3.9898 +09:07:25 3.9540 +09:07:30 3.9883 +09:07:35 4.0885 +09:07:40 4.0357 +09:07:45 4.0111 +09:07:50 3.9162 +09:07:55 3.9756 +09:08:00 4.0568 +09:08:05 4.0227 +09:08:10 4.0309 +09:08:15 3.9628 +09:08:20 3.9529 +09:08:25 3.9398 +09:08:30 3.8903 +09:08:35 3.8768 +09:08:40 3.9322 +09:08:45 3.9810 +09:08:50 3.9501 +09:08:55 3.9341 +09:09:00 4.0024 +09:09:05 4.0412 +09:09:10 4.0311 +09:09:15 3.9860 +09:09:20 3.9736 +09:09:25 3.9748 +09:09:30 4.0204 +09:09:35 3.9503 +09:09:40 3.9509 +09:09:45 3.9139 +09:09:50 3.9201 +09:09:55 3.9490 +09:10:00 4.0020 +09:10:05 3.9734 +09:10:10 3.9946 +09:10:15 4.0287 +09:10:20 4.0056 +09:10:25 3.9963 +09:10:30 3.9700 +09:10:35 3.9943 +09:10:40 3.8822 +09:10:45 3.9416 +09:10:50 4.0093 +09:10:55 4.0461 +09:11:00 4.0392 +09:11:05 3.9744 +09:11:10 3.9849 +09:11:15 3.9157 +09:11:20 4.0314 +09:11:25 4.0579 +09:11:30 4.0791 +09:11:35 4.0032 +09:11:40 3.9909 +09:11:45 4.0130 +09:11:50 3.9939 +09:11:55 3.9481 +09:12:00 3.9951 +09:12:05 4.0153 +09:12:10 3.9742 +09:12:15 3.9734 +09:12:20 3.8981 +09:12:25 3.8917 +09:12:30 4.0098 +09:12:35 3.9335 +09:12:40 3.9522 +09:12:45 3.9576 +09:12:50 3.9824 +09:12:55 3.8814 +09:13:00 3.8504 +09:13:05 3.9867 +09:13:10 3.9064 +09:13:15 3.8306 +09:13:20 3.8611 +09:13:25 3.9154 +09:13:30 3.8721 +09:13:35 3.8510 +09:13:40 3.9872 +09:13:45 4.0319 +09:13:50 3.8819 +09:13:55 4.0158 +09:14:00 3.9158 +09:14:05 3.9536 +09:14:10 4.0790 +09:14:15 4.0152 +09:14:20 4.0560 +09:14:25 3.9705 +09:14:30 4.0517 +09:14:35 4.0736 +09:14:40 4.0872 +09:14:45 4.1996 +09:14:50 4.0228 +09:14:55 4.0313 +09:15:00 4.0599 +09:15:05 4.0157 +09:15:10 3.9526 +09:15:15 3.9166 +09:15:20 3.8356 +09:15:25 3.8660 +09:15:30 3.8097 +09:15:35 3.9104 +09:15:40 3.9859 +09:15:45 3.9674 +09:15:50 3.9716 +09:15:55 4.1186 +09:16:00 4.0244 +09:16:05 3.9607 +09:16:10 3.8917 +09:16:15 3.9409 +09:16:20 3.9663 +09:16:25 3.9814 +09:16:30 3.9684 +09:16:35 3.9023 +09:16:40 4.0013 +09:16:45 3.9684 +09:16:50 3.9354 +09:16:55 3.9427 +09:17:00 3.9185 +09:17:05 3.9001 +09:17:10 3.8790 +09:17:15 3.8939 +09:17:20 3.9965 +09:17:25 3.9104 +09:17:30 3.9415 +09:17:35 3.9409 +09:17:40 3.9442 +09:17:45 3.9813 +09:17:50 4.0043 +09:17:55 4.0353 +09:18:00 3.9322 +09:18:05 3.9964 +09:18:10 3.9802 +09:18:15 3.8984 +09:18:20 3.9210 +09:18:25 3.9290 +09:18:30 4.0317 +09:18:35 4.0445 +09:18:40 4.0734 +09:18:45 3.9932 +09:18:50 3.8709 +09:18:55 3.9434 +09:19:00 3.8759 +09:19:05 3.9505 +09:19:10 3.9768 +09:19:15 3.9329 +09:19:20 3.9821 +09:19:25 3.9729 +09:19:30 4.0061 +09:19:35 4.0010 +09:19:40 3.9859 +09:19:45 3.9958 +09:19:50 3.9214 +09:19:55 3.9344 +09:20:00 4.0097 +09:20:05 3.9930 +09:20:10 3.9946 +09:20:15 3.9889 +09:20:20 3.9376 +09:20:25 3.9850 +09:20:30 4.0629 +09:20:35 4.0633 +09:20:40 3.9687 +09:20:45 3.9667 +09:20:50 3.9618 +09:20:55 3.9653 +09:21:00 3.9441 +09:21:05 3.8934 +09:21:10 3.8937 +09:21:15 3.9045 +09:21:20 3.9774 +09:21:25 4.0146 +09:21:30 3.9514 +09:21:35 3.9383 +09:21:40 3.8049 +09:21:45 3.8884 +09:21:50 3.9176 +09:21:55 3.9486 +09:22:00 3.9925 +09:22:05 3.8731 +09:22:10 3.8810 +09:22:15 3.9566 +09:22:20 3.9881 +09:22:25 3.9633 +09:22:30 4.0458 +09:22:35 3.9979 +09:22:40 4.0002 +09:22:45 3.9824 +09:22:50 4.0000 +09:22:55 3.9607 +09:23:00 3.9936 +09:23:05 4.0417 +09:23:10 4.0602 +09:23:15 4.0866 +09:23:20 4.0276 +09:23:25 4.0074 +09:23:30 3.9939 +09:23:35 3.8641 +09:23:40 3.8143 +09:23:45 3.8374 +09:23:50 3.9005 +09:23:55 3.9436 +09:24:00 4.0564 +09:24:05 3.9793 +09:24:10 3.9287 +09:24:15 3.9742 +09:24:20 4.0483 +09:24:25 3.9959 +09:24:30 3.8512 +09:24:35 3.9290 +09:24:40 3.9476 +09:24:45 3.9360 +09:24:50 4.0257 +09:24:55 3.9716 +09:25:00 3.9111 +09:25:05 3.8669 +09:25:10 3.9086 +09:25:15 4.0175 +09:25:20 4.0335 +09:25:25 3.9244 +09:25:30 3.9637 +09:25:35 3.9755 +09:25:40 3.8314 +09:25:45 3.9610 +09:25:50 3.9638 +09:25:55 3.9125 +09:26:00 4.0177 +09:26:05 4.0074 +09:26:10 4.0639 +09:26:15 3.9969 +09:26:20 3.9355 +09:26:25 3.9460 +09:26:30 4.0225 +09:26:35 3.9444 +09:26:40 3.9885 +09:26:45 3.9520 +09:26:50 3.9252 +09:26:55 3.8318 +09:27:00 3.9500 +09:27:05 3.9884 +09:27:10 3.9043 +09:27:15 3.9582 +09:27:20 3.9076 +09:27:25 3.9684 +09:27:30 4.0209 +09:27:35 4.0364 +09:27:40 3.9894 +09:27:45 3.9853 +09:27:50 4.0116 +09:27:55 4.0085 +09:28:00 4.0179 +09:28:05 3.9244 +09:28:10 3.9808 +09:28:15 3.8864 +09:28:20 3.9076 +09:28:25 4.0409 +09:28:30 3.9916 +09:28:35 4.0585 +09:28:40 4.0840 +09:28:45 4.0490 +09:28:50 3.9851 +09:28:55 3.9885 +09:29:00 4.1232 +09:29:05 4.0850 +09:29:10 4.0292 +09:29:15 3.9309 +09:29:20 3.9597 +09:29:25 3.9522 +09:29:30 3.9379 +09:29:35 4.0095 +09:29:40 4.0190 +09:29:45 4.0249 +09:29:50 4.0412 +09:29:55 3.9565 +09:30:00 4.0236 +09:30:05 4.0018 +09:30:10 3.9787 +09:30:15 3.9928 +09:30:20 3.9974 +09:30:25 3.9451 +09:30:30 4.0004 +09:30:35 3.9485 +09:30:40 3.9685 +09:30:45 4.0494 +09:30:50 3.8650 +09:30:55 3.9053 +09:31:00 4.0077 +09:31:05 3.9653 +09:31:10 3.9742 +09:31:15 4.0010 +09:31:20 3.8874 +09:31:25 3.8892 +09:31:30 3.9428 +09:31:35 3.9144 +09:31:40 3.9430 +09:31:45 3.8940 +09:31:50 3.8977 +09:31:55 3.9750 +09:32:00 4.0103 +09:32:05 4.0475 +09:32:10 4.0435 +09:32:15 4.0587 +09:32:20 3.9976 +09:32:25 4.0013 +09:32:30 4.0503 +09:32:35 4.0356 +09:32:40 3.9835 +09:32:45 3.9023 +09:32:50 3.9678 +09:32:55 3.9503 +09:33:02 3.9728 +09:33:05 3.9769 +09:33:10 3.9422 +09:33:15 3.9545 +09:33:21 3.9696 +09:33:25 3.8915 +09:33:31 3.9039 +09:33:35 3.9445 +09:33:40 4.0206 +09:33:45 3.9472 +09:33:52 3.9385 +09:33:55 3.8471 +09:34:00 3.9455 +09:34:05 4.0128 +09:34:10 3.9719 +09:34:15 3.9362 +09:34:20 3.8932 +09:34:25 4.0424 +09:34:30 4.0974 +09:34:35 3.9899 +09:34:40 4.0433 +09:34:45 4.0237 +09:34:50 3.9966 +09:34:55 3.8746 +09:35:00 3.8128 +09:35:05 3.9183 +09:35:10 3.9684 +09:35:15 4.0538 +09:35:20 4.1515 +09:35:25 4.1264 +09:35:30 4.0369 +09:35:35 4.0213 +09:35:40 4.0043 +09:35:45 4.0158 +09:35:50 3.8874 +09:35:55 3.8374 +09:36:00 3.8926 +09:36:07 3.9555 +09:36:10 3.9158 +09:36:15 3.8723 +09:36:20 3.8382 +09:36:25 3.8439 +09:36:30 3.9041 +09:36:35 3.9704 +09:36:40 3.9511 +09:36:45 3.9337 +09:36:50 4.0467 +09:36:55 4.0334 +09:37:00 3.9430 +09:37:05 3.8771 +09:37:10 3.9500 +09:37:15 3.9846 +09:37:20 3.9649 +09:37:25 3.9022 +09:37:30 3.8949 +09:37:35 3.9527 +09:37:40 3.9218 +09:37:45 3.9184 +09:37:50 3.9804 +09:37:55 3.9053 +09:38:00 3.9329 +09:38:05 3.9465 +09:38:10 3.9204 +09:38:15 3.8679 +09:38:20 3.9099 +09:38:25 3.9964 +09:38:30 3.9820 +09:38:35 4.0495 +09:38:40 4.0729 +09:38:45 4.0411 +09:38:50 4.0335 +09:38:55 4.0293 +09:39:00 4.0594 +09:39:05 3.9820 +09:39:10 3.9536 +09:39:15 3.8824 +09:39:20 3.8979 +09:39:25 3.9119 +09:39:30 3.9845 +09:39:35 4.0669 +09:39:40 4.0154 +09:39:45 3.8817 +09:39:50 3.9392 +09:39:55 3.9675 +09:40:00 3.9735 +09:40:05 3.9735 +09:40:10 3.9781 +09:40:15 3.9865 +09:40:20 3.9901 +09:40:25 3.9914 +09:40:30 3.9495 +09:40:35 3.9514 +09:40:40 3.9518 +09:40:45 3.9480 +09:40:50 3.9451 +09:40:55 3.9441 +09:41:00 3.9600 +09:41:05 4.0559 +09:41:10 4.0028 +09:41:15 4.0028 +09:41:20 3.9576 +09:41:25 3.8866 +09:41:30 3.9163 +09:41:35 3.9252 +09:41:40 3.9763 +09:41:45 4.0747 +09:41:50 4.0355 +09:41:55 4.0340 +09:42:00 4.0756 +09:42:05 4.0985 +09:42:10 4.0372 +09:42:15 3.9845 +09:42:20 4.0437 +09:42:25 3.9746 +09:42:30 4.0273 +09:42:35 3.8792 +09:42:40 3.9676 +09:42:45 4.0374 +09:42:50 4.1033 +09:42:55 4.1044 +09:43:00 4.1182 +09:43:05 4.0746 +09:43:10 4.1441 +09:43:15 4.0915 +09:43:20 4.0743 +09:43:25 4.1226 +09:43:30 4.0927 +09:43:35 4.0978 +09:43:40 4.0538 +09:43:45 4.0307 +09:43:50 3.9976 +09:43:55 3.9155 +09:44:00 4.0192 +09:44:05 4.0911 +09:44:10 4.1070 +09:44:15 4.0718 +09:44:20 4.0149 +09:44:25 4.0042 +09:44:30 3.9882 +09:44:35 4.0548 +09:44:40 4.0148 +09:44:45 3.9106 +09:44:50 4.0067 +09:44:55 4.0710 +09:45:00 3.9924 +09:45:05 4.0026 +09:45:10 3.9525 +09:45:15 4.0169 +09:45:20 3.9477 +09:45:25 3.9071 +09:45:30 3.9583 +09:45:35 3.9589 +09:45:40 3.9991 +09:45:45 4.0727 +09:45:50 4.0091 +09:45:55 3.9534 +09:46:00 4.0820 +09:46:05 4.1160 +09:46:10 4.0894 +09:46:15 4.0011 +09:46:20 3.9954 +09:46:25 3.9608 +09:46:30 3.9897 +09:46:35 3.9456 +09:46:40 3.9599 +09:46:45 3.9983 +09:46:50 4.0550 +09:46:55 4.0984 +09:47:00 4.0649 +09:47:05 4.0484 +09:47:10 3.9757 +09:47:15 3.9836 +09:47:20 4.0030 +09:47:25 4.0606 +09:47:30 4.0414 +09:47:35 4.0162 +09:47:40 4.0602 +09:47:45 4.0070 +09:47:50 4.1211 +09:47:55 4.0743 +09:48:00 4.0283 +09:48:05 4.1394 +09:48:10 4.2489 +09:48:15 4.1891 +09:48:20 4.1098 +09:48:25 4.0643 +09:48:30 4.0177 +09:48:35 3.9889 +09:48:40 4.0019 +09:48:45 4.1154 +09:48:50 4.1039 +09:48:55 4.0413 +09:49:00 4.0292 +09:49:05 4.0173 +09:49:10 4.0202 +09:49:15 4.0435 +09:49:20 4.0260 +09:49:25 4.0894 +09:49:30 4.0815 +09:49:35 4.1953 +09:49:40 4.1304 +09:49:45 4.1109 +09:49:50 4.0761 +09:49:55 4.0359 +09:50:00 3.9914 +09:50:05 4.0975 +09:50:10 4.1445 +09:50:15 4.1314 +09:50:20 4.0451 +09:50:25 4.0479 +09:50:30 4.0062 +09:50:35 4.0001 +09:50:40 4.0693 +09:50:45 4.1258 +09:50:50 4.0258 +09:50:55 4.0563 +09:51:00 4.1786 +09:51:05 4.0785 +09:51:10 4.0746 +09:51:15 4.0489 +09:51:20 3.9864 +09:51:25 4.0283 +09:51:30 4.0087 +09:51:35 4.0923 +09:51:40 4.1493 +09:51:45 4.1672 +09:51:50 4.0838 +09:51:55 4.0516 +09:52:00 4.0783 +09:52:05 4.1618 +09:52:10 4.1663 +09:52:15 4.0433 +09:52:20 4.0241 +09:52:25 3.9010 +09:52:30 3.9354 +09:52:35 4.0255 +09:52:40 4.0281 +09:52:45 4.0441 +09:52:50 4.0796 +09:52:55 4.1222 +09:53:00 4.0795 +09:53:05 4.0863 +09:53:10 4.1284 +09:53:15 4.1350 +09:53:20 4.1341 +09:53:25 4.1147 +09:53:30 4.1407 +09:53:35 4.1428 +09:53:40 4.1368 +09:53:45 4.1213 +09:53:50 4.0971 +09:53:55 3.9504 +09:54:00 3.9375 +09:54:05 4.0359 +09:54:10 4.0653 +09:54:15 4.0977 +09:54:20 4.0772 +09:54:25 4.1851 +09:54:30 4.2068 +09:54:35 4.1559 +09:54:40 4.1528 +09:54:45 4.1780 +09:54:50 4.1068 +09:54:55 4.0843 +09:55:00 4.0484 +09:55:05 4.0639 +09:55:10 4.1047 +09:55:15 4.0662 +09:55:20 3.9734 +09:55:25 3.9199 +09:55:30 4.0582 +09:55:35 4.1753 +09:55:40 4.2284 +09:55:45 4.1133 +09:55:50 4.0931 +09:55:55 4.0467 +09:56:00 4.0667 +09:56:05 4.1194 +09:56:10 4.0805 +09:56:15 4.0952 +09:56:20 4.1551 +09:56:25 4.2370 +09:56:30 4.1593 +09:56:35 4.0834 +09:56:40 4.1003 +09:56:45 4.1730 +09:56:50 4.1709 +09:56:55 4.1149 +09:57:00 4.1200 +09:57:05 4.1790 +09:57:10 4.1433 +09:57:15 4.1328 +09:57:20 4.0720 +09:57:25 4.1192 +09:57:30 4.1593 +09:57:35 4.1201 +09:57:40 4.0631 +09:57:46 4.0085 +09:57:50 4.0861 +09:57:55 4.0710 +09:58:00 4.1447 +09:58:05 4.1334 +09:58:10 4.1315 +09:58:15 4.1230 +09:58:20 4.2004 +09:58:25 4.2006 +09:58:30 4.0918 +09:58:35 4.0489 +09:58:40 4.1218 +09:58:46 4.1397 +09:58:50 4.1492 +09:58:55 4.2171 +09:59:00 4.1661 +09:59:05 4.1092 +09:59:10 4.0616 +09:59:15 4.0564 +09:59:20 4.0529 +09:59:25 4.1183 +09:59:30 4.1689 +09:59:35 4.1584 +09:59:40 4.1458 +09:59:45 4.0754 +09:59:50 4.0178 +09:59:55 4.0501 +10:00:00 4.1977 +10:00:05 4.2674 +10:00:10 4.1983 +10:00:15 4.0753 +10:00:20 3.9931 +10:00:25 4.0636 +10:00:30 4.0834 +10:00:45 4.0694 +10:00:50 4.1598 +10:00:55 4.1347 +10:01:00 4.1412 +10:01:05 4.1510 +10:01:10 4.1470 +10:01:15 4.0717 +10:01:20 4.0523 +10:01:25 4.2238 +10:01:30 4.1766 +10:01:35 4.1164 +10:01:40 4.1662 +10:01:45 4.1937 +10:01:50 4.1156 +10:01:55 4.0887 +10:02:00 4.2136 +10:02:05 4.1548 +10:02:12 4.1868 +10:02:15 4.1844 +10:02:20 4.1122 +10:02:25 4.0962 +10:02:30 4.0991 +10:02:35 4.0933 +10:02:42 4.2038 +10:02:45 4.1918 +10:02:50 4.1831 +10:02:55 4.0717 +10:03:00 4.0739 +10:03:05 4.0588 +10:03:10 4.1502 +10:03:15 4.1033 +10:03:21 4.1834 +10:03:25 4.1737 +10:03:30 4.2225 +10:03:35 4.0744 +10:03:40 4.0806 +10:03:45 4.1095 +10:03:50 4.1095 +10:03:55 4.1157 +10:04:00 4.0868 +10:04:05 4.1876 +10:04:10 4.2363 +10:04:15 4.2441 +10:04:20 4.2084 +10:04:25 4.0711 +10:04:30 4.0777 +10:04:35 4.0843 +10:04:40 4.0911 +10:04:45 4.0911 +10:04:50 4.0977 +10:04:55 4.1013 +10:05:00 4.1850 +10:05:05 4.2107 +10:05:10 4.1775 +10:05:15 4.2481 +10:05:22 4.1634 +10:05:25 4.2640 +10:05:30 4.2360 +10:05:35 4.2693 +10:05:40 4.3123 +10:05:45 4.2252 +10:05:50 4.2214 +10:05:55 4.2603 +10:06:00 4.2239 +10:06:05 4.1652 +10:06:10 4.1733 +10:06:18 4.3517 +10:06:21 4.3203 +10:06:25 4.3227 +10:06:30 4.3146 +10:06:35 4.3042 +10:06:40 4.2688 +10:06:46 4.3110 +10:06:50 4.3023 +10:06:55 4.3432 +10:07:00 4.3735 +10:07:05 4.3672 +10:07:10 4.3883 +10:07:15 4.2332 +10:07:20 4.2698 +10:07:25 4.2874 +10:07:30 4.3263 +10:07:35 4.3695 +10:07:40 4.3052 +10:07:45 4.2942 +10:07:51 4.3971 +10:07:55 4.4498 +10:08:00 4.4626 +10:08:05 4.4267 +10:08:10 4.4793 +#period 5 exact 1 isdst 1 +10:08:16 +10:08:20 undefined +10:08:25 4.5309 +10:08:30 4.4898 +10:08:35 4.4469 +10:08:40 4.4667 +10:08:45 4.4009 +10:08:50 4.4302 +10:08:55 4.4369 +10:09:00 4.4604 +10:09:05 4.4402 +10:09:10 4.4093 +10:09:15 4.3947 +10:09:20 4.4626 +10:09:25 4.4533 +10:09:30 4.5054 +10:09:35 4.6057 +10:09:40 4.5314 +10:09:45 4.5361 +10:09:50 4.5028 +10:09:55 4.5615 +10:10:00 4.6017 +10:10:05 4.5683 +10:10:10 4.5588 +10:10:15 4.5805 +10:10:20 4.5144 +10:10:25 4.4799 +10:10:30 4.5251 +10:10:35 4.5545 +10:10:40 4.5417 +10:10:45 4.5881 +10:10:50 4.6495 +10:10:55 4.6175 +10:11:00 4.5833 +10:11:05 4.5402 +10:11:10 4.6276 +10:11:15 4.6846 +10:11:20 4.6756 +10:11:25 4.6362 +10:11:31 4.7040 +10:11:35 4.7006 +10:11:40 4.7173 +10:11:45 4.6859 +10:11:50 4.7699 +10:11:55 4.8866 +10:12:00 4.8699 +10:12:05 4.7715 +10:12:11 4.9611 +10:12:15 4.9410 +10:12:20 4.9797 +10:12:25 4.9349 +10:12:30 5.0345 +10:12:35 5.1417 +10:12:40 5.0372 +10:12:45 5.0190 +10:12:50 5.1549 +10:12:55 4.9726 +10:13:00 4.8602 +10:13:05 4.9784 +10:13:10 4.8996 +10:13:15 4.8955 +10:13:20 4.7904 +10:13:25 4.6539 +10:13:30 4.7712 +10:13:35 4.6408 +10:13:40 4.0118 +10:13:45 4.2130 +10:13:50 3.9624 +10:13:55 3.8323 +10:14:00 3.8840 +10:14:05 3.7873 +10:14:12 3.7908 +10:14:15 3.7038 +10:14:20 3.8138 +10:14:25 3.6026 +10:14:30 3.4014 +10:14:37 3.0824 +10:14:40 3.1093 +10:14:45 3.0469 +10:14:50 3.0370 +10:14:55 2.9698 +10:15:02 2.9821 +10:15:05 2.9281 +10:15:10 2.9533 +10:15:16 2.7299 +10:15:20 2.8013 +10:15:25 2.7915 +10:15:30 2.7123 +10:15:37 2.7727 +10:15:40 2.6613 +10:15:45 2.6136 +10:15:50 2.5886 +10:15:55 2.5668 +10:16:01 2.5513 +10:16:06 2.5195 +10:16:10 2.5175 +10:16:16 2.4045 +10:16:21 2.3603 +10:16:25 2.3563 +10:16:30 2.3686 +10:16:35 2.2853 +10:16:40 2.2018 +10:16:45 2.1908 +10:16:51 2.1988 +10:16:56 2.1741 +10:17:00 2.1728 +10:17:05 2.1435 +10:17:10 2.0850 +10:17:15 2.0945 +10:17:20 2.0995 +10:17:25 2.1094 +10:17:30 2.0759 +10:17:35 2.0773 +10:17:40 2.0447 +10:17:45 1.9825 +10:17:50 2.0116 +10:17:55 2.0028 +10:18:00 1.9688 +10:18:05 2.0344 +10:18:10 1.9971 +10:18:15 1.8735 +10:18:20 1.8866 +10:18:25 1.9595 +10:18:30 1.9283 +10:18:36 1.8870 +10:18:40 1.8669 +10:18:45 1.8315 +10:18:50 1.8762 +10:18:55 1.8520 +10:19:00 1.7964 +10:19:05 1.7248 +10:19:10 1.7084 +10:19:15 1.7557 +10:19:20 1.7257 +10:19:25 1.7357 +10:19:30 1.6880 +10:19:35 1.7390 +10:19:40 1.7337 +10:19:45 1.6693 +10:19:50 1.6945 +10:19:55 1.6755 +10:20:00 1.6237 +10:20:05 1.6217 +10:20:10 1.6092 +10:20:15 1.6295 +10:20:20 1.6135 +10:20:25 1.6078 +10:20:30 1.6020 +10:20:35 1.6016 +10:20:40 1.5988 +10:20:45 1.5802 +10:20:50 1.6069 +10:20:55 1.6013 +10:21:00 1.6061 +10:21:05 1.5960 +10:21:11 1.5893 +10:21:15 1.5743 +10:21:20 1.5583 +10:21:25 1.5448 +10:21:30 1.5502 +10:21:35 1.5512 +10:21:40 1.5029 +10:21:45 1.5095 +10:21:50 1.4845 +10:21:55 1.4593 +10:22:00 1.4726 +10:22:05 1.4739 +10:22:10 1.4852 +10:22:15 1.4988 +10:22:20 1.4806 +10:22:25 1.4789 +10:22:30 1.4713 +10:22:35 1.4371 +10:22:40 1.4391 +10:22:45 1.4764 +10:22:50 1.5071 +10:22:55 1.4604 +10:23:00 1.4464 +10:23:05 1.4523 +10:23:10 1.4435 +10:23:15 1.4598 +10:23:20 1.4583 +10:23:25 1.4309 +10:23:30 1.4178 +10:23:35 1.4097 +10:23:40 1.4047 +10:23:45 1.4366 +10:23:50 1.4170 +10:23:55 1.4189 +10:24:00 1.3959 +10:24:05 1.3850 +10:24:10 1.3815 +10:24:15 1.3833 +10:24:20 1.3898 +10:24:28 1.4062 +10:24:30 1.4138 +10:24:35 1.4155 +10:24:40 1.4018 +10:24:46 1.3788 +10:24:50 1.3887 +10:24:55 1.3709 +10:25:07 1.3212 +10:25:10 1.3345 +10:25:15 1.3373 +10:25:20 1.3470 +10:25:25 1.3533 +10:25:30 1.3266 +10:25:35 1.3014 +10:25:40 1.3064 +10:25:45 1.3088 +10:25:50 1.3298 +10:25:55 1.3265 +10:26:00 1.3176 +10:26:05 1.3095 +10:26:10 1.3285 +10:26:15 1.3067 +10:26:20 1.3128 +10:26:25 1.3157 +10:26:30 1.3198 +10:26:35 1.3351 +10:26:40 1.3204 +10:26:45 1.3256 +10:26:50 1.3135 +10:26:55 1.2743 +10:27:00 1.2764 +10:27:05 1.3021 +10:27:10 1.2929 +10:27:15 1.2802 +10:27:20 1.2837 +10:27:25 1.2751 +10:27:30 1.2797 +10:27:35 1.2815 +10:27:40 1.2969 +10:27:45 1.2695 +10:27:50 1.2570 +10:27:55 1.2657 +10:28:00 1.2556 +10:28:05 1.2593 +10:28:10 1.2378 +10:28:15 1.2661 +10:28:20 1.2492 +10:28:25 1.2548 +10:28:30 1.2944 +10:28:35 1.2595 +10:28:40 1.2985 +10:28:45 1.3029 +10:28:50 1.3073 +10:28:55 1.2707 +10:29:00 1.2547 +10:29:05 1.2262 +10:29:10 1.2370 +10:29:15 1.2449 +10:29:20 1.2542 +10:29:25 1.2477 +10:29:30 1.2694 +10:29:35 1.2559 +10:29:40 1.2441 +10:29:45 1.2658 +10:29:50 1.2425 +10:29:55 1.2307 +10:30:00 1.2271 +10:30:05 1.2296 +10:30:10 1.2629 +10:30:15 1.2348 +10:30:20 1.2277 +10:30:25 1.2394 +10:30:30 1.2062 +10:30:35 1.1937 +10:30:40 1.2118 +10:30:45 1.2176 +10:30:50 1.2219 +10:30:55 1.2156 +10:31:00 1.2225 +10:31:05 1.2177 +10:31:10 1.2405 +10:31:15 1.2575 +10:31:20 1.2432 +10:31:25 1.2235 +10:31:30 1.2277 +10:31:35 1.2281 +10:31:40 1.2086 +10:31:45 1.2086 +10:31:50 1.2286 +10:32:05 1.2398 +10:32:10 1.2044 +10:32:15 1.2039 +10:32:20 1.1998 +10:32:25 1.2254 +10:32:30 1.2332 +10:32:35 1.2403 +10:32:40 1.2338 +10:32:45 1.2160 +10:32:50 1.2091 +10:32:55 1.2151 +10:33:00 1.2040 +10:33:05 1.2147 +10:33:10 1.2183 +10:33:15 1.2197 +10:33:20 1.2240 +10:33:25 1.2205 +10:33:30 1.1993 +10:33:35 1.2179 +10:33:40 1.2217 +10:33:45 1.2175 +10:33:50 1.2162 +10:33:55 1.2168 +10:34:00 1.2218 +10:34:05 1.2217 +10:34:10 1.2016 +10:34:15 1.2193 +10:34:20 1.2085 +10:34:25 1.2214 +10:34:30 1.1953 +10:34:35 1.1889 +10:34:40 1.2015 +10:34:45 1.1777 +10:34:50 1.1770 +10:34:55 1.1864 +10:35:00 1.1965 +10:35:05 1.2061 +10:35:10 1.1853 +10:35:15 1.1827 +10:35:20 1.1663 +10:35:25 1.1647 +10:35:30 1.1472 +10:35:35 1.1622 +10:35:40 1.1622 +10:35:45 1.1618 +10:35:50 1.1602 +10:35:55 1.1596 +10:36:00 1.1582 +10:36:05 1.1554 +10:36:10 1.1554 +10:36:15 1.1543 +10:36:20 1.1524 +10:36:25 1.1500 +10:36:30 1.1475 +10:36:35 1.1467 +10:36:40 1.1467 +10:36:45 1.1441 +10:36:50 1.1424 +10:36:55 1.1390 +10:37:00 1.1365 +10:37:05 1.1365 +10:37:10 1.1349 +10:37:15 1.1324 +10:37:20 1.1300 +10:37:25 1.1276 +10:37:30 1.1245 +10:37:35 1.1233 +10:37:40 1.1233 +10:37:45 1.1225 +10:37:50 1.1191 +10:37:55 1.1180 +10:38:00 1.1145 +10:38:05 1.1145 +10:38:10 1.1113 +10:38:15 1.1082 +10:38:20 1.1069 +10:38:25 1.1015 +10:38:30 1.0979 +10:38:35 1.0979 +10:38:40 1.0936 +10:38:45 1.0879 +10:38:50 1.0828 +10:38:55 1.0792 +10:39:00 1.0729 +10:39:05 1.0729 +10:39:10 1.0676 +10:39:15 1.0605 +10:39:22 1.0539 +10:39:26 1.0467 +10:39:30 0.84536 +10:39:35 0.74483 +10:39:40 0.75113 +10:39:45 0.74009 +10:39:50 0.73113 +10:39:55 0.72446 +10:40:00 0.72089 +10:40:05 0.71807 +10:40:10 0.70345 +10:40:15 0.70250 +10:40:20 0.69386 +10:40:25 0.68588 +10:40:30 0.66583 +10:40:35 0.65567 +10:40:40 0.65210 +10:40:45 0.64048 +10:40:50 0.63952 +10:40:55 0.62747 +10:41:00 0.62174 +10:41:05 0.61403 +10:41:10 0.60431 +10:41:15 0.60070 +10:41:20 0.59183 +10:41:25 0.58700 +10:41:30 0.57518 +10:41:35 0.56262 +10:41:40 0.55496 +10:41:45 0.55109 +10:41:50 0.53962 +10:41:55 0.53210 +10:42:00 0.52577 +10:42:05 0.52365 +10:42:10 0.51646 +10:42:15 0.50681 +10:42:20 0.49762 +10:42:25 0.49408 +10:42:30 0.48980 +10:42:35 0.48224 +10:42:40 0.47788 +10:42:45 0.47288 +10:42:50 0.46190 +10:42:55 0.45356 +10:43:00 0.44897 +10:43:05 0.44388 +10:43:10 0.43695 +10:43:15 0.43316 +10:43:20 0.42751 +10:43:25 0.42078 +10:43:30 0.41658 +10:43:35 0.41287 +10:43:40 0.40774 +10:43:45 0.40262 +10:43:50 0.39624 +10:43:55 0.38923 +10:44:00 0.38594 +10:44:05 0.38164 +10:44:10 0.37612 +10:44:15 0.37139 +10:44:20 0.36717 +10:44:25 0.36472 +10:44:30 0.36209 +10:44:35 0.35429 +10:44:41 0.35104 +10:44:45 0.35039 +10:44:51 0.34263 +10:44:55 0.34034 +10:45:00 0.33901 +10:45:05 0.33084 +10:45:10 0.32455 +10:45:15 0.32082 +10:45:20 0.31893 +10:45:25 0.31708 +10:45:30 0.31320 +10:45:36 0.30916 +10:45:40 0.30400 +10:45:45 0.30150 +10:45:50 0.29944 +10:45:55 0.29693 +10:46:00 0.29333 +10:46:05 0.28846 +10:46:10 0.28612 +10:46:15 0.28194 +10:46:20 0.27922 +10:46:25 0.27704 +10:46:30 0.27601 +10:46:35 0.27250 +10:46:40 0.27030 +10:46:45 0.26873 +10:46:50 0.26542 +10:46:55 0.26364 +10:47:13 0.26133 +10:47:15 0.25953 +10:47:20 0.25731 +10:47:25 0.25488 +10:47:30 0.25295 +10:47:35 0.25086 +10:47:40 0.24804 +10:47:45 0.24557 +10:47:50 0.24362 +10:47:55 0.24290 +10:48:00 0.23928 +10:48:05 0.23789 +10:48:10 0.23654 +10:48:15 0.23578 +10:48:20 0.23481 +10:48:25 0.23269 +10:48:30 0.23017 +10:48:36 0.22944 +10:48:40 0.22824 +10:48:45 0.22601 +10:48:50 0.22445 +10:48:55 0.22392 +10:49:00 0.22162 +10:49:05 0.22056 +10:49:10 0.21970 +10:49:15 0.21829 +10:49:20 0.21726 +10:49:25 0.21563 +10:49:30 0.21467 +10:49:35 0.21332 +10:49:40 0.21325 +10:49:45 0.21151 +10:49:50 0.20937 +10:49:55 0.20929 +10:50:00 0.20912 +10:50:05 0.20758 +10:50:10 0.20683 +10:50:45 0.20683 +10:50:50 0.20361 +10:50:55 0.19867 +10:51:00 0.20091 +10:51:05 0.19896 +10:51:10 0.19481 +10:51:15 0.19445 +10:51:20 0.19477 +10:51:25 0.19518 +10:51:30 0.19576 +10:51:35 0.19480 +10:51:40 0.19300 +10:51:45 0.19219 +10:51:50 0.19360 +10:51:55 0.19238 +10:52:00 0.19138 +10:52:05 0.19190 +10:52:10 0.19302 +10:52:15 0.18943 +10:52:20 0.18850 +10:52:25 0.19098 +10:52:30 0.18732 +10:52:35 0.18793 +10:52:40 0.18731 +10:52:45 0.18768 +10:52:50 0.18947 +10:52:55 0.18657 +10:53:00 0.18429 +10:53:05 0.18504 +10:53:10 0.18570 +10:53:15 0.18377 +10:53:20 0.18366 +10:53:26 0.18473 +10:53:30 0.18327 +10:53:35 0.18286 +10:53:40 0.18259 +10:53:45 0.18108 +10:53:50 0.18386 +10:53:55 0.18327 +10:54:00 0.18113 +10:54:05 0.18104 +10:54:10 0.17978 +10:54:15 0.18180 +10:54:20 0.18140 +10:54:25 0.17852 +10:54:30 0.17879 +10:54:35 0.17776 +10:54:40 0.17910 +10:54:45 0.17814 +10:54:50 0.17834 +10:54:55 0.17832 +10:55:00 0.17793 +10:55:05 0.17900 +10:55:10 0.17671 +10:55:15 0.17583 +10:55:20 0.17775 +10:55:25 0.17560 +10:55:30 0.17490 +10:55:35 0.17670 +10:55:40 0.17595 +10:55:45 0.17613 +10:55:50 0.17471 +10:55:55 0.17515 +10:56:00 0.17577 +10:56:05 0.17558 +10:56:10 0.17623 +10:56:15 0.17700 +10:56:20 0.17579 +10:56:25 0.17303 +10:56:30 0.17407 +10:56:35 0.17452 +10:56:40 0.17587 +10:56:45 0.17579 +10:56:50 0.17391 +10:56:55 0.17416 +10:57:00 0.17470 +10:57:05 0.17509 +10:57:10 0.17758 +10:57:15 0.17691 +10:57:20 0.17655 +10:57:25 0.17521 +10:57:30 0.17390 +10:57:35 0.17355 +10:57:40 0.17325 +10:57:45 0.17285 +10:57:50 0.17206 +10:57:55 0.17371 +10:58:00 0.17464 +10:58:05 0.17539 +10:58:10 0.17357 +10:58:15 0.17354 +10:58:20 0.17328 +10:58:25 0.17219 +10:58:30 0.17345 +10:58:35 0.17295 +10:58:40 0.17402 +10:58:45 0.17367 +10:58:50 0.17251 +10:58:55 0.17370 +10:59:00 0.17298 +10:59:05 0.17233 +10:59:10 0.17306 +10:59:15 0.17183 +10:59:20 0.17074 +10:59:25 0.17206 +10:59:30 0.17406 +10:59:35 0.17503 +10:59:42 0.17345 +10:59:45 0.17100 +10:59:50 0.17206 +10:59:55 0.17148 +11:00:00 0.17226 +11:00:05 0.17189 +11:00:10 0.17252 +11:00:15 0.17095 +11:00:20 0.17170 +11:00:25 0.17195 +11:00:30 0.17073 +11:00:35 0.17342 +11:00:40 0.17349 +11:00:45 0.17298 +11:00:50 0.17270 +11:00:55 0.17201 +11:01:00 0.17278 +11:01:05 0.17146 +11:01:10 0.17165 +11:01:15 0.17264 +11:01:20 0.17293 +11:01:25 0.17341 +11:01:30 0.17291 +11:01:35 0.17303 +11:01:40 0.17342 +11:01:45 0.17244 +11:01:50 0.17070 +11:01:55 0.16949 +11:02:00 0.17083 +11:02:05 0.17189 +11:02:10 0.17100 +11:02:15 0.17224 +11:02:20 0.17130 +11:02:25 0.17118 +11:02:30 0.17205 +11:02:35 0.17104 +11:02:40 0.17005 +11:02:45 0.17039 +11:02:50 0.17138 +11:02:55 0.17199 +11:03:00 0.17222 +11:03:05 0.17008 +11:03:10 0.17144 +11:03:15 0.17236 +11:03:20 0.17117 +11:03:25 0.17320 +11:03:30 0.17169 +11:03:35 0.17078 +11:03:40 0.16918 +11:03:45 0.16948 +11:03:50 0.16729 +11:03:55 0.17117 +11:04:00 0.17135 +11:04:05 0.17234 +11:04:10 0.17061 +11:04:15 0.17112 +11:04:20 0.17200 +11:04:25 0.17329 +11:04:30 0.17292 +11:04:35 0.17379 +11:04:40 0.17288 +11:04:45 0.17322 +11:04:50 0.17342 +11:04:55 0.17063 +11:05:00 0.17190 +11:05:05 0.17150 +11:05:10 0.17237 +11:05:15 0.17141 +11:05:20 0.17062 +11:05:25 0.16872 +11:05:30 0.17156 +11:05:35 0.17223 +11:05:40 0.17161 +11:05:45 0.16884 +11:05:50 0.17009 +11:05:55 0.17003 +11:06:00 0.16997 +11:06:05 0.16861 +11:06:10 0.16865 +11:06:15 0.16881 +11:06:20 0.16837 +11:06:25 0.16955 +11:06:30 0.16943 +11:06:35 0.16899 +11:06:40 0.17035 +11:06:45 0.16953 +11:06:50 0.16769 +11:06:55 0.16894 +11:07:00 0.17079 +11:07:05 0.17013 +11:07:10 0.16827 +11:07:15 0.16696 +11:07:20 0.16713 +11:07:25 0.17030 +11:07:30 0.16981 +11:07:35 0.17028 +11:07:40 0.17114 +11:07:45 0.17002 +11:07:50 0.16801 +11:07:55 0.16763 +11:08:00 0.16885 +11:08:05 0.16950 +11:08:10 0.16966 +11:08:15 0.16711 +11:08:20 0.16819 +11:08:25 0.16835 +11:08:30 0.16825 +11:08:35 0.16766 +11:08:40 0.16834 +11:08:45 0.16834 +11:08:50 0.16887 +11:08:55 0.16744 +11:09:00 0.16829 +11:09:05 0.16791 +11:09:10 0.16852 +11:09:15 0.16674 +11:09:20 0.16763 +11:09:25 0.16774 +11:09:30 0.16982 +11:09:35 0.16970 +11:09:40 0.16953 +11:09:45 0.16923 +11:09:50 0.16882 +11:09:55 0.16741 +11:10:00 0.16742 +11:10:05 0.16757 +11:10:10 0.16716 +11:10:15 0.16858 +11:10:20 0.16842 +11:10:25 0.16707 +11:10:30 0.16772 +11:10:35 0.16719 +11:10:40 0.16849 +11:10:45 0.17013 +11:10:50 0.16863 +11:10:55 0.16834 +11:11:00 0.16861 +11:11:05 0.16860 +11:11:10 0.17049 +11:11:15 0.16984 +11:11:20 0.16889 +11:11:25 0.16696 +11:11:30 0.16720 +11:11:35 0.16795 +11:11:40 0.16761 +11:11:45 0.16731 +11:11:50 0.16815 +11:11:55 0.16858 +11:12:00 0.16598 +11:12:05 0.16389 +11:12:10 0.16437 +11:12:15 0.16605 +11:12:20 0.16766 +11:12:25 0.16852 +11:12:30 0.16975 +11:12:35 0.16718 +11:12:40 0.16852 +11:12:45 0.16623 +11:12:50 0.16528 +11:12:55 0.16771 +11:13:00 0.16789 +11:13:05 0.16746 +11:13:10 0.16804 +11:13:15 0.16724 +11:13:20 0.16721 +11:13:25 0.16864 +11:13:30 0.16929 +11:13:35 0.16842 +11:13:40 0.16849 +11:13:45 0.16800 +11:13:50 0.16834 +11:13:55 0.16788 +11:14:00 0.16716 +11:14:05 0.16872 +11:14:10 0.16797 +11:14:15 0.16766 +11:14:20 0.16801 +11:14:25 0.16852 +11:14:30 0.16771 +11:14:35 0.16969 +11:14:40 0.17053 +11:14:45 0.16788 +11:14:50 0.16806 +11:14:55 0.17059 +11:15:00 0.16848 +11:15:05 0.16675 +11:15:10 0.16590 +11:15:15 0.16748 +11:15:20 0.16795 +11:15:25 0.16861 +11:15:30 0.16923 +11:15:35 0.16723 +11:15:40 0.16869 +11:15:45 0.16887 +11:15:50 0.16936 +11:15:55 0.16794 +11:16:00 0.16773 +11:16:05 0.16881 +11:16:10 0.16798 +11:16:15 0.16671 +11:16:20 0.16740 +11:16:26 0.16839 +11:16:30 0.16834 +11:16:35 0.16775 +11:16:40 0.16933 +11:16:46 0.17123 +11:16:50 0.16948 +11:16:55 0.16880 +11:17:00 0.17043 +11:17:05 0.16880 +11:17:10 0.16783 +11:17:15 0.16921 +11:17:20 0.16759 +11:17:25 0.16682 +11:17:30 0.16761 +11:17:36 0.16722 +11:17:40 0.16778 +11:17:45 0.16816 +11:17:50 0.16604 +11:17:55 0.16583 +11:18:02 0.16627 +11:18:07 0.16714 +11:18:10 0.16747 +11:18:15 0.16686 +11:18:20 0.16604 +11:18:25 0.16638 +11:18:30 0.16838 +11:18:35 0.16923 +11:18:40 0.16864 +11:18:45 0.16720 +11:18:50 0.16739 +11:18:55 0.16785 +11:19:00 0.16762 +11:19:05 0.16838 +11:19:10 0.16712 +11:19:15 0.16734 +11:19:20 0.16841 +11:19:25 0.16940 +11:19:30 0.16815 +11:19:35 0.16607 +11:19:40 0.16655 +11:19:45 0.16817 +11:19:50 0.16744 +11:19:55 0.16822 +11:20:00 0.16871 +11:20:06 0.16801 +11:20:11 0.16741 +11:20:15 0.16816 +11:20:20 0.16876 +11:20:25 0.16859 +11:20:30 0.16689 +11:20:35 0.16718 +11:20:40 0.16904 +11:20:45 0.16991 +11:20:50 0.16842 +11:20:55 0.16692 +11:21:01 0.17043 +11:21:05 0.16805 +11:21:10 0.16636 +11:21:15 0.16793 +11:21:20 0.16950 +11:21:26 0.16994 +11:21:30 0.16887 +11:21:35 0.16761 +11:21:41 0.16704 +11:21:45 0.16595 +11:21:50 0.16655 +11:22:06 0.16854 +11:22:10 0.16860 +11:22:15 0.16760 +11:22:20 0.16762 +11:22:25 0.16694 +11:22:30 0.16717 +11:22:35 0.16594 +11:22:40 0.16646 +11:22:45 0.16853 +11:22:50 0.16712 +11:22:55 0.16666 +11:23:00 0.16712 +11:23:05 0.16726 +11:23:10 0.16805 +11:23:15 0.16821 +11:23:20 0.16831 +11:23:25 0.16886 +11:23:30 0.16968 +11:23:35 0.16924 +11:23:40 0.16925 +11:23:45 0.16800 +11:23:50 0.16708 +11:23:56 0.16636 +11:24:00 0.16582 +11:24:05 0.16608 +11:24:10 0.16755 +11:24:15 0.16837 +11:24:24 0.16679 +11:24:25 0.16745 +11:24:30 0.16763 +11:24:35 0.16793 +11:24:40 0.16724 +11:24:46 0.16732 +11:24:50 0.16937 +11:24:56 0.16865 +#period 5 exact 1 isdst 1 +11:25:02 +11:25:08 undefined +11:25:10 0.16569 +11:25:15 0.16780 +11:25:20 0.16914 +11:25:25 0.16709 +11:25:30 0.16637 +11:25:42 0.16662 +11:25:45 0.16746 +11:25:50 0.16715 +11:25:55 0.16675 +11:26:00 0.16642 +11:26:05 0.16650 +11:26:10 0.16836 +11:26:15 0.16760 +11:26:20 0.16687 +11:26:25 0.16660 +11:26:30 0.16597 +11:26:35 0.16758 +11:26:40 0.16734 +11:26:45 0.16683 +11:26:50 0.16935 +11:26:55 0.16817 +11:27:00 0.16830 +11:27:05 0.17000 +11:27:10 0.16855 +11:27:15 0.16712 +11:27:20 0.16844 +11:27:25 0.16978 +11:27:30 0.16931 +11:27:35 0.17017 +11:27:40 0.16953 +11:27:45 0.16890 +11:27:50 0.16699 +11:27:55 0.16745 +11:28:00 0.16577 +11:28:05 0.16523 +11:28:10 0.16571 +11:28:15 0.16856 +11:28:20 0.16771 +11:28:25 0.16680 +11:28:30 0.16751 +11:28:35 0.16886 +11:28:40 0.16900 +11:28:45 0.16960 +11:28:50 0.16849 +11:28:55 0.16824 +11:29:00 0.16911 +11:29:05 0.16797 +11:29:10 0.16759 +11:29:15 0.16819 +11:29:20 0.16609 +11:29:25 0.16441 +11:29:30 0.16637 +11:29:35 0.16731 +11:29:40 0.16724 +11:29:45 0.16788 +11:29:50 0.16833 +11:29:55 0.16854 +11:30:00 0.16597 +11:30:05 0.16670 +11:30:10 0.16639 +11:30:15 0.16540 +11:30:20 0.16672 +11:30:25 0.16862 +11:30:30 0.16860 +11:30:35 0.16610 +11:30:40 0.16525 +11:30:45 0.16755 +11:30:50 0.16710 +11:30:55 0.16703 +11:31:00 0.16640 +11:31:05 0.16715 +11:31:10 0.16835 +11:31:15 0.16808 +11:31:20 0.16869 +11:31:25 0.16671 +11:31:30 0.16719 +11:31:35 0.16659 +11:31:40 0.16743 +11:31:45 0.16758 +11:31:50 0.16752 +11:31:55 0.16582 +11:32:00 0.16755 +11:32:05 0.16897 +11:32:10 0.16824 +11:32:15 0.16738 +11:32:20 0.16826 +11:32:25 0.16943 +11:32:30 0.16754 +11:32:35 0.16890 +11:32:40 0.16896 +11:32:45 0.16641 +#period 5 exact 1 isdst 1 +11:32:51 +11:32:54 undefined +11:32:55 0.16691 +11:33:00 0.16803 +11:33:05 0.16942 +11:33:10 0.16927 +11:33:15 0.16927 +11:33:20 0.16815 +11:33:25 0.16657 +11:33:30 0.16763 +#period 5 exact 1 isdst 1 +11:33:34 +11:33:36 undefined +11:33:40 0.16720 +11:33:45 0.16802 +11:33:50 0.16828 +11:33:55 0.16962 +11:34:00 0.16724 +11:34:05 0.18807 +11:34:10 0.26405 +11:34:15 0.29695 +11:34:20 0.28557 +11:34:25 0.27158 +11:34:30 0.26607 +11:34:35 0.25766 +11:34:40 0.24693 +11:34:45 0.24104 +11:34:50 0.23841 +11:34:55 0.23652 +11:35:00 0.22990 +11:35:05 0.22422 +11:35:10 0.22408 +11:35:15 0.22204 +11:35:20 0.21966 +11:35:25 0.21860 +11:35:30 0.21610 +11:35:35 0.21556 +11:35:40 0.21210 +11:35:45 0.21183 +11:35:50 0.20973 +11:35:55 0.20951 +11:36:00 0.20950 +11:36:05 0.20584 +11:36:10 0.20517 +11:36:15 0.20570 +11:36:20 0.20696 +11:36:25 0.20412 +11:36:30 0.20317 +11:36:35 0.20353 +11:36:40 0.20343 +11:36:45 0.20220 +11:36:50 0.20114 +11:36:55 0.19993 +11:37:00 0.19829 +11:37:05 0.21647 +11:37:19 0.24839 +11:37:20 0.25409 +11:37:25 0.30658 +11:37:30 0.31093 +11:37:35 0.30337 +11:37:40 0.28532 +11:37:45 0.27527 +11:37:50 0.26959 +11:37:55 0.26272 +11:38:00 0.29671 +11:38:05 0.37862 +11:38:10 0.40128 +11:38:15 0.38633 +11:38:20 0.37319 +11:38:25 0.36633 +11:38:30 0.36209 +11:38:35 0.34749 +11:38:40 0.33123 +11:38:45 0.32494 +11:38:50 0.31280 +11:38:55 0.30351 +11:39:00 0.30360 +11:39:05 0.29811 +11:39:10 0.28625 +11:39:15 0.28150 +11:39:20 0.27588 +11:39:25 0.27431 +11:39:30 0.27511 +11:39:35 0.26988 +11:39:40 0.26626 +11:39:45 0.26515 +11:39:50 0.26306 +11:39:55 0.25825 +11:40:00 0.25979 +11:40:05 0.25523 +11:40:10 0.25450 +11:40:15 0.24938 +11:40:20 0.24825 +11:40:25 0.24672 +11:40:30 0.24364 +11:40:35 0.24264 +11:40:40 0.24064 +11:40:45 0.24055 +11:40:50 0.24332 +11:40:55 0.23543 +11:41:00 0.23481 +11:41:05 0.23377 +11:41:10 0.23128 +11:41:15 0.23124 +11:41:20 0.23077 +11:41:25 0.23547 +11:41:30 0.24277 +11:41:35 0.23868 +11:41:40 0.24692 +11:41:45 0.24863 +11:41:50 0.24454 +11:41:55 0.24667 +11:42:00 0.24791 +11:42:05 0.24928 +11:42:10 0.25067 +11:42:15 0.25331 +11:42:20 0.25700 +11:42:25 0.25861 +11:42:30 0.25518 +11:42:35 0.25326 +11:42:40 0.26100 +11:42:45 0.26359 +11:42:50 0.26235 +11:42:55 0.26845 +11:43:00 0.26646 +11:43:05 0.26620 +11:43:10 0.26791 +11:43:15 0.26933 +11:43:20 0.27137 +11:43:25 0.27263 +11:43:30 0.27153 +11:43:35 0.27250 +11:43:40 0.27287 +11:43:45 0.27401 +11:43:50 0.27699 +11:43:55 0.27201 +11:44:00 0.27471 +11:44:05 0.28284 +11:44:10 0.28228 +11:44:15 0.27845 +11:44:20 0.27478 +11:44:25 0.27466 +11:44:30 0.27295 +11:44:35 0.27613 +11:44:40 0.28173 +11:44:45 0.28299 +11:44:50 0.28044 +11:44:55 0.28030 +11:45:00 0.28614 +11:45:05 0.27933 +11:45:10 0.28259 +11:45:15 0.28707 +11:45:20 0.28120 +11:45:25 0.28212 +11:45:30 0.29001 +11:45:35 0.29033 +11:45:40 0.29131 +11:45:45 0.28370 +11:45:50 0.28132 +11:45:55 0.28151 +11:46:00 0.28219 +11:46:05 0.28960 +11:46:10 0.29348 +11:46:15 0.29167 +11:46:20 0.29311 +11:46:25 0.28740 +11:46:30 0.28994 +11:46:35 0.28390 +11:46:40 0.28080 +11:46:45 0.28895 +11:46:50 0.28762 +11:46:55 0.28376 +11:47:00 0.28751 +11:47:05 0.28868 +11:47:10 0.29128 +11:47:15 0.28934 +11:47:20 0.29290 +11:47:25 0.29253 +11:47:30 0.28533 +11:47:35 0.28683 +11:47:40 0.28639 +11:47:45 0.28520 +11:47:50 0.29097 +11:47:55 0.29217 +11:48:00 0.29287 +11:48:05 0.29543 +11:48:10 0.29524 +11:48:15 0.29272 +11:48:20 0.29203 +11:48:25 0.29265 +11:48:30 0.28751 +11:48:35 0.29346 +11:48:40 0.29456 +11:48:45 0.28842 +11:48:50 0.29005 +11:48:55 0.29033 +11:49:00 0.29074 +11:49:05 0.28890 +11:49:10 0.28963 +11:49:15 0.29586 +11:49:20 0.29522 +11:49:25 0.28917 +11:49:30 0.29223 +11:49:35 0.29215 +11:49:40 0.29093 +11:49:45 0.29264 +11:49:50 0.28628 +11:49:55 0.28850 +11:50:00 0.29223 +11:50:05 0.29216 +11:50:10 0.28504 +11:50:15 0.28805 +11:50:20 0.28356 +11:50:25 0.28175 +11:50:30 0.28778 +11:50:35 0.28934 +11:50:40 0.28585 +11:50:45 0.28721 +11:50:50 0.28924 +11:50:55 0.28060 +11:51:00 0.28262 +11:51:05 0.28506 +11:51:10 0.28300 +11:51:15 0.28656 +11:51:20 0.28978 +11:51:25 0.28865 +11:51:30 0.28767 +11:51:35 0.28674 +11:51:40 0.28746 +11:51:45 0.29161 +11:51:50 0.28296 +11:51:55 0.28497 +11:52:00 0.28275 +11:52:05 0.28303 +11:52:10 0.28743 +11:52:15 0.28640 +11:52:20 0.28970 +11:52:25 0.28897 +11:52:30 0.29344 +11:52:35 0.29036 +11:52:40 0.28510 +11:52:45 0.28446 +11:52:50 0.28213 +11:52:55 0.28207 +11:53:00 0.28403 +11:53:05 0.28457 +11:53:10 0.28352 +11:53:15 0.28732 +11:53:20 0.29326 +11:53:25 0.29274 +11:53:30 0.28827 +11:53:35 0.28761 +11:53:40 0.28629 +11:53:45 0.29315 +11:53:50 0.28785 +11:53:55 0.28157 +11:54:00 0.28102 +11:54:05 0.28919 +11:54:10 0.28777 +11:54:15 0.28712 +11:54:20 0.28504 +11:54:25 0.29126 +11:54:30 0.29298 +11:54:35 0.28833 +11:54:40 0.28915 +11:54:45 0.28398 +11:54:50 0.28303 +11:54:55 0.29030 +11:55:00 0.28888 +11:55:05 0.29384 +11:55:10 0.29249 +11:55:15 0.28721 +11:55:20 0.28445 +11:55:25 0.28846 +11:55:30 0.28781 +11:55:35 0.28483 +11:55:40 0.28906 +11:55:45 0.28108 +11:55:50 0.27826 +11:55:55 0.27961 +11:56:00 0.28645 +11:56:05 0.28628 +11:56:10 0.27372 +11:56:15 0.27578 +11:56:20 0.28568 +11:56:25 0.29089 +11:56:30 0.28513 +11:56:35 0.28347 +11:56:40 0.28412 +11:56:45 0.28837 +11:56:50 0.29010 +11:56:55 0.28629 +11:57:00 0.28043 +11:57:05 0.27714 +11:57:10 0.28387 +11:57:15 0.28829 +11:57:20 0.28218 +11:57:25 0.28140 +11:57:30 0.28849 +11:57:35 0.28841 +11:57:40 0.28098 +11:57:45 0.28179 +11:57:50 0.28267 +11:57:55 0.28353 +11:58:00 0.28730 +11:58:05 0.28866 +11:58:10 0.28537 +11:58:15 0.28595 +11:58:20 0.28376 +11:58:25 0.28350 +11:58:30 0.28550 +11:58:35 0.28326 +11:58:40 0.27693 +11:58:45 0.28583 +11:58:50 0.28777 +11:58:55 0.28133 +11:59:00 0.28455 +11:59:05 0.27978 +11:59:10 0.28112 +11:59:15 0.28937 +11:59:20 0.29163 +11:59:25 0.28607 +11:59:30 0.27981 +11:59:35 0.28470 +11:59:40 0.28645 +11:59:45 0.28762 +11:59:50 0.28596 +11:59:55 0.28529 +12:00:00 0.28652 +12:00:05 0.28992 +12:00:10 0.28573 +12:00:15 0.28059 +12:00:20 0.27977 +12:00:25 0.28098 +12:00:30 0.28549 +12:00:35 0.28617 +12:00:40 0.28493 +12:00:45 0.28359 +12:00:58 0.27947 +12:01:00 0.28060 +12:01:05 0.27632 +12:01:10 0.28396 +12:01:15 0.28060 +12:01:20 0.28801 +12:01:25 0.28313 +12:01:30 0.28327 +12:01:35 0.27912 +12:01:40 0.28490 +12:01:45 0.28252 +12:01:50 0.27599 +12:01:55 0.27515 +12:02:00 0.27723 +12:02:05 0.28290 +12:02:10 0.28261 +12:02:15 0.28090 +12:02:20 0.28051 +12:02:25 0.28137 +12:02:30 0.28021 +12:02:35 0.28208 +12:02:40 0.28686 +12:02:45 0.28068 +12:02:50 0.28100 +12:02:55 0.27616 +12:03:00 0.28653 +12:03:05 0.28356 +12:03:10 0.28128 +12:03:15 0.27708 +12:03:20 0.28264 +12:03:25 0.28207 +12:03:30 0.28218 +12:03:35 0.28667 +12:03:40 0.28311 +12:03:45 0.27664 +12:03:50 0.28180 +12:03:55 0.28178 +12:04:00 0.27786 +12:04:05 0.27898 +12:04:10 0.27972 +12:04:15 0.28501 +12:04:20 0.28061 +12:04:25 0.28874 +12:04:30 0.28868 +12:04:35 0.28539 +12:04:40 0.28550 +12:04:45 0.28114 +12:04:50 0.29205 +12:04:55 0.28986 +12:05:00 0.28379 +12:05:05 0.28717 +12:05:10 0.28431 +12:05:15 0.28524 +12:05:20 0.28247 +12:05:25 0.28585 +12:05:30 0.28957 +12:05:35 0.28666 +12:05:40 0.28035 +12:05:45 0.28286 +12:05:50 0.28679 +12:05:55 0.28496 +12:06:00 0.28120 +12:06:05 0.27804 +12:06:10 0.27675 +12:06:15 0.28127 +12:06:20 0.28136 +12:06:25 0.27526 +12:06:30 0.27554 +12:06:35 0.27410 +12:06:40 0.27554 +12:06:45 0.28015 +12:06:50 0.28421 +12:06:55 0.28968 +12:07:00 0.28335 +12:07:05 0.27568 +12:07:10 0.27486 +12:07:15 0.27840 +12:07:20 0.28633 +12:07:25 0.29157 +12:07:30 0.28866 +12:07:35 0.28296 +12:07:40 0.27713 +12:07:45 0.27529 +12:07:50 0.27422 +12:07:55 0.27648 +12:08:00 0.27666 +12:08:05 0.28298 +12:08:10 0.28214 +12:08:15 0.28234 +12:08:20 0.27996 +12:08:25 0.28431 +12:08:30 0.28672 +12:08:35 0.28481 +12:08:40 0.28366 +12:08:45 0.29053 +12:08:50 0.28212 +12:08:55 0.27817 +12:09:00 0.28424 +12:09:05 0.28097 +12:09:10 0.28493 +12:09:15 0.28187 +12:09:20 0.28381 +12:09:25 0.27760 +12:09:30 0.28174 +12:09:35 0.28708 +12:09:40 0.28282 +12:09:45 0.28350 +12:09:50 0.27818 +12:09:55 0.27843 +12:10:00 0.28212 +12:10:05 0.27964 +12:10:10 0.27637 +12:10:15 0.27943 +12:10:20 0.28054 +12:10:25 0.28487 +12:10:30 0.28663 +12:10:35 0.28540 +12:10:40 0.28336 +12:10:45 0.28469 +12:10:50 0.28395 +12:10:55 0.28488 +12:11:00 0.28385 +12:11:05 0.27723 +12:11:10 0.27843 +12:11:15 0.27950 +12:11:20 0.27552 +12:11:25 0.28300 +12:11:30 0.28948 +12:11:35 0.28157 +12:11:40 0.27650 +12:11:45 0.27835 +12:11:50 0.27656 +12:11:56 0.28258 +12:12:00 0.28731 +12:12:05 0.28385 +12:12:10 0.27960 +12:12:15 0.27315 +12:12:20 0.27115 +12:12:25 0.26546 +12:12:30 0.25897 +12:12:35 0.25771 +12:12:40 0.25365 +12:12:45 0.25193 +12:12:50 0.24619 +12:12:55 0.24197 +12:13:00 0.23827 +12:13:05 0.23745 +12:13:10 0.23425 +12:13:15 0.23232 +12:13:20 0.23308 +12:13:25 0.22913 +12:13:30 0.22556 +12:13:35 0.22570 +12:13:40 0.22582 +12:13:45 0.22558 +12:13:50 0.22294 +12:13:55 0.22043 +12:14:00 0.21928 +12:14:05 0.21625 +12:14:10 0.21417 +12:14:15 0.21048 +12:14:20 0.20958 +12:14:25 0.21040 +12:14:30 0.21001 +12:14:35 0.21015 +12:14:40 0.20922 +12:14:45 0.20292 +12:14:50 0.20317 +12:14:55 0.20195 +12:15:00 0.20223 +12:15:05 0.20020 +12:15:10 0.20124 +12:15:15 0.20379 +12:15:20 0.20381 +12:15:25 0.20113 +12:15:30 0.19836 +12:15:35 0.19736 +12:15:40 0.19562 +12:15:45 0.19593 +12:15:50 0.19735 +12:15:55 0.19302 +12:16:00 0.19330 +12:16:12 0.19388 +12:16:15 0.19313 +12:16:20 0.19340 +12:16:25 0.19243 +12:16:30 0.19154 +12:16:35 0.19109 +12:16:40 0.19096 +12:16:45 0.19029 +12:16:50 0.18966 +12:16:55 0.18957 +12:17:00 0.18759 +12:17:05 0.18749 +12:17:10 0.18655 +12:17:15 0.18451 +12:17:20 0.18272 +12:17:25 0.18386 +12:17:30 0.18335 +12:17:35 0.18347 +12:17:40 0.18327 +12:17:45 0.18279 +12:17:50 0.18208 +12:17:55 0.18080 +12:18:00 0.17834 +12:18:05 0.17804 +12:18:10 0.18084 +12:18:15 0.18084 +12:18:20 0.17781 +12:18:25 0.17810 +12:18:30 0.18083 +12:18:35 0.17748 +12:18:40 0.17834 +12:18:45 0.17727 +12:18:50 0.17616 +12:18:55 0.17637 +12:19:00 0.17572 +12:19:05 0.17637 +12:19:10 0.17446 +12:19:15 0.17425 +12:19:20 0.17554 +12:19:25 0.17587 +12:19:30 0.17406 +12:19:35 0.17334 +12:19:40 0.17426 +12:19:45 0.17520 +12:19:50 0.17546 +12:19:55 0.17407 +12:20:00 0.17176 +12:20:05 0.17265 +12:20:10 0.17367 +12:20:15 0.17416 +12:20:20 0.17396 +12:20:25 0.17122 +12:20:30 0.17141 +12:20:35 0.17216 +12:20:40 0.17265 +12:20:45 0.17193 +12:20:50 0.17296 +12:20:55 0.17496 +12:21:00 0.17498 +12:21:05 0.17169 +12:21:10 0.17248 +12:21:15 0.17191 +12:21:20 0.17071 +12:21:25 0.17148 +12:21:30 0.17218 +12:21:35 0.17191 +12:21:40 0.17350 +12:21:45 0.17271 +12:21:50 0.17143 +12:21:55 0.16946 +12:22:00 0.16722 +12:22:05 0.17004 +12:22:10 0.16908 +12:22:15 0.17020 +12:22:20 0.17186 +12:22:25 0.16888 +12:22:30 0.16917 +12:22:35 0.16968 +12:22:40 0.17129 +12:22:45 0.17212 +12:22:50 0.17214 +12:22:55 0.17007 +12:23:00 0.16836 +12:23:05 0.16840 +12:23:10 0.16907 +12:23:15 0.16930 +12:23:20 0.17108 +12:23:25 0.17082 +12:23:30 0.17012 +12:23:35 0.16980 +12:23:40 0.16841 +12:23:45 0.16726 +12:23:50 0.16810 +12:23:55 0.16807 +12:24:00 0.16786 +12:24:05 0.16824 +12:24:10 0.17097 +12:24:15 0.16806 +12:24:20 0.16672 +12:24:25 0.16929 +12:24:30 0.17059 +12:24:35 0.16944 +12:24:40 0.16668 +12:24:45 0.16582 +12:24:50 0.16586 +12:24:55 0.16804 +12:25:00 0.16883 +12:25:05 0.16870 +12:25:10 0.16860 +12:25:15 0.16684 +12:25:20 0.16697 +12:25:25 0.16794 +12:25:30 0.16783 +12:25:35 0.16733 +12:25:40 0.16689 +12:25:45 0.16924 +12:25:50 0.16883 +12:25:55 0.16911 +12:26:00 0.17011 +12:26:05 0.16709 +12:26:10 0.16440 +12:26:15 0.16596 +12:26:20 0.16702 +12:26:25 0.16762 +12:26:30 0.16814 +12:26:35 0.16771 +12:26:40 0.16758 +12:26:45 0.16637 +12:26:50 0.16649 +12:26:55 0.16576 +12:27:00 0.16567 +12:27:05 0.16692 +12:27:10 0.16817 +12:27:15 0.16816 +12:27:20 0.16906 +12:27:25 0.16941 +12:27:30 0.16469 +12:27:35 0.16572 +12:27:40 0.16886 +12:27:45 0.16713 +12:27:50 0.16799 +12:27:55 0.16760 +12:28:00 0.16586 +12:28:05 0.16565 +12:28:10 0.16531 +12:28:15 0.16712 +12:28:20 0.16831 +12:28:25 0.16740 +12:28:30 0.16653 +12:28:35 0.16407 +12:28:40 0.16696 +12:28:45 0.16742 +12:28:50 0.16743 +12:28:55 0.16777 +12:29:00 0.16637 +12:29:05 0.16657 +12:29:10 0.16613 +12:29:15 0.16702 +12:29:20 0.16690 +12:29:25 0.16756 +12:29:30 0.16612 +12:29:35 0.16630 +12:29:40 0.16498 +12:29:45 0.16655 +12:29:50 0.16509 +12:29:55 0.16424 +12:30:00 0.16584 +12:30:05 0.16422 +12:30:10 0.16488 +12:30:15 0.16468 +12:30:20 0.16543 +12:30:25 0.16447 +12:30:30 0.16706 +12:30:35 0.16532 +12:30:40 0.16521 +12:30:45 0.16788 +12:30:50 0.16791 +12:30:55 0.16719 +12:31:00 0.16615 +12:31:05 0.16737 +12:31:10 0.16957 +12:31:15 0.16640 +12:31:20 0.16549 +12:31:25 0.16514 +12:31:30 0.16700 +12:31:35 0.16567 +12:31:40 0.16594 +12:31:45 0.16675 +12:31:50 0.16676 +12:31:55 0.16810 +12:32:00 0.16789 +12:32:05 0.16683 +12:32:10 0.16825 +12:32:15 0.16701 +12:32:20 0.16531 +12:32:25 0.16763 +12:32:30 0.16915 +12:32:35 0.16811 +12:32:40 0.16758 +12:32:45 0.16837 +12:32:50 0.16728 +12:32:55 0.16736 +12:33:00 0.16781 +12:33:05 0.16719 +12:33:10 0.16745 +12:33:15 0.16911 +12:33:20 0.16840 +12:33:25 0.16591 +12:33:30 0.16562 +12:33:35 0.16626 +12:33:40 0.16697 +12:33:45 0.16656 +12:33:50 0.16501 +12:33:55 0.16548 +12:34:00 0.16733 +12:34:05 0.16640 +12:34:10 0.16576 +12:34:15 0.16563 +12:34:20 0.16349 +12:34:25 0.16511 +12:34:30 0.16449 +12:34:35 0.16445 +12:34:40 0.16785 +12:34:45 0.16735 +12:34:50 0.16633 +12:34:55 0.16691 +12:35:00 0.16678 +12:35:05 0.16664 +12:35:10 0.16561 +12:35:15 0.16550 +12:35:20 0.16545 +12:35:25 0.16554 +12:35:30 0.16576 +12:35:35 0.16480 +12:35:40 0.16581 +12:35:45 0.16489 +12:35:50 0.16631 +12:35:55 0.16682 +12:36:00 0.16606 +12:36:05 0.16608 +12:36:10 0.16748 +12:36:15 0.16726 +12:36:20 0.16726 +12:36:25 0.16763 +12:36:30 0.16650 +12:36:35 0.16437 +12:36:40 0.16410 +12:36:45 0.16233 +12:36:50 0.16389 +12:36:55 0.16657 +12:37:00 0.16791 +12:37:05 0.16757 +12:37:10 0.16549 +12:37:15 0.16536 +12:37:20 0.16706 +12:37:25 0.16711 +12:37:30 0.16784 +12:37:35 0.16858 +12:37:40 0.16604 +12:37:45 0.16487 +12:37:50 0.16393 +12:37:55 0.16650 +12:38:00 0.16613 +12:38:05 0.16659 +12:38:10 0.16630 +12:38:15 0.16691 +12:38:20 0.16714 +12:38:25 0.16875 +12:38:30 0.16663 +12:38:35 0.16463 +12:38:40 0.16635 +12:38:45 0.16470 +12:38:50 0.16550 +12:38:55 0.16756 +12:39:00 0.16735 +12:39:05 0.16743 +12:39:10 0.16630 +12:39:15 0.16601 +12:39:20 0.16742 +12:39:25 0.16488 +12:39:30 0.16529 +12:39:35 0.16502 +12:39:40 0.16419 +12:39:45 0.16576 +12:39:50 0.16612 +12:39:55 0.16614 +12:40:00 0.16651 +12:40:05 0.16622 +12:40:10 0.16518 +12:40:15 0.16642 +12:40:20 0.16724 +12:40:25 0.16888 +12:40:30 0.17002 +12:40:35 0.16705 +12:40:40 0.16443 +12:40:45 0.16496 +12:40:50 0.16588 +12:41:03 0.16597 +12:41:05 0.16633 +12:41:10 0.16829 +12:41:15 0.16658 +12:41:20 0.16543 +12:41:25 0.16479 +12:41:30 0.16722 +12:41:35 0.16915 +12:41:40 0.17190 +12:41:45 0.17187 +12:41:50 0.17233 +12:41:55 0.17193 +12:42:00 0.17260 +12:42:05 0.17296 +12:42:10 0.17336 +12:42:15 0.17228 +12:42:20 0.17204 +12:42:25 0.17205 +12:42:30 0.17356 +12:42:35 0.17191 +12:42:40 0.17303 +12:42:45 0.17523 +12:42:50 0.17202 +12:42:55 0.17201 +12:43:00 0.17191 +12:43:05 0.17225 +12:43:10 0.17215 +12:43:15 0.17165 +12:43:20 0.17106 +12:43:25 0.17173 +12:43:30 0.17116 +12:43:35 0.16903 +12:43:40 0.16805 +12:43:45 0.17000 +12:43:50 0.17148 +12:43:55 0.17189 +12:44:00 0.17325 +12:44:05 0.17133 +12:44:10 0.17066 +12:44:15 0.17095 +12:44:20 0.17147 +12:44:25 0.17083 +12:44:30 0.17106 +12:44:35 0.17198 +12:44:40 0.17020 +12:44:45 0.16957 +12:44:50 0.16958 +12:44:55 0.17149 +12:45:00 0.17087 +12:45:05 0.16999 +12:45:10 0.17000 +12:45:15 0.16946 +12:45:20 0.16974 +12:45:25 0.16970 +12:45:30 0.17029 +12:45:35 0.17129 +12:45:40 0.17081 +12:45:45 0.16911 +12:45:50 0.17047 +12:45:55 0.16942 +12:46:00 0.17009 +12:46:05 0.17074 +12:46:10 0.16971 +12:46:15 0.16926 +12:46:20 0.16956 +12:46:25 0.16780 +12:46:30 0.16769 +12:46:35 0.16937 +12:46:40 0.17070 +12:46:45 0.17150 +12:46:50 0.17022 +12:46:55 0.17115 +12:47:00 0.17010 +12:47:05 0.16870 +12:47:10 0.16951 +12:47:15 0.17081 +12:47:20 0.17127 +12:47:25 0.17136 +12:47:30 0.17005 +12:47:35 0.17012 +12:47:40 0.16997 +12:47:45 0.17256 +12:47:50 0.17229 +12:47:55 0.17022 +12:48:00 0.16860 +12:48:05 0.16884 +12:48:10 0.16972 +12:48:15 0.17053 +12:48:20 0.17089 +12:48:25 0.16936 +12:48:30 0.17126 +12:48:35 0.17293 +12:48:40 0.17029 +12:48:45 0.16701 +12:48:50 0.16833 +12:48:55 0.17218 +12:49:00 0.17212 +12:49:05 0.17145 +12:49:10 0.17046 +12:49:15 0.16905 +12:49:20 0.16949 +12:49:25 0.17103 +12:49:30 0.17171 +12:49:35 0.17154 +12:49:40 0.17064 +12:49:45 0.16880 +12:49:50 0.16894 +12:49:55 0.17009 +12:50:00 0.16962 +12:50:05 0.17074 +12:50:10 0.16984 +12:50:15 0.16755 +12:50:20 0.16833 +12:50:25 0.16859 +12:50:30 0.16935 +12:50:35 0.16855 +12:50:40 0.17144 +12:50:45 0.17259 +12:50:50 0.17259 +12:50:55 0.16946 +12:51:00 0.16941 +12:51:05 0.17033 +12:51:10 0.16934 +12:51:15 0.16906 +12:51:20 0.17040 +12:51:25 0.17123 +12:51:30 0.16983 +12:51:35 0.16774 +12:51:40 0.16944 +12:51:45 0.16716 +12:51:50 0.16651 +12:51:55 0.16765 +12:52:00 0.16863 +12:52:05 0.17000 +12:52:10 0.16979 +12:52:15 0.17139 +12:52:20 0.17045 +12:52:25 0.16960 +12:52:30 0.17219 +12:52:35 0.17328 +12:52:40 0.17109 +12:52:45 0.17001 +12:52:50 0.16912 +12:52:55 0.16970 +12:53:00 0.17020 +12:53:05 0.17018 +12:53:10 0.17023 +12:53:15 0.16906 +12:53:20 0.16994 +12:53:25 0.16909 +12:53:30 0.16992 +12:53:35 0.16858 +12:53:40 0.16923 +12:53:45 0.17082 +12:53:50 0.17090 +12:53:55 0.17100 +12:54:00 0.17180 +12:54:05 0.17271 +12:54:10 0.17214 +12:54:15 0.17190 +12:54:20 0.17035 +12:54:25 0.16961 +12:54:30 0.17157 +12:54:35 0.17314 +12:54:40 0.17135 +12:54:45 0.17098 +12:54:50 0.17007 +12:54:55 0.17098 +12:55:00 0.16912 +12:55:05 0.16869 +12:55:10 0.16809 +12:55:15 0.16840 +12:55:20 0.17040 +12:55:25 0.17081 +12:55:30 0.17120 +12:55:35 0.16993 +12:55:40 0.17137 +12:55:45 0.17048 +12:55:50 0.17036 +12:55:55 0.17057 +12:56:00 0.17067 +12:56:05 0.17005 +12:56:10 0.17242 +12:56:15 0.16972 +12:56:20 0.17101 +12:56:25 0.17157 +12:56:30 0.17031 +12:56:35 0.16906 +12:56:40 0.16921 +12:56:45 0.16922 +12:56:50 0.17215 +12:56:55 0.17062 +12:57:00 0.17167 +12:57:06 0.17293 +12:57:10 0.17011 +12:57:15 0.16947 +12:57:20 0.16934 +12:57:25 0.17120 +12:57:30 0.17013 +12:57:35 0.16917 +12:57:40 0.17000 +12:57:45 0.17029 +12:57:50 0.16919 +12:57:55 0.16805 +12:58:00 0.16869 +12:58:05 0.17253 +12:58:10 0.17194 +12:58:15 0.17042 +12:58:20 0.16863 +12:58:25 0.17030 +12:58:30 0.17248 +12:58:35 0.17002 +12:58:40 0.16747 +12:58:45 0.17191 +12:58:50 0.17105 +12:58:55 0.17030 +12:59:00 0.17243 +12:59:05 0.16989 +12:59:10 0.17047 +12:59:15 0.17127 +12:59:20 0.17053 +12:59:25 0.17100 +12:59:30 0.17137 +12:59:35 0.17005 +12:59:40 0.17153 +12:59:45 0.17100 +12:59:50 0.16898 +12:59:55 0.16877 +13:00:00 0.16969 +13:00:05 0.17042 +13:00:10 0.17211 +13:00:15 0.17025 +13:00:20 0.16993 +13:00:25 0.16917 +13:00:30 0.16963 +13:00:35 0.17077 +13:00:40 0.17096 +13:00:45 0.16990 +13:00:50 0.16958 +13:00:55 0.17004 +13:01:00 0.17113 +13:01:05 0.17006 +13:01:10 0.16838 +13:01:15 0.16798 +13:01:20 0.17018 +13:01:25 0.17075 +13:01:36 0.17041 +13:01:40 0.16961 +13:01:45 0.17040 +13:01:50 0.17035 +13:01:55 0.17111 +13:02:00 0.16994 +13:02:05 0.16971 +13:02:10 0.16919 +13:02:15 0.16985 +13:02:20 0.17191 +13:02:25 0.17140 +#period 5 exact 1 isdst 1 +13:02:30 +13:02:32 undefined +13:02:35 0.16961 +13:02:40 0.16945 +13:02:45 0.16898 +13:02:50 0.17194 +13:02:55 0.17203 +13:03:00 0.17090 +13:03:05 0.16941 +13:03:10 0.16938 +13:03:15 0.17127 +13:03:20 0.16868 +13:03:25 0.16685 +13:03:30 0.17044 +13:03:35 0.17033 +13:03:40 0.16883 +13:03:45 0.16870 +13:03:50 0.16834 +13:03:55 0.16866 +13:04:00 0.16796 +13:04:05 0.16947 +13:04:10 0.16968 +13:04:15 0.16833 +13:04:20 0.16826 +13:04:25 0.16863 +13:04:30 0.16905 +13:04:35 0.16720 +13:04:40 0.16625 +13:04:45 0.16721 +13:04:50 0.16612 +13:04:55 0.16629 +13:05:00 0.16907 +13:05:05 0.16946 +13:05:10 0.16713 +13:05:15 0.16851 +13:05:20 0.16901 +13:05:25 0.16770 +13:05:30 0.16922 +13:05:35 0.16773 +13:05:40 0.16787 +13:06:01 0.16824 +13:06:05 0.16749 +13:06:33 0.16764 +13:06:35 0.16850 +13:06:40 0.16905 +13:06:45 0.16828 +13:06:50 0.16694 +13:06:55 0.16857 +13:07:00 0.16601 +#period 5 exact 1 isdst 1 +13:07:06 +13:07:08 undefined +13:07:10 0.17018 +13:07:15 0.16628 +13:07:20 0.16709 +13:07:25 0.16646 +13:07:30 0.16505 +13:07:35 0.16710 +13:07:40 0.16544 +13:07:45 0.16825 +13:07:50 0.17227 +13:07:55 0.17411 +13:08:00 0.17251 +13:08:05 0.17249 +13:08:10 0.17160 +13:08:15 0.17400 +13:08:20 0.17340 +13:08:25 0.17109 +13:08:30 0.17206 +13:08:35 0.17242 +13:08:40 0.16953 +13:08:45 0.17057 +13:08:50 0.17080 +13:08:55 0.16985 +13:09:00 0.17148 +13:09:05 0.17258 +13:09:10 0.17202 +13:09:15 0.17125 +13:09:20 0.17098 +13:09:25 0.17167 +13:09:30 0.17156 +13:09:35 0.16973 +13:09:40 0.17168 +13:09:45 0.17064 +13:09:50 0.16770 +13:09:55 0.16862 +13:10:00 0.16948 +13:10:05 0.16830 +13:10:10 0.17019 +13:10:15 0.17189 +13:10:20 0.17232 +13:10:25 0.17118 +13:10:30 0.17022 +13:10:35 0.16962 +13:10:40 0.16996 +13:10:45 0.17309 +13:10:50 0.17238 +13:10:55 0.17085 +13:11:00 0.17038 +13:11:05 0.17081 +13:11:10 0.17179 +13:11:15 0.17083 +13:11:20 0.17061 +13:11:25 0.16986 +13:11:30 0.17154 +13:11:36 0.17122 +13:11:40 0.17242 +13:11:45 0.17146 +13:11:52 0.17084 +13:11:55 0.16807 +13:12:00 0.16878 +13:12:05 0.16965 +13:12:10 0.17142 +13:12:15 0.17043 +13:12:20 0.17007 +13:12:25 0.17030 +13:12:30 0.16939 +13:12:35 0.17003 +13:12:40 0.17069 +13:12:45 0.16971 +13:12:50 0.17087 +13:12:55 0.16932 +13:13:00 0.16974 +13:13:05 0.17009 +13:13:10 0.17117 +13:13:15 0.17025 +13:13:20 0.17112 +13:13:25 0.16892 +13:13:30 0.17046 +13:13:35 0.16943 +13:13:40 0.16908 +13:13:45 0.16970 +13:13:50 0.16991 +13:13:55 0.17048 +13:14:00 0.16939 +13:14:05 0.16937 +13:14:10 0.16822 +13:14:15 0.16776 +13:14:20 0.16925 +13:14:25 0.16878 +13:14:30 0.16868 +13:14:35 0.16683 +13:14:40 0.16860 +13:14:45 0.16993 +13:14:50 0.16916 +13:14:55 0.16858 +13:15:00 0.17018 +13:15:05 0.17160 +13:15:10 0.17176 +13:15:15 0.16945 +13:15:20 0.17059 +13:15:25 0.17194 +13:15:30 0.17228 +13:15:35 0.17107 +13:15:40 0.16990 +13:15:45 0.16804 +13:15:50 0.16878 +13:15:55 0.16936 +13:16:00 0.16923 +13:16:05 0.17072 +13:16:10 0.17079 +13:16:15 0.16991 +13:16:20 0.17098 +13:16:25 0.17077 +13:16:30 0.16931 +13:16:35 0.17011 +13:16:40 0.17093 +13:16:45 0.16852 +13:16:50 0.16685 +13:16:55 0.17080 +13:17:00 0.17003 +13:17:05 0.16824 +13:17:10 0.17036 +13:17:15 0.17121 +13:17:20 0.16901 +13:17:25 0.16920 +13:17:30 0.17034 +13:17:35 0.16925 +13:17:40 0.16954 +13:17:45 0.16979 +13:17:50 0.16842 +13:17:55 0.16913 +13:18:00 0.17033 +13:18:05 0.16942 +13:18:10 0.16927 +13:18:15 0.16965 +13:18:20 0.17072 +13:18:25 0.17081 +13:18:30 0.16878 +13:18:35 0.16849 +13:18:40 0.16906 +13:18:45 0.16978 +13:18:50 0.17065 +13:18:55 0.16952 +13:19:00 0.17285 +13:19:05 0.17239 +13:19:10 0.16909 +13:19:15 0.16886 +13:19:20 0.16987 +13:19:25 0.17015 +13:19:30 0.16913 +13:19:35 0.16951 +13:19:40 0.16887 +13:19:45 0.16814 +13:19:50 0.16991 +13:19:55 0.16936 +13:20:00 0.17138 +13:20:05 0.16963 +13:20:10 0.16979 +13:20:15 0.16874 +13:20:20 0.17020 +13:20:25 0.16983 +13:20:30 0.17088 +13:20:35 0.17070 +13:20:40 0.17009 +13:20:45 0.16879 +13:20:50 0.16755 +13:20:55 0.16779 +13:21:07 0.16919 +13:21:10 0.17003 +13:21:15 0.16891 +13:21:25 0.17083 +13:21:30 0.17019 +13:21:39 0.16888 +13:21:40 0.16948 +13:21:46 0.17054 +13:21:50 0.16989 +13:21:55 0.16745 +13:22:03 0.16879 +13:22:05 0.16966 +13:22:10 0.16923 +13:22:17 0.17088 +13:22:20 0.16836 +13:22:25 0.16805 +13:22:30 0.16837 +13:22:35 0.16677 +13:22:40 0.16803 +13:22:45 0.17088 +13:22:50 0.17014 +13:22:55 0.16876 +13:23:01 0.16877 +13:23:05 0.16957 +13:23:10 0.17060 +13:23:15 0.16844 +13:23:20 0.16930 +13:23:25 0.16815 +13:23:30 0.16645 +13:23:35 0.16850 +13:23:40 0.16830 +13:23:45 0.16912 +13:23:50 0.16890 +13:23:55 0.17178 +13:24:00 0.17248 +13:24:05 0.17216 +13:24:10 0.17213 +13:24:15 0.17670 +13:26:55 0.17670 +13:27:00 0.17698 +13:27:05 0.17711 +13:27:10 0.17738 +13:27:15 0.17884 +13:27:23 0.18748 +13:27:25 0.18717 +13:27:30 0.18209 +13:27:35 0.17551 +13:27:40 0.17145 +13:27:45 0.16991 +13:27:52 0.16461 +13:27:55 0.16565 +13:28:00 0.16950 +13:28:05 0.16527 +13:28:10 0.16973 +13:28:15 0.16447 +13:28:20 0.16096 +13:28:25 0.16224 +13:28:30 0.16901 +13:28:35 0.16590 +13:28:40 0.15760 +13:28:45 0.15738 +13:28:50 0.15536 +13:29:00 0.15971 +13:29:05 0.15667 +13:29:10 0.15590 +13:29:15 0.16156 +13:29:20 0.16147 +13:29:25 0.15904 +13:29:30 0.16192 +13:29:35 0.16236 +13:29:40 0.15791 +13:29:45 0.16440 +13:29:50 0.16307 +13:29:55 0.16639 +13:30:00 0.15786 +13:30:05 0.15923 +13:30:10 0.15471 +13:30:15 0.15761 +13:30:20 0.15708 +13:30:25 0.15905 +13:30:30 0.16344 +13:30:36 0.15675 +13:30:40 0.15264 +13:30:47 0.15270 +13:30:50 0.15478 +13:30:55 0.16107 +13:31:00 0.16781 +13:31:05 0.16402 +13:31:10 0.15810 +13:31:15 0.15550 +13:31:20 0.15648 +13:31:25 0.16440 +13:31:30 0.15813 +13:31:35 0.16189 +13:31:40 0.15355 +13:31:45 0.15782 +13:31:50 0.15822 +13:31:55 0.15695 +13:32:00 0.15562 +13:32:05 0.15600 +13:32:10 0.15832 +13:32:15 0.16088 +13:32:20 0.15847 +13:32:25 0.15991 +13:32:30 0.15635 +13:32:35 0.16079 +13:32:40 0.16014 +13:32:45 0.15929 +13:32:50 0.15751 +13:32:55 0.15782 +13:33:00 0.15591 +13:33:05 0.15729 +13:33:10 0.15574 +13:33:15 0.16319 +13:33:20 0.15527 +13:33:25 0.16070 +13:33:30 0.16396 +13:33:35 0.15524 +13:33:40 0.15581 +13:33:45 0.15331 +13:33:50 0.15960 +13:33:55 0.15923 +13:34:00 0.15694 +13:34:05 0.15627 +13:34:10 0.16303 +13:34:15 0.16244 +13:34:20 0.16105 +13:34:25 0.16186 +13:34:30 0.15468 +13:34:35 0.16119 +13:34:40 0.15480 +13:34:45 0.16179 +13:34:50 0.16174 +13:34:55 0.16252 +13:35:00 0.15568 +13:35:05 0.15771 +13:35:10 0.15818 +13:35:15 0.15813 +13:35:20 0.15601 +13:35:25 0.15565 +13:35:30 0.15791 +13:35:35 0.15855 +13:35:40 0.16231 +13:35:45 0.15308 +13:35:50 0.16210 +13:35:55 0.15676 +13:36:00 0.15505 +13:36:05 0.15574 +13:36:10 0.15444 +13:36:15 0.15714 +13:36:20 0.15427 +13:36:29 0.15533 +13:36:30 0.15823 +13:36:35 0.15654 +13:36:40 0.15967 +13:36:45 0.16103 +13:36:50 0.16069 +13:36:55 0.16245 +13:37:00 0.15846 +13:37:05 0.15410 +13:37:10 0.15790 +13:37:15 0.16438 +13:37:20 0.15626 +13:37:25 0.16028 +13:37:30 0.15657 +13:37:35 0.15854 +13:37:40 0.15441 +13:37:45 0.16417 +13:37:50 0.16192 +13:37:55 0.16755 +13:38:00 0.15580 +13:38:05 0.15580 +13:38:10 0.15563 +13:38:15 0.15520 +13:38:20 0.15540 +13:38:25 0.15348 +13:38:30 0.15579 +13:38:35 0.15645 +13:38:40 0.15767 +13:38:45 0.15707 +13:38:50 0.15301 +13:38:55 0.15804 +13:39:00 0.16147 +13:39:05 0.15843 +13:39:10 0.15897 +13:39:15 0.15840 +13:39:20 0.15366 +13:39:25 0.15438 +13:39:30 0.15983 +13:39:35 0.15944 +13:39:40 0.15479 +13:39:45 0.16169 +13:39:50 0.15395 +13:39:55 0.15564 +13:40:00 0.15225 +13:40:05 0.15655 +13:40:10 0.15721 +13:40:15 0.15065 +13:40:20 0.15673 +13:40:25 0.14899 +13:40:30 0.15467 +13:40:35 0.15818 +13:40:40 0.16559 +13:40:45 0.15694 +13:40:50 0.15839 +13:40:55 0.16370 +13:41:00 0.16477 +13:41:05 0.15744 +13:41:10 0.15403 +13:41:15 0.15584 +13:41:20 0.16130 +13:41:25 0.15488 +13:41:30 0.15879 +13:41:35 0.16157 +13:41:40 0.15426 +13:41:45 0.15830 +13:41:50 0.16092 +13:41:55 0.16017 +13:42:00 0.15878 +13:42:05 0.15673 +13:42:10 0.15612 +13:42:15 0.16204 +13:42:20 0.15767 +13:42:25 0.15518 +13:42:30 0.15441 +13:42:35 0.15466 +13:42:40 0.15947 +13:42:45 0.15578 +13:42:50 0.15308 +13:42:55 0.15618 +13:43:00 0.15858 +13:43:05 0.15642 +13:43:10 0.15540 +13:43:15 0.15818 +13:43:20 0.16102 +13:43:25 0.16497 +13:43:30 0.15631 +13:43:35 0.15772 +13:43:40 0.16198 +13:43:45 0.16265 +13:43:50 0.15636 +13:43:55 0.15847 +13:44:00 0.15487 +13:44:05 0.15723 +13:44:10 0.15401 +13:44:15 0.15416 +13:44:20 0.15882 +13:44:25 0.15994 +13:44:30 0.16299 +13:44:35 0.15974 +13:44:40 0.15967 +13:44:45 0.16412 +13:44:50 0.15464 +13:44:55 0.15609 +13:45:00 0.15822 +13:45:05 0.15684 +13:45:10 0.15404 +13:45:15 0.15192 +13:45:20 0.15077 +13:45:25 0.16113 +13:45:30 0.15858 +13:45:35 0.15885 +13:45:40 0.15769 +13:45:45 0.15259 +13:45:50 0.15496 +13:45:55 0.15775 +13:46:00 0.15885 +13:46:05 0.15881 +13:46:10 0.15537 +13:46:15 0.15495 +13:46:20 0.15336 +13:46:25 0.15300 +13:46:30 0.16676 +13:46:35 0.15952 +13:46:40 0.15519 +13:46:45 0.15425 +13:46:50 0.15140 +13:46:55 0.15244 +13:47:00 0.15164 +13:47:05 0.15577 +13:47:10 0.16006 +13:47:15 0.15516 +13:47:20 0.15368 +13:47:25 0.15887 +13:47:30 0.15966 +13:47:35 0.15314 +13:47:40 0.15636 +13:47:45 0.15515 +13:47:50 0.15745 +13:47:55 0.15327 +13:48:00 0.15312 +13:48:05 0.15372 +13:48:10 0.15587 +13:48:15 0.16069 +13:48:20 0.15856 +13:48:25 0.16313 +13:48:30 0.16347 +13:48:35 0.15901 +13:48:40 0.15241 +13:48:45 0.15674 +13:48:50 0.16314 +13:48:55 0.16475 +13:49:00 0.15921 +13:49:05 0.15758 +13:49:10 0.15477 +13:49:15 0.15122 +13:49:20 0.15348 +13:49:25 0.15055 +13:49:30 0.15150 +13:49:35 0.14807 +13:49:40 0.15193 +13:49:45 0.15561 +13:49:50 0.15405 +13:49:55 0.15491 +13:50:00 0.15479 +13:50:05 0.15308 +13:50:10 0.15616 +13:50:15 0.15880 +13:50:20 0.15403 +13:50:25 0.15545 +13:50:30 0.16036 +13:50:35 0.15506 +13:50:40 0.15406 +13:50:45 0.15230 +13:50:50 0.15301 +13:50:55 0.15608 +13:51:00 0.16314 +13:51:05 0.16482 +13:51:10 0.15504 +13:51:15 0.14897 +13:51:20 0.15338 +13:51:25 0.16191 +13:51:30 0.15958 +13:51:35 0.15498 +13:51:40 0.15679 +13:51:45 0.16010 +13:51:50 0.16052 +13:51:55 0.16214 +13:52:00 0.15889 +13:52:05 0.15303 +13:52:10 0.15368 +13:52:15 0.15814 +13:52:20 0.15845 +13:52:25 0.15826 +13:52:30 0.15815 +13:52:35 0.15857 +13:52:40 0.15942 +13:52:45 0.16220 +13:52:50 0.16362 +13:52:55 0.15838 +13:53:00 0.15771 +13:53:05 0.15590 +13:53:10 0.15878 +13:53:15 0.16084 +13:53:20 0.15630 +13:53:25 0.15379 +13:53:30 0.15305 +13:53:35 0.14820 +13:53:40 0.15387 +13:53:45 0.16189 +13:53:50 0.16235 +13:53:55 0.16847 +13:54:00 0.15823 +13:54:05 0.15132 +13:54:10 0.15320 +13:54:15 0.15679 +13:54:20 0.16128 +13:54:25 0.15859 +13:54:30 0.15606 +13:54:35 0.15344 +13:54:40 0.15424 +13:54:45 0.15545 +13:54:50 0.15846 +13:54:55 0.15692 +13:55:00 0.16054 +13:55:05 0.15718 +13:55:10 0.16181 +13:55:15 0.15596 +13:55:20 0.15808 +13:55:25 0.15706 +13:55:30 0.15933 +13:55:35 0.16558 +13:55:40 0.15604 +13:55:45 0.14879 +13:55:50 0.15633 +13:55:55 0.16189 +13:56:00 0.15535 +13:56:05 0.15347 +13:56:10 0.14894 +13:56:15 0.15314 +13:56:20 0.15700 +13:56:25 0.15889 +13:56:30 0.15127 +13:56:35 0.15765 +13:56:40 0.15548 +13:56:45 0.15216 +13:56:50 0.15362 +13:56:55 0.15360 +13:57:00 0.14620 +13:57:05 0.14955 +13:57:10 0.15740 +13:57:15 0.15442 +13:57:20 0.15938 +13:57:25 0.15866 +13:57:30 0.15868 +13:57:35 0.15542 +13:57:40 0.15381 +13:57:45 0.15708 +13:57:50 0.16142 +13:57:55 0.15780 +13:58:00 0.15464 +13:58:05 0.15476 +13:58:10 0.15718 +13:58:15 0.15782 +13:58:20 0.15996 +13:58:25 0.16345 +13:58:30 0.15711 +13:58:35 0.15284 +13:58:40 0.15065 +13:58:45 0.15378 +13:58:50 0.15303 +13:58:55 0.15797 +13:59:00 0.16215 +13:59:05 0.16014 +13:59:10 0.16201 +13:59:15 0.15971 +13:59:20 0.16175 +13:59:25 0.16074 +13:59:30 0.16122 +13:59:35 0.15715 +13:59:40 0.15513 +13:59:45 0.15546 +13:59:50 0.16014 +13:59:55 0.15992 +14:00:00 0.16061 +14:00:05 0.15941 +14:00:10 0.15571 +14:00:15 0.15057 +14:00:20 0.15410 +14:00:25 0.15692 +14:00:30 0.16004 +14:00:35 0.16792 +14:00:40 0.16303 +14:00:45 0.15769 +14:00:50 0.15909 +14:00:55 0.15617 +14:01:00 0.15802 +14:01:05 0.15589 +14:01:14 0.15559 +14:01:15 0.15559 +14:01:20 0.15652 +14:01:25 0.15729 +14:01:30 0.16341 +14:01:35 0.15561 +14:01:40 0.14942 +14:01:45 0.16009 +14:01:50 0.16436 +14:01:55 0.15884 +14:02:00 0.15882 +14:02:05 0.15491 +14:02:10 0.15566 +14:02:15 0.15410 +14:02:20 0.15053 +14:02:25 0.15540 +14:02:30 0.15887 +14:02:35 0.15906 +14:02:40 0.16310 +14:02:45 0.15313 +14:02:50 0.15593 +14:02:55 0.15595 +14:03:00 0.16020 +14:03:05 0.15867 +14:03:10 0.15602 +14:03:15 0.15852 +14:03:20 0.15876 +14:03:25 0.15372 +14:03:30 0.15532 +14:03:35 0.14972 +14:03:40 0.15123 +14:03:45 0.15268 +14:03:50 0.15505 +14:03:55 0.15555 +14:04:00 0.15793 +14:04:05 0.15638 +14:04:10 0.15151 +14:04:15 0.15293 +14:04:20 0.15168 +14:04:25 0.15615 +14:04:30 0.15618 +14:04:35 0.15629 +14:04:40 0.15565 +14:04:45 0.15305 +14:04:50 0.15867 +14:04:55 0.15600 +14:05:00 0.15395 +14:05:05 0.15512 +14:05:10 0.15928 +14:05:15 0.16120 +14:05:20 0.15480 +14:05:25 0.15436 +14:05:30 0.15851 +14:05:35 0.15500 +14:05:40 0.15609 +14:05:45 0.15579 +14:05:50 0.15773 +14:05:55 0.15955 +14:06:00 0.15358 +14:06:05 0.15607 +14:06:10 0.15945 +14:06:15 0.15886 +14:06:20 0.16407 +14:06:25 0.15947 +14:06:30 0.15587 +14:06:35 0.15901 +14:06:40 0.15552 +14:06:45 0.15570 +14:06:50 0.16119 +14:06:55 0.15811 +14:07:00 0.15512 +14:07:05 0.15735 +14:07:10 0.15098 +14:07:15 0.15158 +14:07:20 0.15634 +14:07:25 0.15568 +14:07:30 0.15239 +14:07:35 0.16040 +14:07:40 0.15708 +14:07:45 0.16067 +14:07:50 0.16128 +14:07:55 0.15798 +14:08:00 0.15416 +14:08:05 0.15632 +14:08:10 0.16083 +14:08:15 0.16430 +14:08:20 0.15546 +14:08:25 0.15473 +14:08:30 0.15280 +14:08:35 0.15266 +14:08:40 0.15706 +14:08:45 0.15607 +14:08:50 0.15674 +14:08:55 0.15789 +14:09:00 0.15476 +14:09:05 0.15638 +14:09:10 0.16471 +14:09:15 0.15893 +14:09:20 0.15499 +14:09:25 0.16237 +14:09:30 0.16163 +14:09:35 0.16158 +14:09:40 0.15589 +14:09:45 0.15782 +14:09:50 0.16190 +14:09:55 0.15579 +14:10:00 0.15959 +14:10:05 0.16088 +14:10:10 0.15538 +14:10:15 0.16015 +14:10:20 0.15317 +14:10:25 0.15347 +14:10:30 0.15172 +14:10:35 0.15541 +14:10:40 0.15531 +14:10:45 0.16080 +14:10:50 0.15850 +14:10:55 0.15483 +14:11:00 0.15525 +14:11:05 0.15587 +14:11:10 0.16014 +14:11:15 0.16037 +14:11:20 0.15471 +14:11:25 0.15468 +14:11:30 0.15693 +14:11:35 0.15578 +14:11:40 0.15762 +14:11:45 0.16030 +14:11:50 0.15710 +14:11:55 0.15185 +14:12:00 0.15749 +14:12:05 0.15434 +14:12:10 0.16149 +14:12:15 0.15955 +14:12:20 0.15518 +14:12:25 0.15625 +14:12:30 0.15635 +14:12:35 0.16165 +14:12:40 0.16037 +14:12:45 0.16260 +14:12:50 0.16008 +14:12:55 0.15674 +14:13:00 0.15469 +14:13:05 0.15361 +14:13:10 0.16202 +14:13:15 0.15663 +14:13:20 0.16224 +14:13:25 0.16374 +14:13:30 0.15420 +14:13:35 0.15573 +14:13:40 0.15660 +14:13:45 0.15721 +14:13:50 0.15214 +14:13:55 0.15493 +14:14:00 0.15992 +14:14:05 0.15460 +14:14:10 0.15397 +14:14:15 0.15975 +14:14:20 0.15309 +14:14:25 0.16019 +14:14:30 0.15858 +14:14:35 0.15617 +14:14:40 0.15724 +14:14:45 0.15765 +14:14:50 0.15196 +14:14:55 0.15928 +14:15:00 0.15934 +14:15:05 0.15683 +14:15:10 0.16168 +14:15:15 0.16156 +14:15:20 0.15372 +14:15:25 0.15673 +14:15:30 0.15563 +14:15:35 0.15123 +14:15:40 0.15079 +14:15:45 0.15237 +14:15:50 0.15699 +14:15:55 0.15886 +14:16:00 0.15827 +14:16:05 0.15838 +14:16:10 0.15600 +14:16:15 0.15679 +14:16:25 0.16225 +14:16:30 0.15933 +14:16:35 0.15388 +14:16:40 0.15906 +14:16:45 0.15541 +14:16:50 0.15509 +14:16:55 0.15687 +14:17:00 0.15256 +14:17:05 0.15621 +14:17:10 0.15507 +14:17:15 0.15627 +14:17:20 0.15783 +14:17:25 0.15454 +14:17:30 0.15960 +14:17:35 0.16179 +14:17:40 0.16288 +14:17:45 0.16232 +14:17:50 0.16373 +14:17:55 0.16898 +14:18:00 0.16840 +14:18:05 0.16562 +14:18:10 0.17473 +14:18:15 0.17406 +14:18:20 0.17487 +14:18:25 0.17801 +14:18:30 0.18845 +14:18:35 0.18892 +14:18:40 0.21422 +14:18:45 0.41001 +14:18:50 0.78100 +14:18:55 0.87237 +14:19:00 1.2188 +14:19:05 1.4900 +14:19:10 1.5785 +14:19:15 1.2923 +14:19:20 1.0776 +14:19:25 1.2900 +14:19:30 1.4591 +14:19:35 1.4109 +14:19:40 1.3475 +14:19:45 1.2398 +14:19:50 1.3375 +14:19:55 1.5821 +14:20:00 1.5364 +14:20:05 4.8250 +14:20:10 3.1921 +14:20:15 1.4507 +14:20:20 1.3861 +14:20:25 1.3782 +14:20:30 1.4600 +14:20:35 2.1113 +14:20:40 2.0152 +14:20:45 2.1944 +14:20:50 1.9211 +14:20:55 1.9631 +14:21:00 2.9701 +14:21:05 1.9396 +14:21:10 1.8230 +14:21:15 2.9968 +14:21:20 2.9712 +14:21:25 2.0347 +14:21:30 1.6284 +14:21:35 2.1419 +14:21:40 3.0369 +14:21:45 2.1591 +14:21:50 2.0545 +14:21:55 2.7250 +14:22:00 1.9995 +14:22:05 1.6586 +14:22:13 2.1303 +14:22:15 2.1813 +14:22:20 2.5220 +14:22:25 2.6809 +14:22:30 1.5945 +14:22:35 1.4464 +14:22:40 1.4437 +14:22:45 1.3317 +14:22:50 1.6946 +14:22:55 1.6581 +14:23:00 1.7462 +14:23:05 3.0054 +14:23:10 2.0527 +14:23:15 2.2458 +14:23:20 1.8125 +14:23:25 1.2014 +14:23:30 1.3047 +14:23:35 1.2093 +14:23:40 1.2969 +14:23:45 1.6198 +14:23:50 2.2268 +14:23:55 2.3651 +14:24:00 2.1144 +14:24:05 1.4402 +14:24:10 2.1089 +14:24:15 2.0186 +14:24:20 2.1608 +14:24:25 2.1114 +14:24:30 2.7092 +14:24:35 2.6155 +14:24:40 3.4728 +14:24:45 2.1291 +14:24:50 1.8464 +14:24:55 2.0649 +14:25:00 2.0490 +14:25:05 1.5565 +14:25:10 2.1299 +14:25:15 4.1631 +14:25:20 2.8430 +14:25:25 2.7431 +14:25:30 3.4791 +14:25:35 2.5467 +14:25:40 2.0791 +14:25:45 3.8225 +14:25:50 3.8369 +14:25:55 2.6653 +14:26:00 2.4913 +14:26:05 3.0326 +14:26:10 2.2092 +14:26:15 1.8703 +14:26:20 2.1218 +14:26:25 1.9838 +14:26:30 1.9627 +14:26:35 1.8702 +14:26:40 2.3995 +14:26:45 2.0044 +14:26:50 1.9100 +14:26:55 1.9704 +14:27:00 2.2946 +14:27:05 2.2029 +14:27:10 2.3654 +14:27:15 2.1168 +14:27:20 1.6774 +14:27:25 1.8196 +14:27:30 1.7340 +14:27:35 2.2993 +14:27:40 2.5057 +14:27:45 2.4950 +14:27:50 3.1228 +14:27:55 3.1616 +14:28:00 2.9337 +14:28:05 2.9027 +14:28:10 2.6826 +14:28:15 1.9078 +14:28:20 1.5077 +14:28:25 1.6715 +14:28:30 2.0773 +14:28:35 1.9607 +14:28:40 2.8687 +14:28:45 2.2899 +14:28:50 2.6356 +14:28:55 3.1693 +14:29:00 2.6068 +14:29:05 1.8361 +14:29:10 2.1318 +14:29:15 2.1678 +14:29:20 1.7904 +14:29:25 1.8703 +14:29:30 3.1321 +14:29:35 3.3451 +14:29:40 2.5354 +14:29:45 1.9244 +14:29:50 2.0543 +14:29:55 1.8395 +14:30:00 1.5418 +14:30:05 2.1691 +14:30:10 2.3013 +14:30:15 1.8439 +14:30:20 1.8641 +14:30:25 2.7176 +14:30:30 2.5047 +14:30:35 2.1262 +14:30:40 1.4688 +14:30:45 1.6228 +14:30:50 1.7463 +14:30:55 1.7207 +14:31:00 2.0140 +14:31:05 1.5982 +14:31:10 1.8905 +14:31:15 2.1515 +14:31:20 2.6844 +14:31:25 3.1188 +14:31:30 2.1709 +14:31:35 2.5886 +14:31:40 3.1698 +14:31:45 2.5839 +14:31:50 2.7280 +14:31:55 4.0561 +14:32:00 1.6991 +14:32:05 1.5750 +14:32:10 1.3896 +14:32:15 1.7427 +14:32:20 1.4348 +14:32:25 1.4334 +14:32:30 1.5415 +14:32:35 2.4559 +14:32:40 2.5026 +14:32:45 2.2687 +14:32:50 3.3489 +14:32:55 3.8634 +14:33:00 2.7512 +14:33:05 1.8408 +14:33:10 2.3280 +14:33:15 2.3215 +14:33:20 2.7826 +14:33:25 2.6021 +14:33:30 3.6104 +14:33:35 2.0327 +14:33:40 2.1239 +14:33:45 2.1737 +14:33:50 2.0805 +14:33:55 2.5848 +14:34:00 3.0732 +14:34:05 2.5781 +14:34:10 2.1309 +14:34:15 2.1244 +14:34:20 2.3401 +14:34:25 2.1999 +14:34:30 2.9446 +14:34:35 3.4992 +14:34:40 3.9875 +14:34:45 3.0706 +14:34:50 4.7272 +14:34:58 5.1302 +14:35:05 4.0056 +14:35:10 4.4945 +14:35:17 5.0313 +14:35:20 5.0499 +14:35:25 5.1350 +14:35:30 5.0106 +14:35:35 3.9304 +14:35:40 4.1074 +14:35:46 4.4212 +14:35:54 3.6196 +14:35:55 4.5012 +14:36:00 4.7160 +14:36:05 4.8141 +14:36:10 4.1347 +14:36:15 4.7422 +14:36:20 4.2952 +14:36:25 4.7712 +14:36:31 4.6552 +14:36:35 4.2307 +14:36:40 4.0650 +14:36:45 4.0060 +14:36:52 4.7759 +14:36:59 5.0065 +14:37:01 4.8188 +14:37:07 4.2559 +14:37:10 5.0478 +14:37:15 4.9210 +14:37:20 3.4581 +14:37:25 3.8950 +14:37:30 4.8515 +14:37:35 4.3551 +14:37:40 3.9469 +14:37:45 4.8318 +14:37:50 4.1543 +14:37:55 4.5691 +14:38:00 3.8149 +14:38:05 4.9151 +14:38:10 3.9805 +14:38:15 4.4270 +14:38:20 3.5962 +14:38:25 3.6687 +14:38:30 4.4751 +14:38:35 3.0519 +14:38:40 3.4429 +14:38:45 3.2280 +14:38:50 3.1266 +14:38:55 3.4860 +14:39:03 4.3663 +14:39:05 3.7392 +14:39:10 2.6529 +14:39:15 3.0949 +14:39:20 3.7387 +14:39:25 3.5572 +14:39:30 4.2081 +14:39:35 4.1362 +14:39:40 4.5150 +14:39:45 4.9422 +14:39:50 5.1242 +14:39:55 5.1242 +14:40:00 5.0244 +14:40:05 4.9493 +14:40:10 3.7043 +14:40:15 3.8544 +14:40:20 4.4121 +14:40:25 5.0255 +14:40:30 4.8742 +14:40:35 4.6467 +14:40:40 4.6296 +14:40:45 3.7527 +14:40:50 3.7917 +14:40:55 3.4857 +14:41:00 4.0162 +14:41:05 4.1214 +14:41:10 3.8011 +14:41:15 4.1370 +14:41:20 4.1956 +14:41:25 3.5047 +14:41:30 4.0278 +14:41:35 5.0463 +14:41:40 5.0872 +14:41:45 4.9564 +14:41:50 4.3404 +14:41:55 3.1634 +14:42:00 4.5703 +14:42:05 4.0812 +14:42:10 4.2885 +14:42:15 3.9365 +14:42:20 5.0816 +14:42:25 4.6555 +14:42:30 3.6089 +14:42:35 3.4086 +14:42:40 4.2345 +14:42:45 4.5008 +14:42:50 4.7698 +14:42:55 4.6087 +14:43:00 5.0890 +14:43:05 3.9844 +14:43:10 3.3929 +14:43:15 3.5147 +14:43:20 4.4961 +14:43:25 4.7387 +14:43:30 4.7217 +14:43:35 5.0412 +14:43:40 5.0206 +14:43:45 4.9618 +14:43:50 4.5130 +14:43:55 4.0343 +14:44:00 3.1452 +14:44:05 3.2401 +14:44:10 3.0429 +14:44:15 3.8125 +14:44:20 3.9854 +14:44:25 4.0588 +14:44:30 4.2503 +14:44:35 4.1770 +14:44:40 4.2310 +14:44:45 4.3937 +14:44:50 2.9416 +14:44:55 3.2820 +14:45:00 2.9899 +14:45:05 3.0242 +14:45:10 2.9708 +14:45:15 2.7438 +14:45:20 3.0548 +14:45:25 2.7919 +14:45:30 3.6389 +14:45:35 3.2412 +14:45:40 3.3260 +14:45:45 4.0548 +14:45:50 3.4130 +14:45:55 3.5946 +14:46:00 2.8852 +14:46:05 3.7608 +14:46:10 4.7485 +14:46:15 4.9187 +14:46:20 4.4179 +14:46:25 4.2325 +14:46:30 4.8619 +14:46:35 4.4754 +14:46:40 3.5703 +14:46:45 4.5403 +14:46:50 3.4993 +14:46:55 3.0152 +14:47:00 3.1246 +14:47:05 3.1586 +14:47:10 4.0283 +14:47:15 3.6102 +14:47:20 4.5847 +14:47:25 4.2917 +14:47:30 3.5848 +14:47:35 3.8719 +14:47:40 4.6561 +14:47:45 4.2296 +14:47:50 3.5808 +14:47:55 4.2895 +14:48:00 4.0953 +14:48:05 3.1817 +14:48:10 4.4750 +14:48:15 4.3748 +14:48:20 3.0079 +14:48:25 3.8458 +14:48:30 3.4098 +14:48:35 4.1065 +14:48:40 4.9178 +14:48:45 4.4329 +14:48:50 3.1905 +14:48:55 3.3332 +14:49:00 3.5097 +14:49:05 4.5185 +14:49:10 4.2362 +14:49:15 3.9386 +14:49:20 2.0419 +14:49:25 3.1712 +14:49:30 4.4247 +14:49:35 4.9970 +14:49:40 4.8842 +14:49:45 5.1506 +14:49:50 4.3711 +14:49:55 3.5476 +14:50:00 3.9071 +14:50:05 3.8639 +14:50:10 3.5371 +14:50:15 4.4881 +14:50:20 4.4470 +14:50:25 4.3819 +14:50:30 4.4402 +14:50:35 4.6129 +14:50:40 3.4963 +14:50:45 3.7861 +14:50:50 4.5448 +14:50:55 4.2310 +14:51:00 3.8084 +14:51:05 3.9762 +14:51:10 3.9739 +14:51:15 3.5739 +14:51:20 3.9396 +14:51:25 4.2651 +14:51:30 4.4844 +14:51:35 3.9608 +14:51:40 3.6818 +14:51:45 3.4129 +14:51:50 3.7100 +14:51:55 3.5340 +14:52:00 3.8764 +14:52:05 4.6628 +14:52:10 4.8502 +14:52:15 4.9944 +14:52:20 4.2582 +14:52:25 4.5458 +14:52:30 5.0109 +14:52:35 4.8235 +14:52:40 4.8607 +14:52:45 4.8896 +14:52:50 4.6132 +14:52:55 3.6442 +14:53:00 4.4602 +14:53:05 4.9746 +14:53:10 3.1687 +14:53:15 4.3439 +14:53:20 4.1353 +14:53:25 4.0062 +14:53:30 4.0938 +14:53:35 4.2403 +14:53:40 3.5565 +14:53:45 3.6352 +14:53:50 5.0975 +14:53:55 4.7209 +14:54:00 4.6470 +14:54:05 5.1167 +14:54:10 4.2715 +14:54:15 4.9665 +14:54:20 4.0763 +14:54:25 4.2809 +14:54:30 4.4555 +14:54:35 4.6470 +14:54:40 5.0003 +14:54:45 4.7497 +14:54:50 4.5890 +14:54:55 4.8740 +14:55:00 4.0362 +14:55:05 4.9024 +14:55:10 3.7485 +14:55:15 3.1337 +14:55:20 3.9894 +14:55:25 4.7961 +14:55:30 4.5765 +14:55:35 4.7683 +14:55:40 3.7457 +14:55:45 3.2225 +14:55:50 2.4384 +14:55:55 2.8683 +14:56:00 2.7061 +14:56:05 4.0842 +14:56:10 2.5299 +14:56:15 4.4479 +14:56:20 2.8100 +14:56:25 4.7457 +14:56:30 3.2385 +14:56:35 3.7688 +14:56:40 4.5341 +14:56:45 4.6938 +14:56:50 5.0368 +14:56:55 5.0211 +14:57:00 3.7509 +14:57:05 3.8207 +14:57:10 3.7379 +14:57:15 3.2405 +14:57:20 2.5792 +14:57:25 3.2422 +14:57:30 3.6622 +14:57:35 3.2560 +14:57:40 4.6923 +14:57:45 3.6954 +14:57:50 3.5134 +14:57:56 4.3686 +14:58:00 3.9063 +14:58:05 4.6352 +14:58:10 4.9259 +14:58:15 4.6910 +14:58:20 4.4678 +14:58:25 4.5679 +14:58:30 3.3506 +14:58:35 4.0658 +14:58:40 3.6075 +14:58:45 2.3810 +14:58:50 3.2624 +14:58:55 4.6035 +14:59:00 4.4081 +14:59:13 4.9171 +14:59:17 4.0662 +14:59:20 4.1487 +14:59:25 4.7783 +14:59:30 4.6604 +14:59:36 4.9341 +14:59:40 4.6574 +14:59:45 4.9124 +14:59:50 4.4971 +14:59:55 4.3894 +15:00:00 5.1211 +15:00:05 3.9982 +15:00:10 4.8046 +15:00:15 4.7012 +15:00:20 3.8995 +15:00:26 3.0856 +15:00:30 3.3430 +15:00:35 4.5266 +15:00:40 4.3647 +15:00:45 4.6591 +15:00:50 4.7625 +15:00:55 4.6689 +15:01:00 4.7110 +15:01:05 3.8538 +15:01:10 3.5960 +15:01:16 3.5279 +15:01:20 2.6005 +15:01:25 4.5662 +15:01:30 3.8245 +15:01:38 4.2261 +15:01:40 3.8425 +15:01:45 4.7769 +15:01:50 4.5973 +15:01:56 4.4149 +15:02:00 3.8324 +15:02:07 3.4295 +15:02:10 4.6343 +15:02:15 4.0480 +15:02:20 5.0389 +15:02:25 3.0484 +15:02:30 4.0833 +15:02:35 5.1093 +15:02:40 3.9803 +15:02:45 4.9780 +15:02:50 3.2970 +15:02:55 4.4228 +15:03:00 2.8577 +15:03:05 3.5472 +15:03:10 3.6960 +15:03:15 4.1321 +15:03:20 3.5264 +15:03:25 4.5213 +15:03:30 4.6481 +15:03:35 4.2143 +15:03:40 4.1998 +15:03:45 3.4891 +15:03:51 4.5291 +15:03:58 4.0335 +15:04:00 3.4658 +15:04:05 3.6556 +15:04:11 2.9325 +15:04:15 3.1396 +15:04:20 4.3290 +15:04:26 3.6793 +15:04:30 3.9021 +15:04:35 3.7590 +15:04:40 4.5041 +15:04:47 4.1426 +15:04:51 3.3112 +15:04:57 3.7590 +15:05:02 4.3033 +15:05:05 4.4554 +15:05:12 4.0625 +15:05:15 4.0331 +15:05:22 4.6265 +15:05:28 4.3487 +15:05:30 3.1857 +15:05:36 2.9882 +15:05:42 3.5770 +15:05:45 4.0475 +15:05:51 4.4456 +15:06:00 4.0065 +15:06:05 2.7791 +15:06:10 4.2369 +15:06:15 3.0891 +15:06:20 3.8759 +15:06:25 3.9173 +15:06:31 4.2046 +15:06:35 3.4916 +15:06:40 3.3164 +15:06:45 3.6887 +15:06:50 3.2659 +15:06:55 3.9265 +15:07:00 3.1595 +15:07:05 3.9887 +15:07:10 3.9594 +15:07:15 3.1072 +15:07:20 4.7449 +15:07:25 3.8658 +15:07:30 4.2975 +15:07:35 4.5659 +15:07:40 4.2452 +15:07:45 4.2052 +15:07:50 3.2710 +15:07:55 2.8941 +15:08:00 3.9788 +15:08:05 4.7096 +15:08:10 3.3579 +15:08:15 4.6452 +15:08:20 3.4665 +15:08:25 4.3437 +15:08:30 4.4395 +15:08:35 5.0097 +15:08:40 4.9457 +15:08:45 3.9214 +15:08:50 4.7544 +15:08:55 3.4557 +15:09:00 3.6499 +15:09:05 3.7397 +15:09:10 3.0223 +15:09:15 3.5375 +15:09:20 4.4060 +15:09:25 4.9537 +15:09:30 4.1028 +15:09:36 4.7996 +15:09:40 4.5860 +15:09:45 4.3259 +15:09:50 4.3007 +15:09:55 4.2802 +15:10:00 3.2956 +15:10:05 3.1497 +15:10:10 3.6630 +15:10:16 4.2005 +15:10:20 3.7970 +15:10:26 3.6850 +15:10:32 4.0624 +15:10:35 3.3703 +15:10:40 3.5958 +15:10:45 4.1215 +15:10:50 4.3751 +15:10:56 3.7933 +15:11:00 3.6074 +15:11:05 3.5466 +15:11:10 4.2689 +15:11:16 4.1222 +15:11:21 4.2462 +15:11:26 4.1700 +15:11:40 3.4369 +15:11:45 3.6291 +15:11:51 4.8817 +15:11:57 4.7252 +15:12:00 4.6008 +15:12:05 3.3738 +15:12:10 3.7236 +15:12:15 3.9230 +15:12:20 3.7132 +15:12:25 4.4135 +15:12:30 4.7577 +15:12:35 3.9632 +15:12:40 4.7232 +15:12:45 3.9490 +15:12:50 3.9311 +15:12:55 3.8749 +15:13:00 4.8197 +15:13:05 4.8255 +15:13:10 4.1803 +15:13:15 3.5291 +15:13:21 3.3685 +15:13:25 4.1323 +15:13:30 4.2274 +15:13:35 4.0041 +15:13:41 3.8375 +15:13:46 4.2266 +15:13:54 4.3074 +15:13:55 3.5857 +15:14:00 3.4089 +15:14:05 3.5144 +15:14:11 4.0357 +15:14:17 4.3330 +15:14:20 3.9776 +15:14:27 4.6330 +15:14:30 4.5952 +15:14:35 3.9972 +15:14:42 3.1962 +15:14:45 3.7487 +15:14:55 4.2996 +15:15:06 4.2906 +15:15:13 5.0396 +15:15:18 4.3532 +15:15:20 3.1873 +15:15:25 1.8356 +15:15:34 4.6100 +15:15:35 2.7322 +15:15:40 3.6042 +15:15:45 4.2982 +15:15:50 3.6596 +15:15:55 3.7637 +15:16:00 3.8533 +15:16:48 3.8691 +15:16:54 4.0854 +15:16:55 3.9145 +15:17:00 3.8565 +15:17:05 4.0900 +15:17:10 4.2668 +15:17:15 4.5612 +15:17:20 4.6264 +15:17:25 4.7419 +15:17:30 3.3621 +15:17:35 2.8598 +15:17:41 4.5943 +15:17:45 4.6145 +15:17:50 3.0591 +15:17:55 3.5438 +15:18:00 3.7712 +15:18:05 3.1788 +15:18:10 3.4818 +15:18:15 3.8744 +15:18:20 3.7498 +15:18:25 4.3014 +15:18:30 3.9566 +15:18:35 2.6596 +15:18:40 3.6802 +15:18:45 3.7579 +15:18:50 3.9190 +15:18:57 3.8695 +15:19:00 2.6564 +15:19:06 4.6534 +15:19:10 4.4296 +15:19:15 4.0382 +15:19:20 4.9317 +15:19:25 4.5007 +15:19:32 3.6640 +15:19:35 4.8829 +15:19:42 4.4323 +15:19:45 4.2974 +15:19:50 4.7321 +15:19:55 4.1828 +15:20:00 4.0782 +15:20:05 4.2462 +15:20:10 4.2712 +15:20:15 3.9919 +15:20:20 3.6666 +15:20:25 2.9239 +15:20:30 3.7230 +15:20:35 5.0828 +15:20:40 4.5134 +15:20:45 4.4724 +15:20:50 4.7821 +15:20:55 3.4196 +15:21:00 2.7891 +15:21:10 2.7570 +15:21:15 3.9705 +15:21:20 3.9389 +15:21:25 4.6344 +15:21:30 4.8470 +15:21:35 4.9319 +15:21:40 4.9101 +15:21:45 4.2418 +15:21:50 3.7708 +15:21:55 3.6534 +15:22:00 3.0165 +15:22:05 3.1831 +15:22:10 4.1335 +15:22:15 4.6802 +15:22:20 4.6883 +15:22:25 4.7305 +15:22:30 3.5468 +15:22:35 4.4628 +15:22:40 4.9027 +15:22:45 4.1616 +15:22:50 3.7034 +15:22:55 3.6235 +15:23:00 3.6344 +15:23:05 4.1065 +15:23:10 3.6308 +15:23:15 4.4060 +15:23:20 4.9768 +15:23:25 4.7487 +15:23:30 4.5619 +15:23:35 4.1228 +15:23:40 4.0845 +15:23:45 4.4343 +15:23:50 4.7816 +15:23:55 4.6888 +15:24:00 4.1218 +15:24:05 4.2811 +15:24:10 4.9119 +15:24:15 4.7081 +15:24:20 3.8448 +15:24:25 4.2118 +15:24:30 4.3141 +15:24:35 2.8787 +15:24:40 2.3096 +15:24:45 3.0453 +15:24:50 3.1223 +15:24:55 3.4361 +15:25:00 4.6786 +15:25:05 4.8238 +15:25:10 3.9141 +15:25:15 3.0532 +15:25:20 3.5844 +15:25:25 4.1636 +15:25:30 4.6639 +15:25:41 4.2526 +15:25:45 3.5818 +15:25:50 4.3284 +15:25:55 3.0972 +15:26:00 3.3592 +15:26:05 2.6707 +15:26:10 2.5910 +15:26:15 3.0275 +15:26:20 3.3105 +15:26:25 4.1370 +15:26:30 3.3704 +15:26:35 2.3095 +15:26:40 3.1479 +15:26:45 3.4726 +15:26:50 4.0446 +15:26:55 4.5630 +15:27:00 3.5423 +15:27:05 3.9095 +15:27:10 4.5574 +15:27:16 3.7128 +15:27:20 3.6147 +15:27:25 2.3083 +15:27:30 3.8030 +15:27:35 3.9167 +15:27:40 3.6425 +15:27:45 3.5450 +15:27:50 3.5685 +15:27:55 3.5555 +15:28:00 3.4802 +15:28:05 3.4802 +15:28:10 3.7658 +15:28:15 3.4044 +15:28:20 3.4830 +15:28:25 3.8128 +15:28:30 4.8181 +15:28:35 4.7007 +15:28:40 4.6712 +15:28:45 4.4058 +15:28:50 5.0051 +15:28:55 5.0291 +15:29:00 3.9822 +15:29:05 3.4761 +15:29:10 4.2317 +15:29:15 5.1026 +15:29:20 4.3960 +15:29:25 4.2040 +15:29:30 3.9823 +15:29:35 4.3102 +15:29:40 4.2233 +15:29:45 4.0966 +15:29:50 4.3017 +15:29:55 4.2482 +15:30:00 3.7821 +15:30:05 3.7102 +15:30:10 3.8120 +15:30:15 3.7733 +15:30:20 3.7787 +15:30:25 3.7787 +15:30:30 3.7234 +15:30:35 3.3555 +15:30:40 3.4839 +15:30:45 3.4334 +15:30:50 3.5458 +15:30:55 3.5458 +15:31:00 3.5737 +15:31:05 3.8093 +15:31:10 3.8687 +15:31:15 3.6158 +15:31:20 3.6400 +15:31:25 3.8343 +15:31:30 4.3289 +15:31:35 4.6827 +15:31:40 4.0072 +15:31:45 3.2425 +15:31:50 5.0425 +15:31:55 5.0143 +15:32:00 4.0130 +15:32:05 3.9664 +15:32:10 3.4517 +15:32:15 2.9267 +15:32:20 3.2338 +15:32:25 3.5358 +15:32:30 3.8163 +15:32:35 2.9669 +15:32:40 3.4165 +15:32:45 4.0559 +15:32:50 3.7171 +15:32:55 3.8179 +15:33:00 3.4580 +15:33:05 3.4154 +15:33:10 3.5092 +15:33:15 3.5714 +15:33:20 3.7812 +15:33:25 3.5706 +15:33:30 3.4405 +15:33:35 3.8478 +15:33:40 3.6984 +15:33:45 3.8862 +15:33:50 4.0245 +15:33:55 5.1584 +15:34:00 4.2329 +15:34:05 4.4908 +15:34:10 4.6532 +15:34:15 3.9406 +15:34:20 3.7473 +15:34:25 3.6472 +15:34:30 3.4764 +15:34:35 4.1516 +15:34:40 4.1582 +15:34:45 3.4295 +15:34:50 3.9380 +15:34:55 3.7949 +15:35:00 3.8207 +15:35:05 3.8994 +15:35:10 4.7165 +15:35:15 4.3260 +15:35:20 2.8046 +15:35:25 4.3127 +15:35:30 4.6219 +15:35:35 3.7009 +15:35:40 3.2196 +15:35:45 4.1235 +15:35:50 3.3892 +15:35:55 4.1848 +15:36:00 4.2728 +15:36:05 3.0149 +15:36:15 3.5741 +15:36:20 3.3450 +15:36:25 3.5353 +15:36:30 3.7689 +15:36:35 3.1939 +15:36:40 3.3990 +15:36:45 3.7898 +15:36:50 4.0800 +15:36:55 4.1625 +15:37:00 4.0324 +15:37:05 2.6636 +15:37:10 2.9293 +15:37:15 3.4335 +15:37:20 3.0982 +15:37:25 2.9650 +15:37:30 3.0823 +15:37:35 3.9720 +15:37:40 4.6494 +15:37:45 4.0524 +15:37:50 3.3487 +15:37:55 3.0645 +15:38:00 3.5640 +15:38:05 3.1275 +15:38:10 3.8128 +15:38:15 4.5543 +15:38:20 4.9980 +15:38:25 5.0811 +15:38:30 4.4440 +15:38:35 4.6937 +15:38:40 4.5591 +15:38:45 3.6078 +15:38:50 3.5456 +15:38:55 3.7120 +15:39:00 3.2494 +15:39:05 3.7511 +15:39:10 3.3647 +15:39:15 4.6861 +15:39:20 4.7032 +15:39:25 4.2812 +15:39:30 3.5334 +15:39:35 2.9161 +15:39:40 3.7053 +15:39:45 2.9780 +15:39:50 4.4497 +15:39:55 4.5490 +15:40:00 4.8275 +15:40:05 4.9899 +15:40:10 4.6672 +15:40:15 3.9396 +15:40:20 4.7298 +15:40:25 3.8586 +15:40:30 3.6741 +15:40:35 3.9354 +15:40:40 3.5975 +15:40:45 2.8065 +15:40:50 3.5648 +15:40:55 4.0775 +15:41:00 4.9281 +15:41:05 4.7539 +15:41:10 4.9543 +15:41:15 4.2202 +15:41:20 5.0146 +15:41:25 4.0771 +15:41:30 4.5286 +15:41:35 5.0618 +15:41:40 5.1537 +15:41:45 4.8183 +15:41:50 4.9481 +15:41:55 4.3225 +15:42:00 3.4334 +15:42:05 4.0070 +15:42:10 3.1660 +15:42:15 2.7699 +15:42:20 3.7629 +15:42:25 3.9472 +15:42:30 5.0832 +15:42:35 4.8400 +15:42:40 4.7717 +15:42:45 4.0138 +15:42:50 3.5939 +15:42:55 3.7825 +15:43:00 5.0680 +15:43:05 3.8511 +15:43:10 5.1326 +15:43:15 4.0633 +15:43:20 3.2859 +15:43:25 2.0645 +15:43:30 2.0550 +15:43:35 3.5359 +15:43:40 3.5696 +15:43:45 3.2064 +15:43:50 4.5037 +15:43:55 4.4547 +15:44:00 4.2186 +15:44:05 3.2178 +15:44:10 2.2721 +15:44:15 4.1242 +15:44:20 4.7840 +15:44:25 3.0753 +15:44:30 5.1292 +15:44:35 4.0880 +15:44:40 4.3629 +15:44:45 4.6045 +15:44:50 4.0396 +15:44:55 3.6352 +15:45:00 3.4947 +15:45:05 4.2058 +15:45:10 3.6470 +15:45:15 3.2292 +15:45:20 3.3054 +15:45:25 4.3254 +15:45:30 3.3571 +15:45:35 3.4613 +15:45:40 3.6562 +15:45:45 3.9765 +15:45:50 3.8140 +15:45:55 3.2001 +15:46:00 4.2890 +15:46:05 4.2849 +15:46:10 2.8204 +15:46:15 3.1173 +15:46:20 3.3885 +15:46:25 3.7724 +15:46:30 3.2397 +15:46:35 3.3354 +15:46:40 3.9091 +15:46:45 4.1466 +15:46:50 4.3368 +15:46:55 4.6446 +15:47:00 4.5703 +15:47:05 4.5498 +15:47:10 4.6055 +15:47:15 4.2674 +15:47:20 2.9780 +15:47:25 2.9015 +15:47:30 2.9928 +15:47:35 3.1222 +15:47:40 3.6976 +15:47:45 4.0542 +15:47:50 4.9046 +15:47:55 3.9439 +15:48:00 4.4248 +15:48:05 4.6497 +15:48:10 3.4013 +15:48:15 3.9242 +15:48:20 2.8499 +15:48:25 3.5752 +15:48:30 4.1481 +15:48:35 3.6090 +15:48:40 3.4556 +15:48:45 4.0051 +15:48:50 4.5730 +15:48:55 5.0374 +15:49:00 3.7008 +15:49:05 4.3418 +15:49:10 4.5581 +15:49:15 4.4103 +15:49:20 4.3618 +15:49:25 4.8249 +15:49:30 4.5168 +15:49:35 4.3327 +15:49:40 2.8764 +15:49:45 4.5569 +15:49:50 4.4800 +15:49:55 4.5583 +15:50:00 3.6093 +15:50:05 4.0981 +15:50:10 2.7855 +15:50:15 2.4716 +15:50:20 2.5959 +15:50:25 2.6254 +15:50:30 4.3024 +15:50:35 4.2036 +15:50:40 4.3863 +15:50:45 4.2410 +15:50:50 2.7534 +15:50:55 4.0177 +15:51:00 4.0177 +15:51:05 3.9786 +15:51:10 3.9597 +15:51:15 3.9455 +15:51:20 3.9528 +15:51:25 3.9113 +15:51:30 3.9113 +15:51:35 3.8331 +15:51:40 3.8125 +15:51:45 3.8326 +15:51:50 3.8736 +15:51:57 3.9346 +15:52:00 3.9346 +15:52:05 3.9783 +15:52:10 3.9860 +15:52:15 3.9491 +15:52:20 3.9283 +15:52:25 3.8969 +15:52:30 3.8969 +15:52:35 3.7973 +15:52:40 3.7131 +15:52:45 3.6774 +15:52:50 3.5981 +15:52:55 3.5934 +15:53:00 3.5934 +15:53:05 3.5908 +15:53:10 3.6047 +15:53:15 3.6349 +15:53:20 3.7138 +15:53:25 3.7360 +15:53:30 3.7360 +15:53:35 3.6574 +15:53:40 3.5424 +15:53:45 3.5037 +15:53:50 3.5389 +15:53:55 3.5389 +15:54:00 3.6133 +15:54:05 3.5394 +15:54:10 3.5531 +15:54:15 3.6394 +15:54:20 3.7266 +15:54:25 3.7266 +15:54:30 3.5878 +15:54:35 3.6012 +15:54:40 3.7164 +15:54:45 3.9094 +15:54:50 4.0513 +15:54:55 4.0513 +15:55:00 4.1209 +15:55:05 4.1304 +15:55:10 4.0536 diff --git a/mcstas/dmc/vdmc.tcl b/mcstas/dmc/vdmc.tcl index dd96b67f..2d7b9049 100644 --- a/mcstas/dmc/vdmc.tcl +++ b/mcstas/dmc/vdmc.tcl @@ -1,13 +1,19 @@ - # -------------------------------------------------------------------------- +#-------------------------------------------------------------------------- # Initialization script for a virtual DMC instrument using a McStas # simulationas a data source # # Dr. Mark Koennecke, June 2005 #--------------------------------------------------------------------------- # O P T I O N S - -set home $env(HOME)/src/workspace/sics/mcstas/dmc - +# wwwMode = 1 when running for the WWW-VDMC application +set wwwMode 0 +if {$wwwMode == 1} { + set home /home/lnswww/vinstrument/mcstas/dmc + set datahome /home/lnswww/www/vinstrument +} else { + set home $env(HOME)/src/workspace/sics/mcstas/dmc + ServerOption LoggerDir $env(HOME)/src/workspace/sics/mcstas/dmc/samenv +} #--------------------------------- first all the server options are set #ServerOption RedirectFile $home/stdcdmc ServerOption ReadTimeOut 10 @@ -26,21 +32,22 @@ SicsUser lnsmanager lnsSICSlns 1 SicsUser Manager Manager 1 SicsUser user looser 2 SicsUser Spy 007 1 +SicsUser User 07lns1 2 #-------------------------------------------------------------------------- # D E V I C E S : M O T O R S #--------------------------------------------------------------------------- ClientPut "Installing Motors" -Motor OmegaM SIM 0 120 -.1 2. -Motor TwoThetaM SIM 30 100 -.1 1. -Motor MonoX SIM -30 30 -.1 3.0 -Motor MonoY SIM -30 30 -.1 3.0 -Motor CurveM SIM 0 20 -.1 3.0 -Motor MonoPhi SIM -30 30 -.1 3.0 -Motor MonoChi SIM -30 30 -.1 3.0 +Motor OmegaM SIM 0 120 .0000001 2. +Motor TwoThetaM SIM 30 100 .0000001 1. +Motor MonoX SIM -30 30 .00000001 3.0 +Motor MonoY SIM -30 30 .000000001 3.0 +Motor CurveM SIM 0 20 .000000001 3.0 +Motor MonoPhi SIM -30 30 .00000001 3.0 +Motor MonoChi SIM -30 30 .00000001 3.0 # sample Table -Motor Table SIM -180 360 -.1 2. -Motor TwoThetaD SIM -10 120 -.1 1. +Motor Table SIM -180 360 .0000001 2. +Motor TwoThetaD SIM -10 120 .0000001 1. #------------------------------------------------------------- # Monochromator #------------------------------------------------------------- @@ -61,6 +68,7 @@ allowexec $home/dmcafter allowexec $home/dmc_sics05 ClientPut "Installing counter" + MakeCounter counter mcstas counter SetExponent 1 MakeHM banana mcstas @@ -74,7 +82,7 @@ banana CountMode Timer banana configure Counter counter banana configure init 0 banana init -banana exponent 3 +#banana exponent 3 #------------------------------------------------------------------------- # Aliases #------------------------------------------------------------------------- @@ -117,33 +125,251 @@ VarMake starttime Text User starttime "" VarMake SicsDataPrefix Text Internal SicsDataPrefix vdmc + #--------- make data number MakeDataNumber SicsDataNumber $home/DataNumber VarMake SicsDataPostFix Text Internal -SicsDataPostFix ".hdf" +SicsDataPostFix ".xml" VarMake Adress Text User VarMake phone Text User VarMake fax Text User VarMake email Text User VarMake sample_mur Float User +VarMake lastdatafile Text User +VarMake lastscancommand Text User +lastscancommand "unknown scan" #-------------------------------------------------------------------------- # P R O C E D U R E S #-------------------------------------------------------------------------- MakeDrive MakeBatchManager MakeNXScript +MakeRuenBuffer +#------------------------------------------------------------------------ +# simulated scanning for demo purposes +#----------------------------------------------------------------------- +MakeCounter lieselotte SIM -1 +MakeMultiCounter scanCter lieselotte +#------------------------------ +proc SICSValue {command} { + set txt [eval $command] + set l [split $txt =] + return [string trim [lindex $l 1]] +} +#----------------------------------------------------------------------- +proc scantransfer {} { + set FWHM 1.5 + set pos 5.33 + set height 700 + set stddev [expr $FWHM/2.354] + set ftmp [expr ([SICSValue a3] - $pos)/$stddev] + set count [expr 10 + $height*0.4*exp(-.5*$ftmp*$ftmp)] + set counti [expr int($count)] + append res [SICSValue "lieselotte gettime"] " " + append res $counti " " + for {set i 1} {$i < 7} {incr i} { + append res [SICSValue "lieselotte getmonitor $i"] " " + } + return $res +} +scancter transferscript scantransfer + +MakeScanCommand xxxscan scancter $home/dmc.hdd $home/recover.bin +MakePeakCenter xxxscan + #-------------------- initialize scripted commands source $home/vdmccom.tcl #-------------------- configure commandlog commandlog auto commandlog intervall 5 -#----------- enable sycamore -#InstallProtocolHandler -#InstallSinfox -#source sycFormat.tcl -#source /usr/lib/tcllib1.6.1/stooop/stooop.tcl -#namespace import stooop::* -#source sinfo.tcl -#source sycamore.tcl -#Publish sinfo Spy +#==================== install Hipadaba +proc hdbReadOnly {} { + error "Parameter is READ ONLY" +} +#------------------------------------ +proc maketwotheta {} { + set txt [TwoThetaD] + set l [split $txt =] + set start [string trim [lindex $l 1]] + for {set i 0} {$i < 400} {incr i } { + append result [expr $start + $i * .2] " " + } + return $result +} +#------------------------------------- +InstallHdb +MakeStateMon +MakeHdbQueue hdbqueue HdbQueue +hmake /instrument spy none +hsetprop /instrument type instrument +#-------- experiment +hmake /instrument/experiment spy none +hattach /instrument/experiment title title +hattach /instrument/experiment starttime starttime +hattach /instrument/experiment user user +hattach /instrument/experiment/user adress address +hattach /instrument/experiment/user phone phone +hattach /instrument/experiment/user email email +hattach /instrument/experiment comment1 comment1 +hattach /instrument/experiment comment2 comment2 +hattach /instrument/experiment comment3 comment3 +#------- SINQ +hmake /instrument/sinq spy none +hmake /instrument/sinq/proton_monitor internal int +hattach /instrument/sinq/proton_monitor counter 4 +#-------- monochromator +hmake /instrument/monochromator spy none +hattach /instrument/monochromator lambda wavelength +hsetprop /instrument/monochromator/wavelength priv user +hattach /instrument/monochromator OmegaM theta +hattach /instrument/monochromator TwoThetaM two_theta +hchain /instrument/monochromator/wavelength /instrument/monochromator/two_theta +hattach /instrument/monochromator MonoX x_translation +hattach /instrument/monochromator MonoY y_translation +hattach /instrument/monochromator MonoChi chi +hattach /instrument/monochromator MonoPhi phi +hattach /instrument/monochromator CurveM vertical_focusing +hmakescript /instrument/monochromator/d_value "mono dd" "mono dd" float +hsetprop /instrument/monochromator/d_value priv manager +hmakescript /instrument/monochromator/scattering_sense "mono ss" "mono ss" int +hsetprop /instrument/monochromator/scattering_sense priv manager + +#----------- sample +hmake /instrument/sample spy none +hmakescript /instrument/sample/name sample sample Text +hattach /instrument/sample Table rotation +hmake /instrument/sample/monitor internal int +hattach /instrument/sample/monitor counter 1 +hsetprop /instrument/sample/monitor priv internal +hsetprop /instrument/sample/monitor sicsdev histogrammemory +#---------- detector +hmake /instrument/detector spy none +hattach /instrument/detector TwoThetaD two_theta +hmakescript /instrument/detector/preset "banana preset" hdbReadOnly float +hsetprop /instrument/detector/preset priv internal +hmakescript /instrument/detector/countmode "banana countmode" hdbReadOnly text +hsetprop /instrument/detector/countmode priv internal +sicspoll add /instrument/detector/preset hdb 30 +sicspoll add /instrument/detector/countmode hdb 30 +hmake /instrument/detector/count_time internal float +hattach /instrument/detector/count_time counter -1 +#------------ commands +hmake /instrument/commands spy none +hcommand /instrument/commands/count count +hsetprop /instrument/commands/count type command +hsetprop /instrument/commands/count priv user +hmake /instrument/commands/count/mode user text +hsetprop /instrument/commands/count/mode values "monitor,timer" +hmake /instrument/commands/count/preset user float +hset /instrument/commands/count/preset 60000 +hset /instrument/commands/count/mode monitor +hcommand /instrument/commands/killfile killfile +hsetprop /instrument/commands/killfile type command +hsetprop /instrument/commands/killfile priv manager + +#------------- scan command +hcommand /instrument/commands/scan hdbscan +hsetprop /instrument/commands/scan type command +hsetprop /instrument/commands/scan priv user +hsetprop /instrument/commands/scan viewer mountaingumui.ScanEditor +hmake /instrument/commands/scan/scan_variables user text +hsetprop /instrument/commands/scan/scan_variables argtype drivable +hmake /instrument/commands/scan/scan_start user text +hmake /instrument/commands/scan/scan_increments user text +hmake /instrument/commands/scan/NP user int +hmake /instrument/commands/scan/mode user text +hsetprop /instrument/commands/scan/mode values "timer,monitor" +hmake /instrument/commands/scan/preset user float +hset /instrument/commands/scan/mode timer +hset /instrument/commands/scan/scan_start 2. +hset /instrument/commands/scan/scan_increments .3 +hset /instrument/commands/scan/NP 25 +hset /instrument/commands/scan/preset 2 + +hcommand /instrument/commands/wait wait +hsetprop /instrument/commands/wait type command +hsetprop /instrument/commands/wait priv user +hmake /instrument/commands/wait/time user int + +#---------------- graphics +hmake /graphics spy none +hmake /graphics/powder_diagram spy none +hattach /graphics/powder_diagram title title +hsetprop /graphics/powder_diagram type graphdata +hsetprop /graphics/powder_diagram viewer default +hmake /graphics/powder_diagram/rank internal int +hset /graphics/powder_diagram/rank 1 +hmake /graphics/powder_diagram/dim internal intar 1 +hset /graphics/powder_diagram/dim 400 +hmakescript /graphics/powder_diagram/two_theta maketwotheta hdbReadOnly floatar 400 +hchain /graphics/powder_diagram/two_theta /instrument/detector/two_theta +hsetprop /graphics/powder_diagram/two_theta type axis +hsetprop /graphics/powder_diagram/two_theta transfer zip +hsetprop /graphics/powder_diagram/two_theta dim 0 +hattach /graphics/powder_diagram banana counts +hsetprop /graphics/powder_diagram/counts type data +hsetprop /graphics/powder_diagram/counts transfer zip +hsetprop /graphics/powder_diagram/counts priv internal +sicspoll add /graphics/powder_diagram/counts hdb 60 + +hmake /graphics/scan_data spy none +hsetprop /graphics/scan_data type graphdata +hsetprop /graphics/scan_data viewer default +hmake /graphics/scan_data/rank mugger int +hset /graphics/scan_data/rank 1 +hsetprop /graphics/scan_data/rank priv internal +hmakescript /graphics/scan_data/dim "xxxscan np" hdbReadOnly intar 1 +hsetprop /graphics/scan_data/dim priv internal +hmakescript /graphics/scan_data/scan_variable "gethdbscanvardata 0" hdbReadOnly floatvarar 1 +hsetprop /graphics/scan_data/scan_variable type axis +hsetprop /graphics/scan_data/scan_variable dim 0 +hsetprop /graphics/scan_data/scan_variable transfer zip +hsetprop /graphics/scan_data/scan_variable priv internal +hmakescript /graphics/scan_data/counts "gethdbscancounts" hdbReadOnly intvarar 1 +hsetprop /graphics/scan_data/counts type data +hsetprop /graphics/scan_data/counts transfer zip +hsetprop /graphics/scan_data/counts priv internal + +hmake /graphics/samenv spy none +hsetprop /graphics/samenv type graphdata +hsetprop /graphics/samenv viewer mountaingumui.TimeSeries +hmake /graphics/samenv/vars user text +hset /graphics/samenv/vars tomato +hmake /graphics/samenv/rank user int +hset /graphics/samenv/rank 1 +hmake /graphics/samenv/dim user intar 1 +hset /graphics/samenv/dim 300 +hmake /graphics/samenv/getdata user text +hsetprop /graphics/samenv/getdata type logcommand +hsetprop /graphics/samenv/getdata datacom true +hmake /graphics/samenv/getdata/starttime spy text +hmake /graphics/samenv/getdata/endtime spy text + +hmake /batch spy none +hmakescript /batch/bufferlist listbatchfiles hdbReadOnly text +sicspoll add /batch/bufferlist hdb 30 +hmake /batch/commandtext spy text +hsetprop /batch/commandtext viewer mountaingumui.TextEdit +hsetprop /batch/commandtext commandtext true + + +hmake /gui spy none +hmake /gui/status internal text +status hdbinterest /gui/status + +proc makeQuickPar {name path} { + hmake /quickview/$name mugger text + hset /quickview/$name $path +} + +hmake /quickview spy none +makeQuickPar title /instrument/experiment/title +makeQuickPar sample /instrument/sample/name +makeQuickPar lambda /instrument/monochromator/wavelength +makeQuickPar two-theta /instrument/detector/two_theta +makeQuickPar preset /instrument/detector/preset +makeQuickPar monitor /instrument/sample/monitor + +restore + diff --git a/mcstas/dmc/vdmccom.tcl b/mcstas/dmc/vdmccom.tcl index 4500cfa0..00c2b020 100644 --- a/mcstas/dmc/vdmccom.tcl +++ b/mcstas/dmc/vdmccom.tcl @@ -15,11 +15,21 @@ if { [info exists vdmcinit] == 0 } { Publish copydmcdata User Publish sample User Publish wwwsics Spy + Publish wwwfilefornumber Spy mcinstall + Publish gethm Spy + Publish hdbscan User + Publish hdbprepare User + Publish hdbcollect User + Publish mgbatch Spy + Publish loadmgbatch Spy + Publish listbatchfiles Spy } source $home/log.tcl source $home/nxsupport.tcl source $home/nxdmc.tcl +source $home/gumxml.tcl +source $home/gumibatch.tcl #------------------------------------------------------------------------ proc SplitReply { text } { set l [split $text =] @@ -38,12 +48,19 @@ proc washlazy {name} { if { [string first "H K L THETA 2THETA D VALUE" $line] > 0} { break } +#-------- A second version to treat the messed up lazy pulverix files +# uploaded through the WWW-interface + if { [string first "H K L THETA" $line] >= 0} { + break + } } #------- process data lines puts $out "// mult Q(hkl) F2 DW w" + clientput "HKL found at: $line" while { [gets $in line] >= 0} { set num [scan $line "%d %d %d %f %f %f %f %f %d %d %d %f %f %f %f %f %d"\ h k l th th2 d di sin h2 k2 l2 I F A B ang mul] + clientput "Line = $num, $line" if { $num == 17} { set q [expr (2.*3.14159265358979323846)/$d] set f2 [expr $F * $F] @@ -155,6 +172,7 @@ proc rundmcoptsim {mode preset } { } else { return $msg } + wait 5 } #------------------------------------------------------------------------ proc copydmcdataold { } { @@ -183,8 +201,17 @@ proc copydmcdataold { } { proc copydmcdata { } { global home set mcversion "McStas 1.8 - Mar. 05, 2004" - washsimfile $home/dmc.xml - mcreader open $home/dmc.xml +#---- loop till the file can be opened + for {set i 0} {$i < 20} {incr i} { + washsimfile $home/dmc.xml + set stat [catch {mcreader open $home/dmc.xml} msg] + if {$stat == 0} { + break + } else { + file copy -force $home/dmc.xml $home/brokenfile.xml + wait 1 + } + } mcreader insertmon \ "/$mcversion/DMC_diff/dmc.xml/PSD_sample/values" \ counter 1 [expr 1./350] @@ -250,6 +277,7 @@ proc count { {mode NULL } { preset NULL } } { #------- count banana InitVal 0 wait 1 + hupdate /graphics/powder_diagram/counts banana count set ret [catch {Success} msg] #------- StoreData @@ -363,7 +391,7 @@ proc wwwpar {type mot} { } #------------- wwwuser formats user information into a html table proc wwwuser {} { - lappend list title sample user email phone adress + lappend list title user email phone adress append txt "" foreach e $list { set ret [catch {$e} msg] @@ -376,6 +404,101 @@ proc wwwuser {} { } return $txt } - - - +#------------- wwwfilefornumber returns the path to a data file for a +# number +proc wwwfilefornumber {num} { + return [makeSimForNum $num] +} +#------------------------------------------------------------------- +proc gethm {} { + banana uuget 0 +} +#-------------------------------------------------------------------- +proc hdbscan {scanvars scanstart scanincr np mode preset} { + xxxscan clear + xxxscan configure script + xxxscan function prepare hdbprepare + xxxscan function collect hdbcollect + set varlist [split $scanvars ,] + set startlist [split $scanstart ,] + set incrlist [split $scanincr ,] + set count 0 + foreach var $varlist { + if {[string first / $var] >= 0} { + set var [string trim [SplitReply [hgetprop $var sicsdev]]] + } + xxxscan add $var [lindex $startlist $count] [lindex $incrlist $count] + incr count + } + set status [catch {xxxscan run $np $mode $preset} msg] + if {$status == 0} { + return $msg + } else { + error $msg + } +} +#------------------------------------------------------------------------------ +proc hdbprepare {obj userdata } { + stdscan prepare $obj userdata + hupdate /graphics/scan_data/dim +} +#------------------------------------------------------------------------------ +proc hdbcollect {obj userobj np} { + stdscan collect $obj $userobj $np + hupdate /graphics/scan_data/scan_variable + hupdate /graphics/scan_data/counts +} +#----------------------------------------------------------------------------- +proc gethdbscanvardata {no} { + set np [string trim [SplitReply [xxxscan np]]] + if {$np == 0} { + return ".0 .0 .0" + } + set status [catch {SplitReply [xxxscan getvardata $no]} txt] + if {$status == 0} { + return [join $txt] + } else { + return ".0 .0 .0" + } +} +#---------------------------------------------------------------------------- +proc gethdbscancounts {} { + set np [string trim [SplitReply [xxxscan np]]] + if {$np == 0} { + return "0 0 0" + } + set status [catch {SplitReply [xxxscan getcounts]} txt] + if {$status == 0} { + return [join $txt] + } else { + return "0 0 0" + } +} +#================= helper to get the list of batch files ================= +proc listbatchfiles {} { + set ext [list *.tcl *.job] + set txt [SplitReply [exe batchpath]] + set dirlist [split $txt :] + set txt [SplitReply [exe syspath]] + set dirlist [concat $dirlist [split $txt :]] + set result [list ""] + foreach dir $dirlist { + foreach e $ext { + set status [catch {glob [string trim $dir]/$e} filetxt] + if {$status == 0} { + set filelist [split $filetxt] + foreach f $filelist { + set nam [file tail $f] + if { [lsearch $result $nam] < 0} { + lappend result $nam + } + } + } + } + } + foreach bf $result { + append resulttxt $bf , + } + return [string trim $resulttxt ,] +} +#----------------------------------------------------------------------- diff --git a/mcstas/dmc/vdmcstatus.tcl b/mcstas/dmc/vdmcstatus.tcl index 96c3854b..1b9a1bb9 100644 --- a/mcstas/dmc/vdmcstatus.tcl +++ b/mcstas/dmc/vdmcstatus.tcl @@ -1,3 +1,9 @@ +exe batchpath tmp +exe syspath ./ + +#--- BEGIN (commands producing errors on last restore) +#--- END (commands producing errors on last restore) + # Motor omegam omegam sign 1.000000 omegam SoftZero 0.000000 @@ -8,17 +14,21 @@ omegam InterruptMode 0.000000 omegam precision 0.010000 omegam ignorefault 0.000000 omegam AccessCode 2.000000 +omegam failafter 3.000000 +omegam maxretry 3.000000 omegam movecount 10.000000 # Motor twothetam twothetam sign 1.000000 twothetam SoftZero 0.000000 -twothetam SoftLowerLim 30.000000 +twothetam SoftLowerLim 35.000000 twothetam SoftUpperLim 100.000000 twothetam Fixed -1.000000 twothetam InterruptMode 0.000000 twothetam precision 0.010000 twothetam ignorefault 0.000000 twothetam AccessCode 2.000000 +twothetam failafter 3.000000 +twothetam maxretry 3.000000 twothetam movecount 10.000000 # Motor monox monox sign 1.000000 @@ -30,6 +40,8 @@ monox InterruptMode 0.000000 monox precision 0.010000 monox ignorefault 0.000000 monox AccessCode 2.000000 +monox failafter 3.000000 +monox maxretry 3.000000 monox movecount 10.000000 # Motor monoy monoy sign 1.000000 @@ -41,6 +53,8 @@ monoy InterruptMode 0.000000 monoy precision 0.010000 monoy ignorefault 0.000000 monoy AccessCode 2.000000 +monoy failafter 3.000000 +monoy maxretry 3.000000 monoy movecount 10.000000 # Motor curvem curvem sign 1.000000 @@ -52,6 +66,8 @@ curvem InterruptMode 0.000000 curvem precision 0.010000 curvem ignorefault 0.000000 curvem AccessCode 2.000000 +curvem failafter 3.000000 +curvem maxretry 3.000000 curvem movecount 10.000000 # Motor monophi monophi sign 1.000000 @@ -63,6 +79,8 @@ monophi InterruptMode 0.000000 monophi precision 0.010000 monophi ignorefault 0.000000 monophi AccessCode 2.000000 +monophi failafter 3.000000 +monophi maxretry 3.000000 monophi movecount 10.000000 # Motor monochi monochi sign 1.000000 @@ -74,17 +92,21 @@ monochi InterruptMode 0.000000 monochi precision 0.010000 monochi ignorefault 0.000000 monochi AccessCode 2.000000 +monochi failafter 3.000000 +monochi maxretry 3.000000 monochi movecount 10.000000 # Motor table table sign 1.000000 table SoftZero 0.000000 -table SoftLowerLim -180.000000 +table SoftLowerLim -360.000000 table SoftUpperLim 360.000000 table Fixed -1.000000 table InterruptMode 0.000000 table precision 0.010000 table ignorefault 0.000000 table AccessCode 2.000000 +table failafter 3.000000 +table maxretry 3.000000 table movecount 10.000000 # Motor twothetad twothetad sign 1.000000 @@ -96,9 +118,11 @@ twothetad InterruptMode 0.000000 twothetad precision 0.010000 twothetad ignorefault 0.000000 twothetad AccessCode 2.000000 +twothetad failafter 3.000000 +twothetad maxretry 3.000000 twothetad movecount 10.000000 # Counter counter -counter SetPreset 60000000.000000 +counter SetPreset 60000.000000 counter SetMode Monitor banana CountMode monitor banana preset 60000.000000 @@ -112,28 +136,34 @@ a1 InterruptMode 0.000000 a1 precision 0.010000 a1 ignorefault 0.000000 a1 AccessCode 2.000000 +a1 failafter 3.000000 +a1 maxretry 3.000000 a1 movecount 10.000000 # Motor a2 a2 sign 1.000000 a2 SoftZero 0.000000 -a2 SoftLowerLim 30.000000 +a2 SoftLowerLim 35.000000 a2 SoftUpperLim 100.000000 a2 Fixed -1.000000 a2 InterruptMode 0.000000 a2 precision 0.010000 a2 ignorefault 0.000000 a2 AccessCode 2.000000 +a2 failafter 3.000000 +a2 maxretry 3.000000 a2 movecount 10.000000 # Motor a3 a3 sign 1.000000 a3 SoftZero 0.000000 -a3 SoftLowerLim -180.000000 +a3 SoftLowerLim -360.000000 a3 SoftUpperLim 360.000000 a3 Fixed -1.000000 a3 InterruptMode 0.000000 a3 precision 0.010000 a3 ignorefault 0.000000 a3 AccessCode 2.000000 +a3 failafter 3.000000 +a3 maxretry 3.000000 a3 movecount 10.000000 # Motor a4 a4 sign 1.000000 @@ -145,6 +175,8 @@ a4 InterruptMode 0.000000 a4 precision 0.010000 a4 ignorefault 0.000000 a4 AccessCode 2.000000 +a4 failafter 3.000000 +a4 maxretry 3.000000 a4 movecount 10.000000 # Motor a5 a5 sign 1.000000 @@ -156,6 +188,8 @@ a5 InterruptMode 0.000000 a5 precision 0.010000 a5 ignorefault 0.000000 a5 AccessCode 2.000000 +a5 failafter 3.000000 +a5 maxretry 3.000000 a5 movecount 10.000000 # Motor a6 a6 sign 1.000000 @@ -167,6 +201,8 @@ a6 InterruptMode 0.000000 a6 precision 0.010000 a6 ignorefault 0.000000 a6 AccessCode 2.000000 +a6 failafter 3.000000 +a6 maxretry 3.000000 a6 movecount 10.000000 # Motor a7 a7 sign 1.000000 @@ -178,6 +214,8 @@ a7 InterruptMode 0.000000 a7 precision 0.010000 a7 ignorefault 0.000000 a7 AccessCode 2.000000 +a7 failafter 3.000000 +a7 maxretry 3.000000 a7 movecount 10.000000 # Motor a8 a8 sign 1.000000 @@ -189,6 +227,8 @@ a8 InterruptMode 0.000000 a8 precision 0.010000 a8 ignorefault 0.000000 a8 AccessCode 2.000000 +a8 failafter 3.000000 +a8 maxretry 3.000000 a8 movecount 10.000000 # Motor a9 a9 sign 1.000000 @@ -200,10 +240,12 @@ a9 InterruptMode 0.000000 a9 precision 0.010000 a9 ignorefault 0.000000 a9 AccessCode 2.000000 +a9 failafter 3.000000 +a9 maxretry 3.000000 a9 movecount 10.000000 -title D3C +title Lieselotte Nass title setAccess 2 -user UNKNOWN +user Lukas user setAccess 2 collimation UNKNOWN collimation setAccess 2 @@ -215,17 +257,25 @@ comment2 UNKNOWN comment2 setAccess 2 comment3 UNKNOWN comment3 setAccess 2 -starttime 2006-03-10 15:33:04 +starttime 2008-03-18 13:09:23 starttime setAccess 2 -adress UNKNOWN +adress 2223 Luketown, 33 Luke Drive adress setAccess 2 phone UNKNOWN phone setAccess 2 fax UNKNOWN fax setAccess 2 -email UNKNOWN +email Luke@luke.ch email setAccess 2 sample_mur 0.000000 sample_mur setAccess 2 -exe batchpath ./ -exe syspath ./ +lastdatafile /afs/psi.ch/user/k/koennecke/src/workspace/sics/mcstas/dmc/000/vdmc2008n000230.xml +lastdatafile setAccess 2 +lastscancommand unknown scan +lastscancommand setAccess 2 +# Counter lieselotte +lieselotte SetPreset 2.000000 +lieselotte SetMode Timer +# Counter scancter +scancter SetPreset 2.000000 +scancter SetMode Timer diff --git a/mcstascounter.c b/mcstascounter.c index e7f07269..adbf2c02 100644 --- a/mcstascounter.c +++ b/mcstascounter.c @@ -8,6 +8,7 @@ -----------------------------------------------------------------------------*/ #include #include +#include #include "sics.h" #include "countdriv.h" #include "mccontrol.h" @@ -76,6 +77,7 @@ static int McReadValues(struct __COUNTER *self){ if(status == HWFault){ self->iErrorCode = SCRIPTERROR; } + self->fTime = time(NULL) - pMcStas->startTime; return status; } /*--------------------------------------------------------------------------*/ diff --git a/mesure.c b/mesure.c index 771864fd..5257678a 100644 --- a/mesure.c +++ b/mesure.c @@ -32,6 +32,8 @@ #include "nxscript.h" #include "fourtable.h" #include "lld.h" +#include "stdscan.h" +#include "exeman.h" extern void SNXFormatTime(char *pBueffel, int iLen); extern float nintf(float f); @@ -65,6 +67,7 @@ int iLogFile; /* log file num at connection */ SConnection *pCon; /* log file owning connection */ char *pCurrentFile; /* current file root */ + char headerTemplate[512]; int iCount; /* count of reflection */ int CountMode; /* timer or preset */ int np; /* number of scan points */ @@ -146,7 +149,7 @@ static void ListMesure(pMesure self, char *name, SConnection *pCon) pMesure CreateMesure(pHKL pCryst, pScanData pScanner, pMotor pOmega, char *pOm, pMotor p2Theta, char *p2t, char *pFileRoot, - pDataNumber pDanu) + pDataNumber pDanu, char *hdTemplate) { pMesure pNew = NULL; @@ -197,7 +200,8 @@ static void ListMesure(pMesure self, char *name, SConnection *pCon) pNew->lCounts = (long *)malloc(90*sizeof(long)); #endif pNew->lCounts = (long *)malloc(50*sizeof(long)); - pNew->stepTable = MakeFourCircleTable(); + pNew->stepTable = MakeFourCircleTable(); + strncpy(pNew->headerTemplate,hdTemplate,511); return pNew; } /*------------------------------------------------------------------------*/ @@ -249,7 +253,7 @@ static void ListMesure(pMesure self, char *name, SConnection *pCon) /* check no of parameters inicom name hkl scan omega root danu */ - if(argc < 8) + if(argc < 9) { SCWrite(pCon, "ERROR: Insufficient number of parameters to MesureFactory",eError); @@ -336,7 +340,7 @@ static void ListMesure(pMesure self, char *name, SConnection *pCon) /* finally create the thing */ pNew = CreateMesure(pCryst,pScan,pMot,argv[4], pMot2, argv[5], - argv[6],pDanu); + argv[6],pDanu,argv[8]); if(!pNew) { SCWrite(pCon,"ERROR: no memory in MesureFactory",eError); @@ -442,7 +446,9 @@ static int MesureCalculateSettings(pMesure self, float fHKL[3], float fSet[4], float fPsi, SConnection *pCon) { int status, np; - float step, tolerance; + float step, tolerance, fHard; + char *scanvar = NULL; + char buffer[256]; SetHKLScanTolerance(self->pCryst,.0); status = CalculateSettings(self->pCryst,fHKL,fPsi,0,fSet,pCon); @@ -457,7 +463,26 @@ static int MesureCalculateSettings(pMesure self, float fHKL[3], float fSet[4], np = getMesureNP(self,(double)fSet[0]); tolerance = (step * (float)np)/2. + .2; SetHKLScanTolerance(self->pCryst,tolerance); - return CalculateSettings(self->pCryst,fHKL,fPsi,0,fSet,pCon); + status = CalculateSettings(self->pCryst,fHKL,fPsi,0,fSet,pCon); + if(status != 1){ + return status; + } + scanvar = GetFourCircleScanVar(self->stepTable,fSet[0]); + if(scanvar != NULL && strcmp(scanvar,"om") != 0){ + tolerance *= 2.; + strcpy(buffer,"ERROR: 2theta limit problem:"); + if(!MotorCheckBoundary(self->p2Theta,fSet[0]-tolerance,&fHard, + buffer,256-strlen(buffer))){ + SCWrite(pCon,buffer,eWarning); + return 0; + } + if(!MotorCheckBoundary(self->p2Theta,fSet[0]+tolerance,&fHard, + buffer,256-strlen(buffer))){ + SCWrite(pCon,buffer,eWarning); + return 0; + } + } + return status; } /*--------------------------------------------------------------------------*/ int MesureReflection(pMesure self, float fHKL[3], float fPsi, @@ -759,7 +784,7 @@ static int ScanReflection(pMesure self, float twoTheta, SConnection *pCon) char *pFile = NULL, *pPtr; float zero, pos; pMotor pMot = NULL; - + FILE *temp = NULL; assert(self); assert(pCon); @@ -813,6 +838,19 @@ static int ScanReflection(pMesure self, float twoTheta, SConnection *pCon) SCSetInterrupt(pCon,eAbortBatch); return 0; } + temp = fopen(self->headerTemplate,"r"); + if(temp == NULL) + { + SCWrite(pCon,"ERROR: failed to open header template",eError); + } + if(temp != NULL && self->fRefl != NULL) + { + WriteTemplate(self->fRefl, temp, pFilename, NULL, + pCon, pServ->pSics); + fclose(temp); + } + + /* open hkl-data file */ strcpy(pFilename,pRoot); @@ -855,59 +893,6 @@ static int ScanReflection(pMesure self, float twoTheta, SConnection *pCon) sprintf(pBuff,"user = %s",pVar->text); strcat(pBueffel,pBuff); } - fprintf(self->fRefl,"%s\n",pBueffel); - fprintf(self->fRefl,"lambda = %f Angstroem\n",fVal); - fprintf(self->fRefl, - "UB = %7.6f %7.6f %7.6f %7.6f %7.6f %7.6f %7.6f %7.6f %7.6f\n", - fUB[0], fUB[1],fUB[2],fUB[3],fUB[4],fUB[5],fUB[6],fUB[7],fUB[8]); - - - /* - * zero points for Juerg - */ - pBueffel[0] = '\0'; - if(self->pCryst->pTheta != NULL){ - MotorGetPar(self->pCryst->pTheta,"softzero",&zero); - snprintf(pBuff,131,"stt.zero = %f ", zero); - strcat(pBueffel,pBuff); - } - if(self->pCryst->pOmega != NULL){ - MotorGetPar(self->pCryst->pOmega,"softzero",&zero); - snprintf(pBuff,131,"om.zero = %f ", zero); - strcat(pBueffel,pBuff); - } - if(self->pCryst->pChi != NULL){ - MotorGetPar(self->pCryst->pChi,"softzero",&zero); - snprintf(pBuff,131,"chi.zero = %f ", zero); - strcat(pBueffel,pBuff); - } - if(self->pCryst->pPhi != NULL){ - MotorGetPar(self->pCryst->pPhi,"softzero",&zero); - snprintf(pBuff,131,"phi.zero = %f ", zero); - strcat(pBueffel,pBuff); - } - fprintf(self->fRefl,"%s\n",pBueffel); - - /* - * CEX motors .... - */ - pBueffel[0] = '\0'; - pMot = FindCommandData(pServ->pSics,"cex1","Motor"); - if(pMot != NULL){ - MotorGetSoftPosition(pMot,pCon,&pos); - snprintf(pBuff,131,"cex1 = %f ",pos); - strcat(pBueffel,pBuff); - } - pMot = FindCommandData(pServ->pSics,"cex2","Motor"); - if(pMot != NULL){ - MotorGetSoftPosition(pMot,pCon,&pos); - snprintf(pBuff,131,"cex2 = %f ",pos); - strcat(pBueffel,pBuff); - } - if(strlen(pBueffel) > 1) { - fprintf(self->fRefl,"%s\n",pBueffel); - } - return 1; } /*---------------------------------------------------------------------------*/ @@ -1002,14 +987,34 @@ static int ScanReflection(pMesure self, float twoTheta, SConnection *pCon) return 1; } +/*---------------------------------------------------------------------------*/ +static double getProtonAverage(pMesure self){ + int np, i; + long *lData = NULL, lSum = 0; + + np = GetScanNP(self->pScanner); + lData = (long *)malloc((np+1)*sizeof(long)); + if(lData == NULL || np == 0){ + return 0.; + } + memset(lData,0,(np+1)*sizeof(long)); + GetScanMonitor(self->pScanner,2,lData, np); + for(i = 0; i < np; i++){ + lSum += lData[i]; + } + return (double)lSum/(double)np; +} /*---------------------------------------------------------------------------*/ static int WriteReflection(pMesure self, float fHKL[3],SConnection *pCon) { float fSum, fSigma, fSet[4], fTemp, fPreset, fStep; + double prot; static float fMax = 10.; int iRet, i,ii, iLF, iNP; char pBueffel[512], pNum[10], pTime[132]; - pEVControl pEva = NULL; + pEVControl pEva = NULL; + pDummy pPtr = NULL; + pIDrivable pDriv = NULL; assert(self); assert(pCon); @@ -1095,7 +1100,20 @@ static int ScanReflection(pMesure self, float twoTheta, SConnection *pCon) fTemp = -777.77; pEva = (pEVControl)FindCommandData(pServ->pSics,"temperature", "Environment Controller"); - if(pEva) + if(pEva == NULL) + { + pPtr = (pDummy)FindCommandData(pServ->pSics,"temperature", + "RemObject"); + if(pPtr != NULL) + { + pDriv = pPtr->pDescriptor->GetInterface(pPtr,DRIVEID); + if(pDriv != NULL) + { + fTemp = pDriv->GetValue(pPtr,pCon); + } + } + } + else { iRet = EVCGetPos(pEva, pCon,&fTemp); } @@ -1107,8 +1125,9 @@ static int ScanReflection(pMesure self, float twoTheta, SConnection *pCon) SNXFormatTime(pBueffel,512); GetScanVarStep(self->pScanner,0,&fStep); fPreset = GetScanPreset(self->pScanner); - fprintf(self->fRefl,"%3d %7.4f %9.0f %7.3f %s\n",iNP,fStep, - fPreset,fTemp,pBueffel); + prot = getProtonAverage(self); + fprintf(self->fRefl,"%3d %7.4f %9.0f %7.3f %12f %s\n",iNP,fStep, + fPreset,fTemp,prot, pBueffel); for(i = 0; i < iNP; i++) { for(ii = 0; ii < 10 && i < iNP; ii++) @@ -1157,6 +1176,19 @@ static int ScanReflection(pMesure self, float twoTheta, SConnection *pCon) } return 1; } + /*---------------------------------------------------------------------*/ + static FILE *openListFile(char *pName){ + FILE *fd = NULL; + pDynString filename = NULL; + filename = findBatchFile(pServ->pSics,pName); + if(filename != NULL){ + fd = fopen(GetCharArray(filename),"r"); + DeleteDynString(filename); + } else { + fd = fopen(pName,"r"); + } + return fd; + } /*------------------------------------------------------------------------*/ int MesureFile(pMesure self, char *pFile, int iSkip, SConnection *pCon) { @@ -1169,7 +1201,7 @@ static int ScanReflection(pMesure self, float twoTheta, SConnection *pCon) assert(pCon); /* well before doing a thing, open the list file */ - fd = fopen(pFile,"r"); + fd = openListFile(pFile); if(!fd) { sprintf(pBueffel,"ERROR: reflection file %s NOT found!",pFile); @@ -1258,7 +1290,7 @@ static int ScanReflection(pMesure self, float twoTheta, SConnection *pCon) assert(pCon); /* well before doing a thing, open the list file */ - fd = fopen(pFile,"r"); + fd = openListFile(pFile); if(!fd) { sprintf(pBueffel,"ERROR: reflection file %s NOT found!",pFile); @@ -1317,7 +1349,7 @@ static int ScanReflection(pMesure self, float twoTheta, SConnection *pCon) assert(pCon); /* well before doing a thing, open the list file */ - fd = fopen(pFile,"r"); + fd = openListFile(pFile); if(!fd) { sprintf(pBueffel,"ERROR: reflection file %s NOT found!",pFile); diff --git a/mesure.h b/mesure.h index 9d89f607..295e3dca 100644 --- a/mesure.h +++ b/mesure.h @@ -17,8 +17,8 @@ /*--------------------- live & death --------------------------------------*/ pMesure CreateMesure(pHKL pCryst, pScanData pScanner, pMotor pOmega, char *pom, - pMotor p2Theta, char *p2t, - char *pFileRoot,pDataNumber pDanu); + pMotor p2Theta, char *p2t, + char *pFileRoot,pDataNumber pDanu, char *headerTemplate); void DeleteMesure(void *pData); int MesureFactory(SConnection *pCon, SicsInterp *pSics, void *pData, diff --git a/mesure.tex b/mesure.tex index 68dd6121..e9ab4dbd 100644 --- a/mesure.tex +++ b/mesure.tex @@ -30,7 +30,8 @@ $\langle$mesureint {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@/*--------------------- live & death --------------------------------------*/@\\ \mbox{}\verb@ pMesure CreateMesure(pHKL pCryst, pScanData pScanner, @\\ \mbox{}\verb@ pMotor pOmega, char *pom,@\\ -\mbox{}\verb@ char *pFileRoot,pDataNumber pDanu);@\\ +\mbox{}\verb@ pMotor p2Theta, char *p2t,@\\ +\mbox{}\verb@ char *pFileRoot,pDataNumber pDanu, char *headerTemplate);@\\ \mbox{}\verb@ void DeleteMesure(void *pData);@\\ \mbox{}\verb@@\\ \mbox{}\verb@ int MesureFactory(SConnection *pCon, SicsInterp *pSics, void *pData,@\\ @@ -117,11 +118,13 @@ $\langle$fourtableint {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@@\\ \mbox{}\verb@ int MakeFourCircleTable();@\\ \mbox{}\verb@ void DeleteFourCircleTable(int handle);@\\ -\mbox{}\verb@ int HandleFourCircleCommands(int handle, SConnection *pCon, @\\ +\mbox{}\verb@ int HandleFourCircleCommands(int *handle, SConnection *pCon, @\\ \mbox{}\verb@ int argc, char *argv[], int *err);@\\ \mbox{}\verb@ char *GetFourCircleScanVar(int handle, double two_theta);@\\ \mbox{}\verb@ double GetFourCircleStep(int handle, double two_theta);@\\ \mbox{}\verb@ int SaveFourCircleTable(int handle, char *objName, FILE *fd);@\\ +\mbox{}\verb@ float GetFourCirclePreset(int handle, double twoTheta);@\\ +\mbox{}\verb@ int GetFourCircleScanNP(int handle, double twoTheta);@\\ \mbox{}\verb@@$\diamond$ \end{list} \vspace{-1ex} diff --git a/mesure.w b/mesure.w index 3806ad08..3ef463e4 100644 --- a/mesure.w +++ b/mesure.w @@ -26,7 +26,7 @@ The interface to this object consists of these functions: pMesure CreateMesure(pHKL pCryst, pScanData pScanner, pMotor pOmega, char *pom, pMotor p2Theta, char *p2t, - char *pFileRoot,pDataNumber pDanu); + char *pFileRoot,pDataNumber pDanu, char *headerTemplate); void DeleteMesure(void *pData); int MesureFactory(SConnection *pCon, SicsInterp *pSics, void *pData, @@ -100,11 +100,13 @@ interface to this module is: @d fourtableint @{ int MakeFourCircleTable(); void DeleteFourCircleTable(int handle); - int HandleFourCircleCommands(int handle, SConnection *pCon, + int HandleFourCircleCommands(int *handle, SConnection *pCon, int argc, char *argv[], int *err); char *GetFourCircleScanVar(int handle, double two_theta); double GetFourCircleStep(int handle, double two_theta); int SaveFourCircleTable(int handle, char *objName, FILE *fd); + float GetFourCirclePreset(int handle, double twoTheta); + int GetFourCircleScanNP(int handle, double twoTheta); @} Many functions takes as the first argument a handle to the four circle table as created by MakeFourCircleTable. diff --git a/modriv.h b/modriv.h index 536243bd..2b7cd1b9 100644 --- a/modriv.h +++ b/modriv.h @@ -73,5 +73,6 @@ /* ----------------------- Simulation -----------------------------------*/ MotorDriver *CreateSIM(SConnection *pCon, int argc, char *argv[]); void KillSIM(void *pData); + MotorDriver *RGMakeMotorDriver(void); #endif diff --git a/moregress.c b/moregress.c new file mode 100644 index 00000000..de86e59b --- /dev/null +++ b/moregress.c @@ -0,0 +1,258 @@ +/** + * This is a regression testing motor driver for SICS. + * A parameter can be set which makes this driver cause + * various error conditions. This can then be used to + * verify and debug the working of upper level code + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, July 2007 + */ +#include +#include +#include + +/*===================== supported errors ======================*/ +#define NONE 0 +#define STARTFAIL 1 +#define BADPOS 2 /* positioning problem */ +#define FAIL 3 /* failure */ +#define OFFPOS 4 /* off pos by .2 */ +#define READFAIL 5 +#define RUN 6 /* keep running; for interrupt testing */ +/*=============================================================*/ +typedef struct __RGMoDriv{ + /* general motor driver interface + fields. REQUIRED! + */ + float fUpper; /* upper limit */ + float fLower; /* lower limit */ + char *name; + int (*GetPosition)(void *self, float *fPos); + int (*RunTo)(void *self,float fNewVal); + int (*GetStatus)(void *self); + void (*GetError)(void *self, int *iCode, char *buffer, int iBufLen); + int (*TryAndFixIt)(void *self, int iError,float fNew); + int (*Halt)(void *self); + int (*GetDriverPar)(void *self, char *name, + float *value); + int (*SetDriverPar)(void *self,SConnection *pCon, + char *name, float newValue); + void (*ListDriverPar)(void *self, char *motorName, + SConnection *pCon); + void (*KillPrivate)(void *self); + /* your drivers private fields follow below */ + float target; + int errorType; + int recover; + int counter; + } RGMotorDriver; + +/*================================================================ + GetPos returns OKOK on success, HWFault on failure +------------------------------------------------------------------*/ +static int RGGetPos(void *data, float *fPos){ + RGMotorDriver *self = NULL; + + self = (RGMotorDriver *)data; + if(self->errorType == READFAIL){ + return HWFault; + } + if(self->errorType > 1 && self->errorType < 6){ + *fPos = self->target - .2; + } else { + *fPos = self->target; + } + return OKOK; +} +/*---------------------------------------------------------------- + RunTo starts the motor running. Returns OKOK on success, HWfault + on Errors +------------------------------------------------------------------*/ +static int RGRunTo(void *data, float newValue){ + RGMotorDriver *self = NULL; + + self = (RGMotorDriver *)data; + self->target = newValue; + if(self->errorType == STARTFAIL){ + return HWFault; + } + return OKOK; +} +/*----------------------------------------------------------------- + CheckStatus queries the sattus of a running motor. Possible return + values can be: + HWBusy : motor still running + HWFault : motor error detected + HWPosFault : motor finished, but position not reached + HWIdle : motor finished OK + HWWarn : motor issued warning +--------------------------------------------------------------------*/ +static int RGCheckStatus(void *data){ + RGMotorDriver *self = NULL; + + self = (RGMotorDriver *)data; + switch(self->errorType){ + case BADPOS: + return HWPosFault; + break; + case FAIL: + return HWFault; + break; + case RUN: + return HWBusy; + break; + } + return HWIdle; +} +/*------------------------------------------------------------------ + GetError gets more information about error which occurred + *iCode is an integer error code to be used in TryFixIt as indicator + buffer is a buffer for a text description of the problem + iBufLen is the length of buffer +--------------------------------------------------------------------*/ +static void RGGetError(void *data, int *iCode, char *buffer, + int iBufLen){ + RGMotorDriver *self = NULL; + + self = (RGMotorDriver *)data; + *iCode = self->errorType; + switch(self->errorType){ + case NONE: + strncpy(buffer,"No error found",iBufLen); + break; + case BADPOS: + strncpy(buffer,"Position not reached",iBufLen); + break; + case FAIL: + strncpy(buffer,"Hardware is mad",iBufLen); + break; + case STARTFAIL: + strncpy(buffer,"Failed to start motor",iBufLen); + break; + case READFAIL: + strncpy(buffer,"Failed to read motor",iBufLen); + break; + + } +} +/*------------------------------------------------------------------ + TryAndFixIt tries everything which is possible in software to fix + a problem. iError is the error code from GetError, newValue is + the target value for the motor + Possible retrun values are: + MOTOK : everything fixed + MOTREDO : try again + MOTFAIL : cannot fix this +--------------------------------------------------------------------*/ +static int RGFixIt(void *data, int iError, float newValue){ + RGMotorDriver *self = NULL; + + self = (RGMotorDriver *)data; + if(self->recover == 1){ + self->errorType = NONE; + return MOTREDO; + } + return MOTFAIL; +} +/*------------------------------------------------------------------- + Halt tries to stop the motor. Halt errors are ignored +---------------------------------------------------------------------*/ +static int RGHalt(void *data){ + RGMotorDriver *self = NULL; + + self = (RGMotorDriver *)data; + self->errorType = NONE; + return 1; +} +/*-------------------------------------------------------------------- + GetDriverPar retrieves the value of a driver parameter. + Name is the name of the parameter, fValue the value when found. + Returns 0 on success, 0 else +-----------------------------------------------------------------------*/ +static int RGGetDriverPar(void *data, char *name, float *value){ + RGMotorDriver *self = NULL; + + self = (RGMotorDriver *)data; + if(strcmp(name,"errortype") == 0){ + *value = (float)self->errorType; + return 1; + } else if (strcmp(name,"recover") == 0){ + *value = self->recover; + return 1; + } + + return 0; +} +/*---------------------------------------------------------------------- + SetDriverPar sets a driver parameter. Returns 0 on failure, 1 on + success. Name is the parameter name, pCon the connection to report + errors too, value the new value +------------------------------------------------------------------------*/ +static int RGSetDriverPar(void *data, SConnection *pCon, + char *name, float value){ + RGMotorDriver *self = NULL; + + self = (RGMotorDriver *)data; + if(strcmp(name,"errortype") == 0){ + self->errorType = (int)value; + return 1; + } else if (strcmp(name,"recover") == 0){ + self->recover = (int)value; + return 1; + } + return 0; +} +/*----------------------------------------------------------------------- + ListDriverPar lists the names and values of driver parameters to + pCon. Motorname is the name of the motor ro prefix to the listing. +-------------------------------------------------------------------------*/ +static void RGListDriverPar(void *data, char *motorname, + SConnection *pCon){ + RGMotorDriver *self = NULL; + char buffer[256]; + + self = (RGMotorDriver *)data; + snprintf(buffer,255,"%s errortype = %d", motorname, + self->errorType); + SCWrite(pCon,buffer,eValue); + + snprintf(buffer,255,"%s recover = %d", motorname, + self->recover); + SCWrite(pCon,buffer,eValue); +} +/*----------------------------------------------------------------------- + KillPrivate has the task to delete possibly dynamically allocated + memory in the private part of the driver structure +------------------------------------------------------------------------*/ +static void RGKillPrivate(void *data){ + RGMotorDriver *self = NULL; + + self = (RGMotorDriver *)data; +} +/*=======================================================================*/ +MotorDriver *RGMakeMotorDriver(void) { + RGMotorDriver *pNew = NULL; + + pNew = malloc(sizeof(RGMotorDriver)); + if(pNew == NULL){ + return NULL; + } + memset(pNew,0,sizeof(RGMotorDriver)); + + pNew->GetPosition = RGGetPos; + pNew->RunTo = RGRunTo; + pNew->GetStatus = RGCheckStatus; + pNew->GetError = RGGetError; + pNew->TryAndFixIt = RGFixIt; + pNew->Halt = RGHalt; + pNew->GetDriverPar = RGGetDriverPar; + pNew->SetDriverPar = RGSetDriverPar; + pNew->ListDriverPar = RGListDriverPar; + pNew->KillPrivate = RGKillPrivate; + pNew->fLower = -180.; + pNew->fUpper = 180.; + + return (MotorDriver *)pNew; +} + diff --git a/motor.c b/motor.c index 08c2d105..3258b07a 100644 --- a/motor.c +++ b/motor.c @@ -76,13 +76,6 @@ #define IGNOREFAULT 10 #define MOVECOUNT 11 -/*------------------------------------------------------------------------ - a tiny structure used in CallBack work -*/ - typedef struct { - float fVal; - char *pName; - } MotCallback; /*-------------------------------------------------------------------------*/ static void *MotorGetInterface(void *pData, int iID) @@ -142,7 +135,7 @@ float fVal = 0.; assert(pData); - iRet = MotorGetHardPosition((pMotor)pData,pCon,&fVal); + iRet = MotorGetSoftPosition((pMotor)pData,pCon,&fVal); if(iRet != OKOK) { fVal = -9999999.99; @@ -178,8 +171,10 @@ fputs(pBueffel,fd); sprintf(pBueffel,"%s AccessCode %f\n",name,ObVal(self->ParArray,USRIGHTS)); fputs(pBueffel,fd); - sprintf(pBueffel,"%s poscount %f\n",name, - ObVal(self->ParArray,POSCOUNT)); + sprintf(pBueffel,"%s failafter %f\n",name,ObVal(self->ParArray,ECOUNT)); + fputs(pBueffel,fd); + sprintf(pBueffel,"%s maxretry %f\n",name,ObVal(self->ParArray,POSCOUNT)); + fputs(pBueffel,fd); sprintf(pBueffel,"%s movecount %f\n",name, ObVal(self->ParArray,MOVECOUNT)); fputs(pBueffel,fd); @@ -262,6 +257,8 @@ static void finishDriving(pMotor self, SConnection *pCon) MotCallback sCall; MotorGetSoftPosition(self,pCon,&sCall.fVal); sCall.pName = self->name; + self->fPosition = sCall.fVal; + self->fPosition = sCall.fVal; InvokeCallBack(self->pCall, MOTDRIVE, &sCall); /* send also very last position */ InvokeCallBack(self->pCall, MOTEND, &sCall); } @@ -336,7 +333,7 @@ static int evaluateStatus(pMotor self, SConnection *pCon) newStatus = checkPosition(self,pCon); if(newStatus != HWBusy) { - finishDriving(self,pCon); + finishDriving(self,pCon); } } break; @@ -357,6 +354,7 @@ static int evaluateStatus(pMotor self, SConnection *pCon) } if(newStatus == HWFault) { + finishDriving(self,pCon); MotorInterrupt(pCon,ObVal(self->ParArray,INT)); self->retryCount = 0; } @@ -416,7 +414,7 @@ static void handleMoveCallback(pMotor self, SConnection *pCon) /* create and initialize parameters */ - pM->ParArray = ObParCreate(12); + pM->ParArray = ObParCreate(MOTOBPARLENGTH); if(!pM->ParArray) { free(pM); @@ -492,6 +490,8 @@ extern void KillPiPiezo(void *pData); pM = (pMotor)self; + /* MotorHalt(pM); */ + if(pM->name) free(pM->name); @@ -604,6 +604,7 @@ extern void KillPiPiezo(void *pData); if(iRet == 1) { SCparChange(pCon); + InvokeCallBack(self->pCall,HDBVAL,self); return iRet; } } @@ -629,6 +630,7 @@ extern void KillPiPiezo(void *pData); fLimit -= fChange; ObParSet(self->ParArray,self->name,"softlowerlim",fLimit,pCon); SCparChange(pCon); + InvokeCallBack(self->pCall,HDBVAL,self); return 1; } @@ -650,6 +652,7 @@ extern void KillPiPiezo(void *pData); ObParInit(self->ParArray,SZERO,"softzero",ZEROINACTIVE,usUser); } } + InvokeCallBack(self->pCall,HDBVAL,self); SCparChange(pCon); return iRet; @@ -745,10 +748,11 @@ extern void KillPiPiezo(void *pData); } /* check boundaries first */ - iRet = MotorCheckBoundary(self,fNew,&fHard,pBueffel,511); + iRet = MotorCheckBoundary(self,fNew,&fHard,pError,131); if(!iRet) { - SCWrite(pCon,pBueffel,eStatus); + snprintf(pBueffel,511,"ERROR: %s",pError); + SCWrite(pCon,pBueffel,eError); SCSetInterrupt(pCon,eAbortOperation); return 0; } @@ -782,6 +786,7 @@ extern void KillPiPiezo(void *pData); self->retryCount = 0; self->stopped = 0; self->fTarget = fHard; + InvokeCallBack(self->pCall,HDBVAL,self); self->posCount = 0; iRet = self->pDriver->RunTo(self->pDriver,fHard); if(iRet != OKOK) @@ -1022,7 +1027,22 @@ extern MotorDriver *MakePiPiezo(Tcl_Interp *pTcl, char *pArray); SCWrite(pCon,pBueffel,eError); return 0; } - } + } else if(strcmp(argv[2],"regress") == 0) + { + pDriver = RGMakeMotorDriver(); + if(!pDriver) + { + return 0; + } + /* create the motor */ + pNew = MotorInit("regress",argv[1],pDriver); + if(!pNew) + { + sprintf(pBueffel,"Failure to create motor %s",argv[1]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + } else { site = getSite(); diff --git a/motor.h b/motor.h index 67d61d74..277afb1c 100644 --- a/motor.h +++ b/motor.h @@ -14,6 +14,7 @@ #include "obdes.h" #include "interface.h" +#define MOTOBPARLENGTH 12 typedef struct __Motor { pObjectDescriptor pDescriptor; ObPar *ParArray; @@ -32,6 +33,13 @@ int stopped; } Motor; typedef Motor *pMotor; +/*------------------------------------------------------------------------ + a tiny structure used in CallBack work +--------------------------------------------------------------------------*/ +typedef struct { + float fVal; + char *pName; + } MotCallback; /*-------------------------------------------------------------------------*/ /* parameter management */ diff --git a/motorlist.c b/motorlist.c index 8dc4db9f..60c3ff77 100644 --- a/motorlist.c +++ b/motorlist.c @@ -83,7 +83,7 @@ static long MOLISetValue(void *data, SConnection *pCon, float val){ return test; } else { tuktuk.running = 1; - LLDnodeDataFrom(self,&tuktuk); + LLDnodeDataFrom(self,&tuktuk); } iRet = LLDnodePtr2Next(self); } diff --git a/multicounter.c b/multicounter.c new file mode 100644 index 00000000..674fd3c3 --- /dev/null +++ b/multicounter.c @@ -0,0 +1,426 @@ +/** + * The MultiCounter is another counter which coordinates multiple + * counting objects, counters and histogram memories. It also calls a + * script function after TransferData which collects counters and monitors. + * The purpose is to have a flexible counter abstraction for upper level + * code such as maximizers and scan functions. The script can deal with + * counting on monitors or on sums of histogram memories. + * + * This is a bit unclean. The counter driver is of no use, therefore its + * private data structure is used to hold the other counters and the name + * of the script. It would have been better to inherit from counter but + * that would have required lost of type casts. I am to lazy for this. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, September 2006 + */ +#include +#include +#include +#include +#include "multicounter.h" +#include "counter.h" +#include "HistMem.h" +#include "macro.h" +#include "splitter.h" + +#define MAXSLAVE 16 +#define NOCOUNTERS -2727 +/*=============== code for the driver ======================================*/ +typedef struct { + void *slaveData[MAXSLAVE]; + pICountable slaves[MAXSLAVE]; + char *transferScript; + int nSlaves; +}MultiCounter, *pMultiCounter; +/*--------------------------------------------------------------------------*/ +static void KillMultiDriver(struct __COUNTER *data){ + pMultiCounter self = (pMultiCounter)data->pData; + if(self == NULL){ + return; + } + if(self->transferScript != NULL){ + free(self->transferScript); + } + free(self); +} +/*============== countable interface functions ============================*/ +static int MMCCHalt(void *pData){ + int i, retVal = OKOK, status; + pCounter pCount = NULL; + pMultiCounter self = NULL; + + pCount = (pCounter)pData; + if(pCount != NULL){ + self = (pMultiCounter)pCount->pDriv->pData; + } + assert(self); + + for(i = 0; i < self->nSlaves; i++){ + status = self->slaves[i]->Halt(self->slaveData[i]); + if(status != OKOK) + retVal = status; + } + return retVal; +} +/*-------------------------------------------------------------------------*/ +static int MMCCStart(void *pData, SConnection *pCon) +{ + int i, status; + pCounter pCount = NULL; + pMultiCounter self = NULL; + + pCount = (pCounter)pData; + if(pCount != NULL){ + self = (pMultiCounter)pCount->pDriv->pData; + } + assert(self); + + for(i = 0; i < self->nSlaves; i++){ + self->slaves[i]->SetCountParameters(self->slaveData[i], + pCount->pDriv->fPreset, pCount->pDriv->eMode); + status = self->slaves[i]->StartCount(self->slaveData[i],pCon); + if(status != OKOK){ + MMCCHalt(pData); + return status; + } + } + pCount->isUpToDate = 0; + pCount->tStart = time(NULL); + InvokeCallBack(pCount->pCall,COUNTSTART,pCon); + return OKOK; +} +/*-------------------------------------------------------------------------*/ +static int MMCCStatus(void *pData, SConnection *pCon){ + int status,i; + pCounter pCount = NULL; + pMultiCounter self = NULL; + pDummy pDum = NULL; + + pCount = (pCounter)pData; + if(pCount != NULL){ + self = (pMultiCounter)pCount->pDriv->pData; + } + assert(self); + + if(self->nSlaves == 0) { + pCount->pDriv->iErrorCode = NOCOUNTERS; + return HWFault; + } + + status = self->slaves[0]->CheckCountStatus(self->slaveData[0],pCon); + if(status == HWIdle || status == HWFault){ + /* + stop counting on slaves when finished or when an error + occurred. + */ + InvokeCallBack(pCount->pCall,COUNTEND,pCon); + MMCCHalt(pCount); + } + for(i = 1; i < MAXSLAVE; i++){ + if(self->slaves[i] != NULL){ + pDum = (pDummy)self->slaveData[i]; + if(strcmp(pDum->pDescriptor->name,"HistMem") == 0){ + HistDirty((pHistMem)self->slaveData[i]); + } + } + } + return status; +} +/*-------------------------------------------------------------------------*/ +static int MMCCPause(void *pData, SConnection *pCon){ + int i, status; + pCounter pCount = NULL; + pMultiCounter self = NULL; + + pCount = (pCounter)pData; + if(pCount != NULL){ + self = (pMultiCounter)pCount->pDriv->pData; + } + assert(self); + + for(i = 0; i < self->nSlaves; i++){ + status = self->slaves[i]->Pause(self->slaveData[i],pCon); + if(status != OKOK){ + MMCCHalt(pCount); + return status; + } + } + return OKOK; +} +/*--------------------------------------------------------------------------*/ +static int MMCCContinue(void *pData, SConnection *pCon){ + int i, status; + pCounter pCount = NULL; + pMultiCounter self = NULL; + + pCount = (pCounter)pData; + if(pCount != NULL){ + self = (pMultiCounter)pCount->pDriv->pData; + } + assert(self); + + for(i = 0; i < self->nSlaves; i++){ + status = self->slaves[i]->Continue(self->slaveData[i],pCon); + if(status != OKOK){ + MMCCHalt(pCount); + return status; + } + } + return OKOK; +} +/*------------------------------------------------------------------------*/ +static char *getNextMMCCNumber(char *pStart, char pNumber[80]){ + int charCount = 0; + pNumber[0] = '\0'; + + /* advance to first digit */ + while(isspace(*pStart) && *pStart != '\0'){ + pStart++; + } + if(*pStart == '\0'){ + return NULL; + } + + /* copy */ + while(!isspace(*pStart) && *pStart != '\0' && charCount < 78){ + pNumber[charCount] = *pStart; + pStart++; + charCount++; + } + pNumber[charCount] = '\0'; + return pStart; +} +/*-------------------------------------------------------------------------*/ +static void loadCountData(pCounter pCount, const char *data){ + char *pPtr = NULL; + char pNumber[80]; + int i = 0; + + pPtr = (char *)data; + pPtr = getNextMMCCNumber(pPtr,pNumber); + pCount->pDriv->fTime = atof(pNumber); + while(pPtr != NULL && i < MAXCOUNT){ + pPtr = getNextMMCCNumber(pPtr,pNumber); + pCount->pDriv->lCounts[i] = atoi(pNumber); + i++; + } +} +/*--------------------------------------------------------------------------*/ +static int MMCCTransfer(void *pData, SConnection *pCon){ + int i, retVal = OKOK, status; + char pBueffel[132]; + pCounter pCount = NULL; + pMultiCounter self = NULL; + int tclStatus; + + pCount = (pCounter)pData; + if(pCount != NULL){ + self = (pMultiCounter)pCount->pDriv->pData; + } + assert(self); + + for(i = 0; i < self->nSlaves; i++){ + status = self->slaves[i]->TransferData(self->slaveData[i], pCon); + if(status != OKOK){ + retVal = status; + sprintf(pBueffel,"WARNING: slave histogram %d failed to transfer data", + i); + SCWrite(pCon,pBueffel,eWarning); + } + } + if(self->transferScript != NULL){ + MacroPush(pCon); + tclStatus = Tcl_Eval(InterpGetTcl(pServ->pSics), self->transferScript); + if(tclStatus != TCL_OK){ + snprintf(pBueffel,131,"ERROR: TransferScript returned: %s", + Tcl_GetStringResult(InterpGetTcl(pServ->pSics))); + SCWrite(pCon,pBueffel,eError); + MacroPop(); + return HWFault; + } + MacroPop(); + loadCountData(pCount,Tcl_GetStringResult(InterpGetTcl(pServ->pSics))); + } + return retVal; +} +/*-------------------------------------------------------------------------*/ +static void MMCCParameter(void *pData, float fPreset, CounterMode eMode ){ + int i; + pCounter pCount = NULL; + pMultiCounter self = NULL; + + pCount = (pCounter)pData; + if(pCount != NULL){ + self = (pMultiCounter)pCount->pDriv->pData; + } + assert(self); + + for(i = 0; i < self->nSlaves; i++){ + self->slaves[i]->SetCountParameters(self->slaveData[i], fPreset, + eMode); + } +} +/*======================= Driver Interface ==============================*/ +static int MultiCounterSet(struct __COUNTER *self, char *name, + int iCter, float fVal){ + + return 0; +} +/*-----------------------------------------------------------------------*/ +static int MultiCounterGet(struct __COUNTER *self, char *name, + int iCter, float *fVal){ + + return 0; +} +/*-----------------------------------------------------------------------*/ +static int MultiCounterSend(struct __COUNTER *self, char *pText, + char *reply, int replylen){ + + strncpy(reply,"NOT Implemented",replylen); + return 0; +} +/*---------------------------------------------------------------------*/ +static int MultiCounterError(struct __COUNTER *pDriv, int *iCode, + char *error, int errlen){ + + + if(pDriv->iErrorCode == NOCOUNTERS){ + strncpy(error,"NO counters configured!",errlen); + } else { + strncpy(error,"Not Implemented", errlen); + } + return COTERM; +} +/*----------------------------------------------------------------------*/ +static int MultiCounterFix(struct __COUNTER *self, int iCode){ + return COTERM; +} +/*=============== Interpreter Interface ================================ */ +int MultiCounterAction(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]){ + pMultiCounter self = NULL; + pCounter pCount = NULL; + char buffer[256]; + + if(argc > 1){ + strtolower(argv[1]); + if(strcmp(argv[1],"transferscript") == 0){ + pCount = (pCounter)pData; + self = (pMultiCounter)pCount->pDriv->pData; + if(argc < 3){ + SCPrintf(pCon,eValue,"%s.transferscript = %s", + argv[0],self->transferScript); + return 1; + } else { + if(!SCMatchRights(pCon,usUser)){ + return 0; + } + if(self->transferScript != NULL){ + free(self->transferScript); + } + Arg2Text(argc-2,&argv[2],buffer,255); + self->transferScript = strdup(buffer); + SCSendOK(pCon); + return 1; + } + } + } + return CountAction(pCon,pSics,pData,argc,argv); +} +/*------------------------------------------------------------------------*/ +int MakeMultiCounter(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]){ + int i, status; + pCounter pNew = NULL; + char pBueffel[132]; + CommandList *pCom; + pICountable pCount; + pMultiCounter self = NULL; + pCounterDriver pDriv = NULL; + + /* + need at least two parameters + */ + if(argc < 3){ + SCWrite(pCon,"ERROR: insufficient number of arguments to MakeMultiCounter", + eError); + return 0; + } + + /* + allocate our data structure + */ + self = malloc(sizeof(MultiCounter)); + pDriv = malloc(sizeof(CounterDriver)); + if(self == NULL || pDriv == NULL){ + SCWrite(pCon,"ERROR: out of memory in MakeMultiCounter",eError); + return 0; + } + memset(self,0,sizeof(MultiCounter)); + memset(pDriv,0,sizeof(CounterDriver)); + pDriv->pData = self; + pDriv->KillPrivate = KillMultiDriver; + pDriv->iNoOfMonitors = MAXCOUNT; + pNew = CreateCounter(argv[1],pDriv); + if(pNew == NULL){ + SCWrite(pCon,"ERROR: out of memory in MakeMultiCounter",eError); + return 0; + } + pDriv->Get = MultiCounterGet; + pDriv->GetError = MultiCounterError; + pDriv->TryAndFixIt = MultiCounterFix; + pDriv->Set = MultiCounterSet; + pDriv->Send = MultiCounterSend; + + /* + assign interface functions + */ + pNew->pCountInt->Halt = MMCCHalt; + pNew->pCountInt->StartCount = MMCCStart; + pNew->pCountInt->CheckCountStatus = MMCCStatus; + pNew->pCountInt->Pause = MMCCPause; + pNew->pCountInt->Continue = MMCCContinue; + pNew->pCountInt->TransferData = MMCCTransfer; + pNew->pCountInt->SetCountParameters = MMCCParameter; + + /* + now loop through the remaining arguments, thereby entering them into + the slave list. + */ + for(i = 2; i < argc; i++){ + pCom = FindCommand(pSics,argv[i]); + if(!pCom){ + sprintf(pBueffel,"ERROR: object %s not found in MakeMultiCounter", + argv[i]); + SCWrite(pCon,pBueffel,eError); + continue; + } + pCount = GetCountableInterface(pCom->pData); + if(!pCount){ + sprintf(pBueffel,"ERROR: object %s is NOT countable", + argv[i]); + SCWrite(pCon,pBueffel,eError); + continue; + } + self->slaves[self->nSlaves] = pCount; + self->slaveData[self->nSlaves] = pCom->pData; + self->nSlaves++; + } + + /* + now install our action command and we are done + */ + status = AddCommand(pSics,argv[1],MultiCounterAction,DeleteCounter, + pNew); + if(!status){ + sprintf(pBueffel,"ERROR: duplicate command %s not created",argv[1]); + SCWrite(pCon,pBueffel,eError); + DeleteCounter(pNew); + return 0; + } + + return 1; +} + diff --git a/multicounter.h b/multicounter.h new file mode 100644 index 00000000..400e36f6 --- /dev/null +++ b/multicounter.h @@ -0,0 +1,19 @@ +/** + * The MultiCounter is another counter which coordinates multiple + * counting objects, counters and histogram memories. It also calls a + * script function after TransferData which collects counters and monitors. + * The purpose is to have a flexible counter abstraction for upper level + * code such as maximizers and scan functions. The script can deal with + * counting on monitors or on sums of histogram memories. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, September 2006 + */ +#ifndef MULTICOUNTER_H_ +#define MULTICOUNTER_H_ +#include + +int MakeMultiCounter(SConnection *pCon, SicsInterp *pSics, + void *pData, int argc, char *argv[]); +#endif /*MULTICOUNTER_H_*/ diff --git a/mumo.c b/mumo.c index 3fbb3ad8..d3b8c406 100644 --- a/mumo.c +++ b/mumo.c @@ -872,6 +872,7 @@ static void RecoverNamPos(pMulMot self, int argc, char *argv[]) sprintf(pBueffel,"%s list of known named positions \n", argv[0]); Tcl_DStringAppend(&tString,pBueffel,strlen(pBueffel)); + StringDictKillScan(self->pNamPos); pPtr = StringDictGetNext(self->pNamPos,pError,131); while(pPtr != NULL) { diff --git a/mumoconf.c b/mumoconf.c index 00293496..305a7417 100644 --- a/mumoconf.c +++ b/mumoconf.c @@ -315,6 +315,7 @@ char pError[132]; int i, iToken, iRet; sParser PP; + CommandList *pCom = NULL; assert(pCon); assert(pSics); @@ -345,9 +346,10 @@ break; case ENDCONFIG: /* reconfigure command to final state */ - RemoveCommand(pSics,argv[0]); - AddCommand(pSics,argv[0],MultiWrapper,KillMultiMotor, - self); + pCom = FindCommand(pSics,argv[0]); + assert(pCom != NULL); + pCom->OFunc = MultiWrapper; + pCom->KFunc = KillMultiMotor; return 1; break; case ALIAS: diff --git a/napi.c b/napi.c index 3920f900..3de6d75a 100644 --- a/napi.c +++ b/napi.c @@ -3,7 +3,7 @@ Application Program Interface Routines - Copyright (C) 1997-2000 Mark Koennecke, Przemek Klosowski + Copyright (C) 1997-2006 Mark Koennecke, Przemek Klosowski This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -23,7 +23,7 @@ ----------------------------------------------------------------------------*/ -static const char* rscid = "$Id$"; /* Revision interted by CVS */ +static const char* rscid = "$Id$"; /* Revision inserted by CVS */ #include #include @@ -33,8 +33,81 @@ static const char* rscid = "$Id$"; /* Revision interted by CVS */ #include #include #include "napi.h" +#include "nxstack.h" +/*--------------------------------------------------------------------- + Recognized and handled napimount URLS + -----------------------------------------------------------------------*/ +#define NXBADURL 0 +#define NXFILE 1 + +/*--------------------------------------------------------------------*/ static int iFortifyScope; +/*---------------------------------------------------------------------- + This is a section with code for searching the NX_LOAD_PATH + -----------------------------------------------------------------------*/ +#ifdef _WIN32 +#define LIBSEP ";" +#define PATHSEP "\\" +#else +#define LIBSEP ":" +#define PATHSEP "/" +#endif +extern char *stptok(char *s, char *tok, size_t toklen, char *brk); +/*--------------------------------------------------------------------- + wrapper for getenv. This is a future proofing thing for porting to OS + which have different ways of accessing environment variables + --------------------------------------------------------------------*/ +static char *nxgetenv(const char *name){ + return getenv(name); +} +/*----------------------------------------------------------------------*/ +static int canOpen(char *filename){ + FILE *fd = NULL; + + fd = fopen(filename,"r"); + if(fd != NULL){ + fclose(fd); + return 1; + } else { + return 0; + } +} +/*--------------------------------------------------------------------*/ +static char *locateNexusFileInPath(char *startName){ + char *loadPath = NULL, *testPath = NULL, *pPtr = NULL; + char pathPrefix[256]; + int length; + + if(canOpen(startName)){ + return strdup(startName); + } + + loadPath = nxgetenv("NX_LOAD_PATH"); + if(loadPath == NULL){ + /* file not found will be issued by upper level code */ + return strdup(startName); + } + + pPtr = stptok(loadPath,pathPrefix,255,LIBSEP); + while(pPtr != NULL){ + length = strlen(pathPrefix) + strlen(startName) + strlen(PATHSEP) + 2; + testPath = malloc(length*sizeof(char)); + if(testPath == NULL){ + return strdup(startName); + } + memset(testPath,0,length*sizeof(char)); + strcpy(testPath, pathPrefix); + strcat(testPath,PATHSEP); + strcat(testPath,startName); + if(canOpen(testPath)){ + return(testPath); + } + free(testPath); + pPtr = stptok(pPtr,pathPrefix,255,LIBSEP); + } + return strdup(startName); +} /*------------------------------------------------------------------------ HDF-5 cache size special stuff -------------------------------------------------------------------------*/ @@ -49,7 +122,6 @@ NXstatus NXsetcache(long newVal) } return NX_ERROR; } - /*-----------------------------------------------------------------------*/ static NXstatus NXisXML(CONSTCHAR *filename) @@ -69,22 +141,14 @@ static NXstatus NXisXML(CONSTCHAR *filename) } /*-------------------------------------------------------------------------*/ - - - /*---------------------------------------------------------------------*/ - - void NXNXNXReportError(void *pData, char *string) + static void NXNXNXReportError(void *pData, char *string) { printf("%s \n",string); } - /*---------------------------------------------------------------------*/ - void *NXpData = NULL; void (*NXIReportError)(void *pData, char *string) = NXNXNXReportError; - /*---------------------------------------------------------------------*/ - extern void NXMSetError(void *pData, void (*NewError)(void *pD, char *text)) { @@ -95,7 +159,27 @@ static NXstatus NXisXML(CONSTCHAR *filename) extern ErrFunc NXMGetError(){ return NXIReportError; } - + +/*----------------------------------------------------------------------*/ +void NXNXNoReport(void *pData, char *string){ + /* do nothing */ +} +/*----------------------------------------------------------------------*/ + +static ErrFunc last_errfunc = NXNXNXReportError; + +extern void NXMDisableErrorReporting() +{ + last_errfunc = NXMGetError(); + NXMSetError(NXpData, NXNXNoReport); +} + +extern void NXMEnableErrorReporting() +{ + NXMSetError(NXpData, last_errfunc); +} + +/*----------------------------------------------------------------------*/ #ifdef HDF5 #include "napi5.h" #endif @@ -105,7 +189,6 @@ extern ErrFunc NXMGetError(){ #ifdef NXXML #include "nxxml.h" #endif - /* ---------------------------------------------------------------------- Definition of NeXus API @@ -148,17 +231,49 @@ static int determineFileType(CONSTCHAR *filename) */ return 0; } +/*---------------------------------------------------------------------*/ +static pNexusFunction handleToNexusFunc(NXhandle fid){ + pFileStack fileStack = NULL; + fileStack = (pFileStack)fid; + if(fileStack != NULL){ + return peekFileOnStack(fileStack); + } else { + return NULL; + } +} +/*--------------------------------------------------------------------*/ +static NXstatus NXinternalopen(CONSTCHAR *userfilename, NXaccess am, + pFileStack fileStack); +/*----------------------------------------------------------------------*/ +NXstatus NXopen(CONSTCHAR *userfilename, NXaccess am, NXhandle *gHandle){ + int status; + pFileStack fileStack = NULL; + NXhandle hfile; + + fileStack = makeFileStack(); + if(fileStack == NULL){ + NXIReportError (NXpData,"ERROR: no memory to create filestack"); + return NX_ERROR; + } + status = NXinternalopen(userfilename,am,fileStack); + if(status == NX_OK){ + *gHandle = fileStack; + } + + return status; +} /*-----------------------------------------------------------------------*/ - NXstatus NXopen(CONSTCHAR *filename, NXaccess am, NXhandle *gHandle) +static NXstatus NXinternalopen(CONSTCHAR *userfilename, NXaccess am, pFileStack fileStack) { int hdf_type=0; int iRet=0; - NXhandle hdf5_handle; - NXhandle hdf4_handle; - NXhandle xmlHandle; - pNexusFunction fHandle; - NXstatus retstat; + NXhandle hdf5_handle = NULL; + NXhandle hdf4_handle = NULL; + NXhandle xmlHandle = NULL; + pNexusFunction fHandle = NULL; + NXstatus retstat = NX_ERROR; char error[1024]; + char *filename = NULL; /* configure fortify iFortifyScope = Fortify_EnterScope(); @@ -168,13 +283,12 @@ static int determineFileType(CONSTCHAR *filename) /* allocate data */ - *gHandle = NULL; fHandle = (pNexusFunction)malloc(sizeof(NexusFunction)); if (fHandle == NULL) { NXIReportError (NXpData,"ERROR: no memory to create Function structure"); return NX_ERROR; - } - memset(fHandle, 0, sizeof(NexusFunction)); /* so any functions we miss are NULL */ + } + memset(fHandle, 0, sizeof(NexusFunction)); /* so any functions we miss are NULL */ /* test the strip flag. Elimnate it for the rest of the tests to work @@ -188,49 +302,68 @@ static int determineFileType(CONSTCHAR *filename) if (am==NXACC_CREATE) { /* HDF4 will be used ! */ hdf_type=1; + filename = strdup(userfilename); } else if (am==NXACC_CREATE4) { /* HDF4 will be used ! */ hdf_type=1; + filename = strdup(userfilename); } else if (am==NXACC_CREATE5) { /* HDF5 will be used ! */ hdf_type=2; + filename = strdup(userfilename); } else if (am==NXACC_CREATEXML) { /* XML will be used ! */ hdf_type=3; + filename = strdup(userfilename); } else { + filename = locateNexusFileInPath((char *)userfilename); + if(filename == NULL){ + NXIReportError(NXpData,"Out of memory in NeXus-API"); + free(fHandle); + return NX_ERROR; + } /* check file type hdf4/hdf5/XML for reading */ iRet = determineFileType(filename); if(iRet < 0) { snprintf(error,1023,"failed to open %s for reading", filename); NXIReportError(NXpData,error); + free(filename); return NX_ERROR; } if(iRet == 0){ - snprintf(error,1023,"failed to detrmine filetype for %s ", + snprintf(error,1023,"failed to determine filetype for %s ", filename); NXIReportError(NXpData,error); + free(filename); + free(fHandle); return NX_ERROR; } hdf_type = iRet; } + if(filename == NULL){ + NXIReportError(NXpData,"Out of memory in NeXus-API"); + return NX_ERROR; + } + if (hdf_type==1) { /* HDF4 type */ #ifdef HDF4 retstat = NX4open((const char *)filename,am,&hdf4_handle); if(retstat != NX_OK){ free(fHandle); + free(filename); return retstat; } - fHandle->pNexusData=hdf4_handle; - NX4assignFunctions(fHandle); - *gHandle = fHandle; + fHandle->pNexusData=hdf4_handle; + NX4assignFunctions(fHandle); + pushFileStack(fileStack,fHandle,filename); #else NXIReportError (NXpData, "ERROR: Attempt to create HDF4 file when not linked with HDF4"); - *gHandle = NULL; retstat = NX_ERROR; #endif /* HDF4 */ + free(filename); return retstat; } else if (hdf_type==2) { /* HDF5 type */ @@ -238,17 +371,18 @@ static int determineFileType(CONSTCHAR *filename) retstat = NX5open(filename,am,&hdf5_handle); if(retstat != NX_OK){ free(fHandle); + free(filename); return retstat; } - fHandle->pNexusData=hdf5_handle; - NX5assignFunctions(fHandle); - *gHandle = fHandle; + fHandle->pNexusData=hdf5_handle; + NX5assignFunctions(fHandle); + pushFileStack(fileStack,fHandle, filename); #else NXIReportError (NXpData, "ERROR: Attempt to create HDF5 file when not linked with HDF5"); - *gHandle = NULL; retstat = NX_ERROR; #endif /* HDF5 */ + free(filename); return retstat; } else if(hdf_type == 3){ /* @@ -258,24 +392,26 @@ static int determineFileType(CONSTCHAR *filename) retstat = NXXopen(filename,am,&xmlHandle); if(retstat != NX_OK){ free(fHandle); + free(filename); return retstat; } fHandle->pNexusData=xmlHandle; NXXassignFunctions(fHandle); - *gHandle = fHandle; + pushFileStack(fileStack,fHandle, filename); #else NXIReportError (NXpData, "ERROR: Attempt to create XML file when not linked with XML"); - *gHandle = NULL; retstat = NX_ERROR; #endif } else { NXIReportError (NXpData, "ERROR: Format not readable by this NeXus library"); - *gHandle = NULL; - return NX_ERROR; + retstat = NX_ERROR; } - return NX_OK; + if (filename != NULL) { + free(filename); + } + return retstat; } /* ------------------------------------------------------------------------- */ @@ -284,13 +420,19 @@ static int determineFileType(CONSTCHAR *filename) { NXhandle hfil; int status; + pFileStack fileStack = NULL; pNexusFunction pFunc=NULL; - pFunc = (pNexusFunction)*fid; + fileStack = (pFileStack)*fid; + pFunc = peekFileOnStack(fileStack); hfil = pFunc->pNexusData; status = pFunc->nxclose(&hfil); pFunc->pNexusData = hfil; free(pFunc); + popFileStack(fileStack); + if(fileStackDepth(fileStack) < 0){ + killFileStack(fileStack); + } /* Fortify_CheckAllMemory(); */ @@ -301,24 +443,113 @@ static int determineFileType(CONSTCHAR *filename) NXstatus NXmakegroup (NXhandle fid, CONSTCHAR *name, CONSTCHAR *nxclass) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); return pFunc->nxmakegroup(pFunc->pNexusData, name, nxclass); } + /*------------------------------------------------------------------------*/ +static int analyzeNapimount(char *napiMount, char *extFile, int extFileLen, + char *extPath, int extPathLen){ + char *pPtr = NULL, *path = NULL; + int length; + memset(extFile,0,extFileLen); + memset(extPath,0,extPathLen); + pPtr = strstr(napiMount,"nxfile://"); + if(pPtr == NULL){ + return NXBADURL; + } + path = strrchr(napiMount,'#'); + if(path == NULL){ + length = strlen(napiMount) - 9; + if(length > extFileLen){ + NXIReportError(NXpData,"ERROR: internal errro with external linking"); + return NXBADURL; + } + memcpy(extFile,pPtr+9,length); + strcpy(extPath,"/"); + return NXFILE; + } else { + pPtr += 9; + length = path - pPtr; + if(length > extFileLen){ + NXIReportError(NXpData,"ERROR: internal errro with external linking"); + return NXBADURL; + } + memcpy(extFile,pPtr,length); + length = strlen(path-1); + if(length > extPathLen){ + NXIReportError(NXpData,"ERROR: internal error with external linking"); + return NXBADURL; + } + strcpy(extPath,path+1); + return NXFILE; + } + return NXBADURL; +} /*------------------------------------------------------------------------*/ NXstatus NXopengroup (NXhandle fid, CONSTCHAR *name, CONSTCHAR *nxclass) { - pNexusFunction pFunc = (pNexusFunction)fid; - return pFunc->nxopengroup(pFunc->pNexusData, name, nxclass); + int status, attStatus, type = NX_CHAR, length = 1023; + NXaccess access = NXACC_RDWR; + NXlink breakID; + pFileStack fileStack; + char nxurl[1024], exfile[512], expath[512]; + ErrFunc oldError; + pNexusFunction pFunc = NULL; + + fileStack = (pFileStack)fid; + pFunc = handleToNexusFunc(fid); + + status = pFunc->nxopengroup(pFunc->pNexusData, name, nxclass); + oldError = NXMGetError(); + NXIReportError = NXNXNoReport; + attStatus = NXgetattr(fid,"napimount",nxurl,&length, &type); + NXIReportError = oldError; + if(attStatus == NX_OK){ + /* + this is an external linking group + */ + status = analyzeNapimount(nxurl,exfile,511,expath,511); + if(status == NXBADURL){ + return NX_ERROR; + } + status = NXinternalopen(exfile,access, fileStack); + if(status == NX_ERROR){ + return status; + } + status = NXopenpath(fid,expath); + NXgetgroupID(fid,&breakID); + setCloseID(fileStack,breakID); + } + + return status; } /* ------------------------------------------------------------------- */ NXstatus NXclosegroup (NXhandle fid) { - pNexusFunction pFunc = (pNexusFunction)fid; - return pFunc->nxclosegroup(pFunc->pNexusData); + int status; + pFileStack fileStack = NULL; + NXlink closeID, currentID; + + pNexusFunction pFunc = handleToNexusFunc(fid); + fileStack = (pFileStack)fid; + if(fileStackDepth(fileStack) == 0){ + return pFunc->nxclosegroup(pFunc->pNexusData); + } else { + /* we have to check for leaving an external file */ + NXgetgroupID(fid,¤tID); + peekIDOnStack(fileStack,&closeID); + if(NXsameID(fid,&closeID,¤tID) == NX_OK){ + NXclose(&fid); + status = NXclosegroup(fid); + } else { + status = pFunc->nxclosegroup(pFunc->pNexusData); + } + return status; + } } /* --------------------------------------------------------------------- */ @@ -326,17 +557,8 @@ static int determineFileType(CONSTCHAR *filename) NXstatus NXmakedata (NXhandle fid, CONSTCHAR *name, int datatype, int rank, int dimensions[]) { - pNexusFunction pFunc = (pNexusFunction)fid; - if ( (datatype == NX_CHAR) && (rank > 1) ) - { - NXIReportError (NXpData, - "ERROR: multi-dimensional NX_CHAR arrays are not supported by the NeXus library"); - return NX_ERROR; - } - else - { - return pFunc->nxmakedata(pFunc->pNexusData, name, datatype, rank, dimensions); - } + pNexusFunction pFunc = handleToNexusFunc(fid); + return pFunc->nxmakedata(pFunc->pNexusData, name, datatype, rank, dimensions); } @@ -345,17 +567,8 @@ static int determineFileType(CONSTCHAR *filename) NXstatus NXcompmakedata (NXhandle fid, CONSTCHAR *name, int datatype, int rank, int dimensions[],int compress_type, int chunk_size[]) { - pNexusFunction pFunc = (pNexusFunction)fid; - if ( (datatype == NX_CHAR) && (rank > 1) ) - { - NXIReportError (NXpData, - "ERROR: multi-dimensional NX_CHAR arrays are not supported by the NeXus library"); - return NX_ERROR; - } - else - { - return pFunc->nxcompmakedata (pFunc->pNexusData, name, datatype, rank, dimensions, compress_type, chunk_size); - } + pNexusFunction pFunc = handleToNexusFunc(fid); + return pFunc->nxcompmakedata (pFunc->pNexusData, name, datatype, rank, dimensions, compress_type, chunk_size); } @@ -363,7 +576,7 @@ static int determineFileType(CONSTCHAR *filename) NXstatus NXcompress (NXhandle fid, int compress_type) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); return pFunc->nxcompress (pFunc->pNexusData, compress_type); } @@ -372,7 +585,7 @@ static int determineFileType(CONSTCHAR *filename) NXstatus NXopendata (NXhandle fid, CONSTCHAR *name) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); return pFunc->nxopendata(pFunc->pNexusData, name); } @@ -381,7 +594,7 @@ static int determineFileType(CONSTCHAR *filename) NXstatus NXclosedata (NXhandle fid) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); return pFunc->nxclosedata(pFunc->pNexusData); } @@ -389,7 +602,7 @@ static int determineFileType(CONSTCHAR *filename) NXstatus NXputdata (NXhandle fid, void *data) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); return pFunc->nxputdata(pFunc->pNexusData, data); } @@ -398,7 +611,7 @@ static int determineFileType(CONSTCHAR *filename) NXstatus NXputattr (NXhandle fid, CONSTCHAR *name, void *data, int datalen, int iType) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); return pFunc->nxputattr(pFunc->pNexusData, name, data, datalen, iType); } @@ -406,7 +619,7 @@ static int determineFileType(CONSTCHAR *filename) NXstatus NXputslab (NXhandle fid, void *data, int iStart[], int iSize[]) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); return pFunc->nxputslab(pFunc->pNexusData, data, iStart, iSize); } @@ -414,7 +627,7 @@ static int determineFileType(CONSTCHAR *filename) NXstatus NXgetdataID (NXhandle fid, NXlink* sRes) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); return pFunc->nxgetdataID(pFunc->pNexusData, sRes); } @@ -423,9 +636,16 @@ static int determineFileType(CONSTCHAR *filename) NXstatus NXmakelink (NXhandle fid, NXlink* sLink) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); return pFunc->nxmakelink(pFunc->pNexusData, sLink); } + /* ------------------------------------------------------------------- */ + + NXstatus NXmakenamedlink (NXhandle fid, CONSTCHAR *newname, NXlink* sLink) + { + pNexusFunction pFunc = handleToNexusFunc(fid); + return pFunc->nxmakenamedlink(pFunc->pNexusData, newname, sLink); + } /* --------------------------------------------------------------------*/ NXstatus NXopensourcegroup(NXhandle fid) { @@ -445,10 +665,12 @@ static int determineFileType(CONSTCHAR *filename) NXstatus NXflush(NXhandle *pHandle) { NXhandle hfil; + pFileStack fileStack = NULL; int status; pNexusFunction pFunc=NULL; - pFunc = (pNexusFunction)*pHandle; + fileStack = (pFileStack)*pHandle; + pFunc = peekFileOnStack(fileStack); hfil = pFunc->pNexusData; status = pFunc->nxflush(&hfil); pFunc->pNexusData = hfil; @@ -469,7 +691,7 @@ static int determineFileType(CONSTCHAR *filename) if ((datatype == NX_CHAR) || (datatype == NX_INT8) || (datatype == NX_UINT8)) { /* allow for terminating \0 */ - size += 1; + size += 2; } else if ((datatype == NX_INT16) || (datatype == NX_UINT16)) { size *= 2; @@ -478,6 +700,9 @@ static int determineFileType(CONSTCHAR *filename) || (datatype == NX_FLOAT32)) { size *= 4; } + else if ((datatype == NX_INT64) || (datatype == NX_UINT64)){ + size *= 8; + } else if (datatype == NX_FLOAT64) { size *= 8; } @@ -513,7 +738,7 @@ static int determineFileType(CONSTCHAR *filename) NXstatus NXgetnextentry (NXhandle fid, NXname name, NXname nxclass, int *datatype) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); return pFunc->nxgetnextentry(pFunc->pNexusData, name, nxclass, datatype); } /*----------------------------------------------------------------------*/ @@ -524,7 +749,7 @@ static int determineFileType(CONSTCHAR *filename) */ #define NUL '\0' -static char *nxitrim(char *str) +char *nxitrim(char *str) { char *ibuf = str, *obuf = str; int i = 0, cnt = 0; @@ -563,12 +788,13 @@ static char *nxitrim(char *str) int status, type, rank, iDim[NX_MAXRANK]; char *pPtr, *pPtr2; - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); status = pFunc->nxgetinfo(pFunc->pNexusData, &rank, iDim, &type); /* unstripped size if string */ - if ( (type == NX_CHAR) && (pFunc->stripFlag == 1) ) + /* only strip one dimensional strings */ + if ( (type == NX_CHAR) && (pFunc->stripFlag == 1) && (rank == 1) ) { - pPtr = (char*)malloc(iDim[0]+1); - memset(pPtr, 0, iDim[0]+1); + pPtr = (char*)malloc(iDim[0]+5); + memset(pPtr, 0, iDim[0]+5); status = pFunc->nxgetdata(pFunc->pNexusData, pPtr); pPtr2 = nxitrim(pPtr); strncpy((char*)data, pPtr2, strlen(pPtr2)); /* not NULL terminated by default */ @@ -589,12 +815,13 @@ static char *nxitrim(char *str) int status; char *pPtr = NULL; - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); status = pFunc->nxgetinfo(pFunc->pNexusData, rank, dimension, iType); /* the length of a string may be trimmed.... */ - if(*iType == NX_CHAR && pFunc->stripFlag == 1){ + /* only strip one dimensional strings */ + if((*iType == NX_CHAR) && (pFunc->stripFlag == 1) && (*rank == 1)){ pPtr = (char *)malloc((dimension[0]+1)*sizeof(char)); if(pPtr != NULL){ memset(pPtr,0,(dimension[0]+1)*sizeof(char)); @@ -603,11 +830,6 @@ static char *nxitrim(char *str) free(pPtr); } } - if ( (*iType == NX_CHAR) && (*rank > 1) ) - { - NXIReportError(NXpData, - "WARNING: multi-dimensional character arrays are not really supported"); - } return status; } @@ -616,7 +838,7 @@ static char *nxitrim(char *str) NXstatus NXgetslab (NXhandle fid, void *data, int iStart[], int iSize[]) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); return pFunc->nxgetslab(pFunc->pNexusData, data, iStart, iSize); } @@ -626,7 +848,7 @@ static char *nxitrim(char *str) NXstatus NXgetnextattr (NXhandle fileid, NXname pName, int *iLength, int *iType) { - pNexusFunction pFunc = (pNexusFunction)fileid; + pNexusFunction pFunc = handleToNexusFunc(fileid); return pFunc->nxgetnextattr(pFunc->pNexusData, pName, iLength, iType); } @@ -635,7 +857,7 @@ static char *nxitrim(char *str) NXstatus NXgetattr (NXhandle fid, char *name, void *data, int* datalen, int* iType) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); return pFunc->nxgetattr(pFunc->pNexusData, name, data, datalen, iType); } @@ -644,7 +866,7 @@ static char *nxitrim(char *str) NXstatus NXgetattrinfo (NXhandle fid, int *iN) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); return pFunc->nxgetattrinfo(pFunc->pNexusData, iN); } @@ -653,7 +875,7 @@ static char *nxitrim(char *str) NXstatus NXgetgroupID (NXhandle fileid, NXlink* sRes) { - pNexusFunction pFunc = (pNexusFunction)fileid; + pNexusFunction pFunc = handleToNexusFunc(fileid); return pFunc->nxgetgroupID(pFunc->pNexusData, sRes); } @@ -661,7 +883,7 @@ static char *nxitrim(char *str) NXstatus NXgetgroupinfo (NXhandle fid, int *iN, NXname pName, NXname pClass) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); return pFunc->nxgetgroupinfo(pFunc->pNexusData, iN, pName, pClass); } @@ -670,7 +892,7 @@ static char *nxitrim(char *str) NXstatus NXsameID (NXhandle fileid, NXlink* pFirstID, NXlink* pSecondID) { - pNexusFunction pFunc = (pNexusFunction)fileid; + pNexusFunction pFunc = handleToNexusFunc(fileid); return pFunc->nxsameID(pFunc->pNexusData, pFirstID, pSecondID); } @@ -678,7 +900,7 @@ static char *nxitrim(char *str) NXstatus NXinitattrdir (NXhandle fid) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); return pFunc->nxinitattrdir(pFunc->pNexusData); } /*-------------------------------------------------------------------------*/ @@ -686,7 +908,7 @@ static char *nxitrim(char *str) NXstatus NXsetnumberformat (NXhandle fid, int type, char *format) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); if(pFunc->nxsetnumberformat != NULL) { return pFunc->nxsetnumberformat(pFunc->pNexusData,type,format); @@ -706,10 +928,78 @@ static char *nxitrim(char *str) NXstatus NXinitgroupdir (NXhandle fid) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); return pFunc->nxinitgroupdir(pFunc->pNexusData); - } - + } +/*----------------------------------------------------------------------*/ + NXstatus NXinquirefile(NXhandle handle, char *filename, + int filenameBufferLength){ + pFileStack fileStack; + char *pPtr = NULL; + int length; + + fileStack = (pFileStack)handle; + pPtr = peekFilenameOnStack(fileStack); + if(pPtr != NULL){ + length = strlen(pPtr); + if(length > filenameBufferLength){ + length = filenameBufferLength -1; + } + memset(filename,0,filenameBufferLength); + memcpy(filename,pPtr, length); + return NX_OK; + } else { + return NX_ERROR; + } +} +/*------------------------------------------------------------------------*/ +NXstatus NXisexternalgroup(NXhandle fid, CONSTCHAR *name, CONSTCHAR *class, + char *url, int urlLen){ + int status, attStatus, length = 1023, type = NX_CHAR; + ErrFunc oldError; + char nxurl[1024]; + + pNexusFunction pFunc = handleToNexusFunc(fid); + + status = pFunc->nxopengroup(pFunc->pNexusData, name,class); + if(status != NX_OK){ + return status; + } + oldError = NXMGetError(); + NXIReportError = NXNXNoReport; + attStatus = NXgetattr(fid,"napimount",nxurl,&length, &type); + NXIReportError = oldError; + pFunc->nxclosegroup(pFunc->pNexusData); + if(attStatus == NX_OK){ + length = strlen(nxurl); + if(length > urlLen){ + length = urlLen - 1; + } + memset(url,0,urlLen); + memcpy(url,nxurl,length); + return attStatus; + } else { + return NX_ERROR; + } +} +/*------------------------------------------------------------------------*/ +NXstatus NXlinkexternal(NXhandle fid, CONSTCHAR *name, CONSTCHAR *class, + CONSTCHAR *url){ + int status, type = NX_CHAR, length; + pNexusFunction pFunc = handleToNexusFunc(fid); + + status = pFunc->nxopengroup(pFunc->pNexusData,name,class); + if(status != NX_OK){ + return status; + } + length = strlen(url); + status = NXputattr(fid, "napimount",(void *)url,length, type); + if(status != NX_OK){ + return status; + } + pFunc->nxclosegroup(pFunc->pNexusData); + return NX_OK; +} /*------------------------------------------------------------------------ Implementation of NXopenpath. --------------------------------------------------------------------------*/ @@ -1009,7 +1299,7 @@ NXstatus NXopengrouppath(NXhandle hfil, CONSTCHAR *path) NXstatus NXIprintlink(NXhandle fid, NXlink* link) { - pNexusFunction pFunc = (pNexusFunction)fid; + pNexusFunction pFunc = handleToNexusFunc(fid); return pFunc->nxprintlink(pFunc->pNexusData, link); } @@ -1228,241 +1518,3 @@ char *NXIformatNeXusTime(){ return ret; } -/* - * windows stdcall aliases - */ -#ifdef _WIN32 - -NXstatus __stdcall NXISETCACHE(long newVal) -{ - return NXsetcache(newVal); -} - -void __stdcall NXNXNXREPORTERROR(void *pData, char *string) -{ - NXNXNXReportError(pData, string); -} - -NXstatus __stdcall NXIOPEN(CONSTCHAR *filename, NXaccess am, NXhandle *gHandle) -{ - return NXopen(filename, am, gHandle); -} - -NXstatus __stdcall NXICLOSE(NXhandle *fid) -{ - return NXclose(fid); -} - -NXstatus __stdcall NXIFLUSH(NXhandle* pHandle) -{ - return NXflush(pHandle); -} - -NXstatus __stdcall NXIMAKEGROUP(NXhandle handle, CONSTCHAR *name, CONSTCHAR* NXclass) -{ - return NXmakegroup(handle, name, NXclass); -} - -NXstatus __stdcall NXIOPENGROUP(NXhandle handle, CONSTCHAR *name, CONSTCHAR* NXclass) -{ - return NXopengroup(handle, name, NXclass); -} - -NXstatus __stdcall NXIOPENPATH(NXhandle handle, CONSTCHAR *path) -{ - return NXopenpath(handle, path); -} - -NXstatus __stdcall NXIOPENGROUPPATH (NXhandle handle, CONSTCHAR *path) -{ - return NXopengrouppath(handle, path); -} - -NXstatus __stdcall NXICLOSEGROUP(NXhandle handle) -{ - return NXclosegroup(handle); -} - -NXstatus __stdcall NXIMAKEDATA (NXhandle handle, CONSTCHAR* label, int datatype, int rank, int dim[]) -{ - return NXmakedata (handle, label, datatype, rank, dim); -} - -NXstatus __stdcall NXICOMPMAKEDATA (NXhandle handle, CONSTCHAR* label, int datatype, int rank, int dim[], int comp_typ, int bufsize[]) -{ - return NXcompmakedata (handle, label, datatype, rank, dim, comp_typ, bufsize); -} - -NXstatus __stdcall NXICOMPRESS (NXhandle handle, int compr_type) -{ - return NXcompress (handle, compr_type); -} - -NXstatus __stdcall NXIOPENDATA (NXhandle handle, CONSTCHAR* label) -{ - return NXopendata (handle, label); -} - -NXstatus __stdcall NXICLOSEDATA(NXhandle handle) -{ - return NXclosedata(handle); -} - -NXstatus __stdcall NXIPUTDATA(NXhandle handle, void* data) -{ - return NXputdata(handle, data); -} - -NXstatus __stdcall NXIPUTATTR(NXhandle handle, CONSTCHAR* name, void* data, int iDataLen, int iType) -{ - return NXputattr(handle, name, data, iDataLen, iType); -} - -NXstatus __stdcall NXIPUTSLAB(NXhandle handle, void* data, int start[], int size[]) -{ - return NXputslab(handle, data, start, size); -} - -NXstatus __stdcall NXIGETDATAID(NXhandle handle, NXlink* pLink) -{ - return NXgetdataID(handle, pLink); -} - -NXstatus __stdcall NXIMAKELINK(NXhandle handle, NXlink* pLink) -{ - return NXmakelink(handle, pLink); -} - -NXstatus __stdcall NXIOPENSOURCEGROUP(NXhandle handle) -{ - return NXopensourcegroup(handle); -} - -NXstatus __stdcall NXIGETDATA(NXhandle handle, void* data) -{ - return NXgetdata(handle, data); -} - -NXstatus __stdcall NXIGETINFO(NXhandle handle, int* rank, int dimension[], int* datatype) -{ - return NXgetinfo(handle, rank, dimension, datatype); -} - -NXstatus __stdcall NXIGETNEXTENTRY(NXhandle handle, NXname name, NXname nxclass, int* datatype) -{ - return NXgetnextentry(handle, name, nxclass, datatype); -} - -NXstatus __stdcall NXIGETSLAB(NXhandle handle, void* data, int start[], int size[]) -{ - return NXgetslab(handle, data, start, size); -} - -NXstatus __stdcall NXIGETNEXTATTR(NXhandle handle, NXname pName, int *iLength, int *iType) -{ - return NXgetnextattr(handle, pName, iLength, iType); -} - -NXstatus __stdcall NXIGETATTR(NXhandle handle, char* name, void* data, int* iDataLen, int* iType) -{ - return NXgetattr(handle, name, data, iDataLen, iType); -} - -NXstatus __stdcall NXIGETATTRINFO(NXhandle handle, int* no_items) -{ - return NXgetattrinfo(handle, no_items); -} - -NXstatus __stdcall NXIGETGROUPID(NXhandle handle, NXlink* pLink) -{ - return NXgetgroupID(handle, pLink); -} - -NXstatus __stdcall NXIGETGROUPINFO(NXhandle handle, int* no_items, NXname name, NXname nxclass) -{ - return NXgetgroupinfo(handle, no_items, name, nxclass); -} - -NXstatus __stdcall NXISAMEID(NXhandle handle, NXlink* pFirstID, NXlink* pSecondID) -{ - return NXsameID(handle, pFirstID, pSecondID); -} - -NXstatus __stdcall NXIINITGROUPDIR(NXhandle handle) -{ - return NXinitgroupdir(handle); -} -NXstatus __stdcall NXIINITATTRDIR(NXhandle handle) -{ - return NXinitattrdir(handle); -} -NXstatus __stdcall NXISETNUMBERFORMAT(NXhandle handle, int type, char *format) -{ - return NXsetnumberformat(handle,type, format); -} - -NXstatus __stdcall NXIMALLOC(void** data, int rank, int dimensions[], int datatype) -{ - return NXmalloc(data, rank, dimensions, datatype); -} - -NXstatus __stdcall NXIFREE(void** data) -{ - return NXfree(data); -} - -#if 0 -/*----------------------------------------------------------------------- - NAPI internals -------------------------------------------------------------------------*/ -extern void NXMSetError(void *pData, void (*ErrFunc)(void *pD, char *text)); -extern void (*NXIReportError)(void *pData,char *text); -extern void *NXpData; -extern char *NXIformatNeXusTime(); -#endif - -/* FORTRAN internals */ - -NXstatus __stdcall NXIFOPEN(char * filename, NXaccess* am, - NexusFunction* pHandle) -{ - return NXfopen(filename, am, pHandle); -} - -NXstatus __stdcall NXIFCLOSE (NexusFunction* pHandle) -{ - return NXfclose (pHandle); -} - -NXstatus __stdcall NXIFPUTATTR(NXhandle fid, char *name, void *data, - int *pDatalen, int *pIType) -{ - return NXfputattr(fid, name, data, pDatalen, pIType); -} - -NXstatus __stdcall NXIFCOMPRESS(NXhandle fid, int *compr_type) -{ - return NXfcompress(fid, compr_type); -} - -NXstatus __stdcall NXIFCOMPMAKEDATA(NXhandle fid, char *name, - int *pDatatype, - int *pRank, int dimensions[], - int *compression_type, int chunk[]) -{ - return NXfcompmakedata(fid, name, pDatatype, pRank, dimensions, - compression_type, chunk); -} - -NXstatus __stdcall NXIFMAKEDATA(NXhandle fid, char *name, int *pDatatype, - int *pRank, int dimensions[]) -{ - return NXfmakedata(fid, name, pDatatype, pRank, dimensions); -} - -NXstatus __stdcall NXIFFLUSH(NexusFunction* pHandle) -{ - return NXfflush(pHandle); -} - -#endif /* _WIN32 */ diff --git a/napi.h b/napi.h index 2d38809c..50536245 100644 --- a/napi.h +++ b/napi.h @@ -3,7 +3,7 @@ Application Program Interface Header File - Copyright (C) 2000-2005 Mark Koennecke, Uwe Filges + Copyright (C) 2000-2007 Mark Koennecke, Uwe Filges This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -19,7 +19,7 @@ License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - For further information, see + For further information, see $Id$ @@ -29,14 +29,14 @@ #define NEXUSAPI /* NeXus HDF45 */ -#define NEXUS_VERSION "3.0.0" /* major.minor.patch */ +#define NEXUS_VERSION "4.1.0" /* major.minor.patch */ #define CONSTCHAR const char -#ifdef _WIN32 -//#define snprintf nxisnprintf +#ifdef _MSC_VER +#define snprintf nxisnprintf extern int nxisnprintf(char* buffer, int len, const char* format, ... ); -#endif /* _WIN32 */ +#endif /* _MSC_VER */ typedef void* NXhandle; /* really a pointer to a NexusFile structure */ typedef int NXstatus; @@ -101,6 +101,8 @@ typedef struct { #define NX_UINT16 23 #define NX_INT32 24 #define NX_UINT32 25 +#define NX_INT64 26 +#define NX_UINT64 27 #define NX_CHAR 4 #define NX_BINARY 21 @@ -113,10 +115,8 @@ typedef struct { typedef struct { long iTag; /* HDF4 variable */ long iRef; /* HDF4 variable */ - char iTag5[1024]; /* HDF5 variable */ - char iRef5[1024]; /* HDF5 variable */ - char iRefd[1024]; /* HDF5 variable */ - char targetPath[1024]; /* XML path */ + char targetPath[1024]; /* path to item to link */ + int linkType; /* HDF5: 0 for group link, 1 for SDS link */ } NXlink; #define NXMAXSTACK 50 @@ -146,6 +146,7 @@ typedef struct { # define NXputattr MANGLE(nxiputattr) # define NXgetdataID MANGLE(nxigetdataid) # define NXmakelink MANGLE(nximakelink) +# define NXmakenamedlink MANGLE(nximakenamedlink) # define NXopensourcegroup MANGLE(nxiopensourcegroup) # define NXmalloc MANGLE(nximalloc) # define NXfree MANGLE(nxifree) @@ -164,8 +165,11 @@ typedef struct { # define NXsameID MANGLE(nxisameid) # define NXinitgroupdir MANGLE(nxiinitgroupdir) # define NXinitattrdir MANGLE(nxiinitattrdir) -# define NXsetnumberformat MANGLE(nxisetnumberformat) +# define NXsetnumberformat MANGLE(nxisetnumberformat) # define NXsetcache MANGLE(nxisetcache) +# define NXinquirefile MANGLE(nxiinquirefile) +# define NXisexternalgroup MANGLE(nxiisexternalgroup) +# define NXlinkexternal MANGLE(nxilinkexternal) /* * FORTRAN helpers - for NeXus internal use only @@ -181,6 +185,11 @@ typedef struct { /* * Standard interface + * + * Functions added here are not automatically exported from + * a shared library/dll - the symbol name must also be added + * to the file src/nexus_symbols.txt + * */ #ifdef __cplusplus @@ -209,6 +218,7 @@ extern NXstatus NXputslab(NXhandle handle, void* data, int start[], int size[] extern NXstatus NXgetdataID(NXhandle handle, NXlink* pLink); extern NXstatus NXmakelink(NXhandle handle, NXlink* pLink); +extern NXstatus NXmakenamedlink(NXhandle handle, CONSTCHAR* newname, NXlink* pLink); extern NXstatus NXopensourcegroup(NXhandle handle); extern NXstatus NXgetdata(NXhandle handle, void* data); @@ -226,7 +236,11 @@ extern NXstatus NXsameID(NXhandle handle, NXlink* pFirstID, NXlink* pSecondID) extern NXstatus NXinitgroupdir(NXhandle handle); extern NXstatus NXinitattrdir(NXhandle handle); extern NXstatus NXsetnumberformat(NXhandle handle, - int type, char *format); + int type, char *format); + +extern NXstatus NXinquirefile(NXhandle handle, char *filename, int filenameBufferLength); +extern NXstatus NXisexternalgroup(NXhandle fid, CONSTCHAR *name, CONSTCHAR *nxclass, char *url, int urlLen); +extern NXstatus NXlinkexternal(NXhandle fid, CONSTCHAR *name, CONSTCHAR *nxclass, CONSTCHAR *url); extern NXstatus NXmalloc(void** data, int rank, int dimensions[], int datatype); extern NXstatus NXfree(void** data); @@ -238,6 +252,8 @@ extern NXstatus NXfree(void** data); typedef void (*ErrFunc)(void *data, char *text); extern void NXMSetError(void *pData, ErrFunc); extern ErrFunc NXMGetError(); +extern void NXMDisableErrorReporting(); +extern void NXMEnableErrorReporting(); extern void (*NXIReportError)(void *pData,char *text); extern void *NXpData; extern char *NXIformatNeXusTime(); @@ -266,6 +282,7 @@ extern NXstatus NXsetcache(long newVal); NXstatus ( *nxputslab)(NXhandle handle, void* data, int start[], int size[]); NXstatus ( *nxgetdataID)(NXhandle handle, NXlink* pLink); NXstatus ( *nxmakelink)(NXhandle handle, NXlink* pLink); + NXstatus ( *nxmakenamedlink)(NXhandle handle, CONSTCHAR *newname, NXlink* pLink); NXstatus ( *nxgetdata)(NXhandle handle, void* data); NXstatus ( *nxgetinfo)(NXhandle handle, int* rank, int dimension[], int* datatype); NXstatus ( *nxgetnextentry)(NXhandle handle, NXname name, NXname nxclass, int* datatype); diff --git a/napi4.c b/napi4.c index 8277a836..ff9f8b79 100644 --- a/napi4.c +++ b/napi4.c @@ -3,7 +3,7 @@ Application Program Interface (HDF4) Routines - Copyright (C) 1997-2002 Mark Koennecke, Przemek Klosowski + Copyright (C) 1997-2006 Mark Koennecke, Przemek Klosowski This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -64,13 +64,50 @@ extern void *NXpData; assert(pRes->iNXID == NXSIGNATURE); return pRes; } - + /*----------------------------------------------------------------------*/ +static int findNapiClass(pNexusFile pFile, int groupRef, NXname nxclass) +{ + NXname classText, linkClass; + int32 tags[2], attID, linkID, groupID; + + groupID = Vattach(pFile->iVID,groupRef,"r"); + Vgetclass(groupID, classText); + if(strcmp(classText,"NAPIlink") != 0) + { + /* normal group */ + strcpy(nxclass,classText); + Vdetach(groupID); + return groupRef; + } + else + { + /* code for linked renamed groups */ + attID = Vfindattr(groupID,"NAPIlink"); + if(attID >= 0) + { + Vgetattr(groupID,attID, tags); + linkID = Vattach(pFile->iVID,tags[1],"r"); + Vgetclass(linkID, linkClass); + Vdetach(groupID); + Vdetach(linkID); + strcpy(nxclass,linkClass); + return tags[1]; + } + else + { + /* this allows for finding the NAPIlink group in NXmakenamedlink */ + strcpy(nxclass,classText); + Vdetach(groupID); + return groupRef; + } + } +} /* --------------------------------------------------------------------- */ static int32 NXIFindVgroup (pNexusFile pFile, CONSTCHAR *name, CONSTCHAR *nxclass) { int32 iNew, iRef, iTag; - int iN, i; + int iN, i, status; int32 *pArray = NULL; NXname pText; @@ -93,17 +130,16 @@ extern void *NXpData; for (i = 0; i < iN; i++) { iNew = Vattach (pFile->iVID, pArray[i], "r"); Vgetname (iNew, pText); + Vdetach(iNew); if (strcmp (pText, name) == 0) { - Vgetclass (iNew, pText); + pArray[i] = findNapiClass(pFile,pArray[i],pText); if (strcmp (pText, nxclass) == 0) { /* found ! */ - Vdetach (iNew); iNew = pArray[i]; free (pArray); return iNew; } } - Vdetach (iNew); } /* nothing found */ free (pArray); @@ -115,15 +151,13 @@ extern void *NXpData; if (iTag == DFTAG_VG) { iNew = Vattach (pFile->iVID, iRef, "r"); Vgetname (iNew, pText); + Vdetach(iNew); if (strcmp (pText, name) == 0) { - Vgetclass (iNew, pText); + iRef = findNapiClass(pFile,iRef, pText); if (strcmp (pText, nxclass) == 0) { - /* found ! */ - Vdetach (iNew); return iRef; } } - Vdetach (iNew); } } /* end for */ } /* end else */ @@ -264,8 +298,15 @@ extern void *NXpData; if (pFile->iCurrentSDS != 0) { /* SDS level */ iRet = SDgetinfo (pFile->iCurrentSDS, pNam, &iRank, iDim, &iType, &iAtt); - } else { /* global level */ - iRet = SDfileinfo (pFile->iSID, &iData, &iAtt); + } else { + if(pFile->iCurrentVG == 0){ + /* global level */ + iRet = SDfileinfo (pFile->iSID, &iData, &iAtt); + } else { + /* group attribute */ + iRet = Vnattrs(pFile->iCurrentVG); + iAtt = iRet; + } } if (iRet < 0) { NXIReportError (NXpData, "ERROR: HDF cannot read attribute numbers"); @@ -325,7 +366,7 @@ extern void *NXpData; { pNexusFile pNew = NULL; char pBuffer[512]; - char *time_puffer; + char *time_puffer = NULL; char HDF_VERSION[64]; uint32 lmajor, lminor, lrelease; int32 am1=0; @@ -350,8 +391,6 @@ extern void *NXpData; } memset (pNew, 0, sizeof (NexusFile)); - time_puffer = NXIformatNeXusTime(); - #if WRITE_OLD_IDENT /* not used at moment */ /* * write something that can be used by OLE @@ -402,6 +441,8 @@ extern void *NXpData; return NX_ERROR; } } + + time_puffer = NXIformatNeXusTime(); if (am == NXACC_CREATE || am == NXACC_CREATE4) { if (SDsetattr(pNew->iSID, "file_name", DFNT_CHAR8, strlen(filename), (char*)filename) < 0) { NXIReportError (NXpData, "ERROR: HDF failed to store file_name attribute "); @@ -415,9 +456,11 @@ extern void *NXpData; free(time_puffer); return NX_ERROR; } - free(time_puffer); } } + if (time_puffer != NULL) { + free(time_puffer); + } /* * Otherwise we try to create the file two times which makes HDF @@ -534,8 +577,6 @@ extern void *NXpData; /*------------------------------------------------------------------------*/ - - NXstatus NX4opengroup (NXhandle fid, CONSTCHAR *name, CONSTCHAR *nxclass) { pNexusFile pFile; @@ -566,7 +607,6 @@ extern void *NXpData; NXIKillDir (pFile); return NX_OK; } - /* ------------------------------------------------------------------- */ @@ -661,6 +701,11 @@ extern void *NXpData; { type=DFNT_FLOAT64; } + else + { + NXIReportError (NXpData, "ERROR: invalid type in NX4makedata"); + return NX_ERROR; + } if (rank <= 0) { sprintf (pBuffer, "ERROR: invalid rank specified for SDS %s", @@ -787,6 +832,11 @@ extern void *NXpData; { type=DFNT_FLOAT64; } + else + { + NXIReportError (NXpData, "ERROR: invalid datatype in NX4compmakedata"); + return NX_ERROR; + } if (rank <= 0) { sprintf (pBuffer, "ERROR: invalid rank specified for SDS %s", @@ -877,7 +927,7 @@ extern void *NXpData; else { NXIReportError (NXpData, "Unknown compression method!"); - NX_ERROR; + return NX_ERROR; } /* link into Vgroup, if in one */ if (pFile->iCurrentVG != 0) { @@ -900,7 +950,7 @@ extern void *NXpData; pNexusFile pFile; int32 iRank, iAtt, iType, iRet; int32 iSize[MAX_VAR_DIMS]; - int compress_typei; + int compress_typei = COMP_CODE_NONE; NXname pBuffer; char pError[512]; comp_info compstruct; @@ -962,7 +1012,7 @@ extern void *NXpData; NXstatus NX4opendata (NXhandle fid, CONSTCHAR *name) { pNexusFile pFile; - int32 iNew; + int32 iNew, attID, tags[2]; char pBuffer[256]; int iRet; @@ -986,10 +1036,19 @@ extern void *NXpData; } /* clear pending attribute directories first */ NXIKillAttDir (pFile); - - /* open the SDS */ + + /* open the SDS, thereby watching for linked SDS under a different name */ iNew = SDreftoindex (pFile->iSID, iNew); pFile->iCurrentSDS = SDselect (pFile->iSID, iNew); + attID = SDfindattr(pFile->iCurrentSDS,"NAPIlink"); + if(attID >= 0) + { + SDreadattr(pFile->iCurrentSDS,attID, tags); + SDendaccess(pFile->iCurrentSDS); + iNew = SDreftoindex (pFile->iSID, tags[1]); + pFile->iCurrentSDS = SDselect (pFile->iSID, iNew); + } + if (pFile->iCurrentSDS < 0) { NXIReportError (NXpData, "ERROR: HDF error opening SDS"); pFile->iCurrentSDS = 0; @@ -1105,15 +1164,25 @@ extern void *NXpData; { type=DFNT_FLOAT64; } + else + { + NXIReportError (NXpData, "ERROR: Invalid data type for HDF attribute"); + return NX_ERROR; + } if (pFile->iCurrentSDS != 0) { /* SDS attribute */ iRet = SDsetattr (pFile->iCurrentSDS, (char*)name, (int32)type, (int32)datalen, data); } else { - /* global attribute */ - iRet = SDsetattr (pFile->iSID, (char*)name, (int32)type, - (int32)datalen, data); - + if(pFile->iCurrentVG == 0){ + /* global attribute */ + iRet = SDsetattr (pFile->iSID, (char*)name, (int32)type, + (int32)datalen, data); + } else { + /* group attribute */ + iRet = Vsetattr(pFile->iCurrentVG, (char *)name, (int32) type, + (int32)datalen,data); + } } iType = type; if (iRet < 0) { @@ -1218,15 +1287,66 @@ extern void *NXpData; return NX_ERROR; } Vaddtagref(pFile->iCurrentVG, sLink->iTag, sLink->iRef); + length = strlen(sLink->targetPath); if(sLink->iTag == DFTAG_SDG || sLink->iTag == DFTAG_NDG || sLink->iTag == DFTAG_SDS) { dataID = SDreftoindex(pFile->iSID,sLink->iRef); dataID = SDselect(pFile->iSID,dataID); - length = strlen(sLink->targetPath); SDsetattr(dataID,name,type,length,sLink->targetPath); SDendaccess(dataID); } + else + { + dataID = Vattach(pFile->iVID,sLink->iRef,"w"); + Vsetattr(dataID, (char *)name, type, (int32) length, sLink->targetPath); + Vdetach(dataID); + } + return NX_OK; + } + /* ------------------------------------------------------------------- */ + + + NXstatus NX4makenamedlink (NXhandle fid, CONSTCHAR* newname, NXlink* sLink) + { + pNexusFile pFile; + int32 iVG, iRet, dataID, type = DFNT_CHAR8, length, dataType = NX_CHAR, + rank = 1, attType = NX_INT32; + int iDim[1]; + char name[] = "target"; + int tags[2]; + + pFile = NXIassert (fid); + + if (pFile->iCurrentVG == 0) { /* root level, can not link here */ + return NX_ERROR; + } + + tags[0] = sLink->iTag; + tags[1] = sLink->iRef; + + length = strlen(sLink->targetPath); + if(sLink->iTag == DFTAG_SDG || sLink->iTag == DFTAG_NDG || + sLink->iTag == DFTAG_SDS) + { + iDim[0] = 1; + NX4makedata(fid,newname, dataType,rank,iDim); + NX4opendata(fid,newname); + NX4putattr(fid,"NAPIlink",tags, 2, attType); + NX4closedata(fid); + dataID = SDreftoindex(pFile->iSID,sLink->iRef); + dataID = SDselect(pFile->iSID,dataID); + SDsetattr(dataID,name,type,length,sLink->targetPath); + SDendaccess(dataID); + } else { + NX4makegroup(fid,newname,"NAPIlink"); + NX4opengroup(fid,newname,"NAPIlink"); + NX4putattr(fid,"NAPIlink",tags, 2, attType); + NX4closegroup(fid); + dataID = Vattach(pFile->iVID,sLink->iRef,"w"); + Vsetattr(dataID, (char *)name, type, (int32) length, sLink->targetPath); + Vdetach(dataID); + } return NX_OK; } @@ -1236,8 +1356,7 @@ extern void *NXpData; { pNexusFile pFile; pFile = NXIassert (fid); - printf("HDF4 link: iTag = %ld, iRef = %ld, target=\"%s\"\n", - sLink->iTag, sLink->iRef, sLink->targetPath); + printf("HDF4 link: iTag = %ld, iRef = %ld, target=\"%s\"\n", sLink->iTag, sLink->iRef, sLink->targetPath); return NX_OK; } @@ -1272,6 +1391,10 @@ extern void *NXpData; ac = NXACC_READ; }else if(pFile->iAccess[0] == 'w') { ac = NXACC_RDWR; + } else { + NXIReportError (NXpData, + "ERROR: NX4flush failed to determine file access mode"); + return NX_ERROR; } pCopy = (char *)malloc((strlen(pFileName)+10)*sizeof(char)); if(!pCopy) { @@ -1345,11 +1468,13 @@ extern void *NXpData; return NX_EOD; } } + /* Next case: end of directory */ if (iCurDir >= pFile->iStack[pFile->iStackPtr].iNDir) { NXIKillDir (pFile); return NX_EOD; } + /* Next case: we have data! supply it and increment counter */ if (pFile->iCurrentVG == 0) { /* root level */ iTemp = Vattach (pFile->iVID, @@ -1359,10 +1484,10 @@ extern void *NXpData; return NX_ERROR; } Vgetname (iTemp, name); - Vgetclass (iTemp, nxclass); + Vdetach (iTemp); + findNapiClass(pFile, pFile->iStack[pFile->iStackPtr].iRefDir[iCurDir], nxclass); *datatype = DFTAG_VG; pFile->iStack[pFile->iStackPtr].iCurDir++; - Vdetach (iTemp); return NX_OK; } else { /* in Vgroup */ if (pFile->iStack[iStackPtr].iTagDir[iCurDir] == DFTAG_VG) {/* Vgroup */ @@ -1373,7 +1498,8 @@ extern void *NXpData; return NX_ERROR; } Vgetname (iTemp, name); - Vgetclass (iTemp, nxclass); + Vdetach(iTemp); + findNapiClass(pFile, pFile->iStack[pFile->iStackPtr].iRefDir[iCurDir], nxclass); *datatype = DFTAG_VG; pFile->iStack[pFile->iStackPtr].iCurDir++; Vdetach (iTemp); @@ -1511,7 +1637,7 @@ extern void *NXpData; { pNexusFile pFile; int iRet; - int32 iPType, iCount; + int32 iPType, iCount, count; pFile = NXIassert (fileid); @@ -1528,9 +1654,16 @@ extern void *NXpData; return NX_EOD; } /* well, there must be data to copy */ - if (pFile->iCurrentSDS == 0) { /* global attribute */ - iRet = SDattrinfo (pFile->iSID, pFile->iAtt.iCurDir, - pName, &iPType, &iCount); + if (pFile->iCurrentSDS == 0) { + if(pFile->iCurrentVG == 0) { + /* global attribute */ + iRet = SDattrinfo (pFile->iSID, pFile->iAtt.iCurDir, + pName, &iPType, &iCount); + }else { + /* group attribute */ + iRet = Vattrinfo(pFile->iCurrentVG, pFile->iAtt.iCurDir, + pName, &iPType, &iCount, &count); + } } else { iRet = SDattrinfo (pFile->iCurrentSDS, pFile->iAtt.iCurDir, pName, &iPType, &iCount); @@ -1552,7 +1685,7 @@ extern void *NXpData; NXstatus NX4getattr (NXhandle fid, char *name, void *data, int* datalen, int* iType) { pNexusFile pFile; - int32 iNew, iType32; + int32 iNew, iType32, count; void *pData = NULL; int32 iLen, iRet; int type; @@ -1604,8 +1737,13 @@ extern void *NXpData; /* SDS attribute */ iNew = SDfindattr (pFile->iCurrentSDS, name); } else { - /* global attribute */ - iNew = SDfindattr (pFile->iSID, name); + if(pFile->iCurrentVG == 0){ + /* global attribute */ + iNew = SDfindattr (pFile->iSID, name); + } else { + /* group attribute */ + iNew = Vfindattr(pFile->iCurrentVG, name); + } } if (iNew < 0) { sprintf (pBuffer, "ERROR: attribute %s not found", name); @@ -1617,7 +1755,12 @@ extern void *NXpData; if (pFile->iCurrentSDS != 0) { iRet = SDattrinfo (pFile->iCurrentSDS, iNew, pNam, &iType32, &iLen); } else { - iRet = SDattrinfo (pFile->iSID, iNew, pNam, &iType32, &iLen); + if(pFile->iCurrentVG == 0){ + iRet = SDattrinfo (pFile->iSID, iNew, pNam, &iType32, &iLen); + } else { + iRet = Vattrinfo(pFile->iCurrentVG,iNew,pNam,&iType32,&count, + &iLen); + } } if (iRet < 0) { sprintf (pBuffer, "ERROR: HDF could not read attribute info"); @@ -1637,7 +1780,11 @@ extern void *NXpData; if (pFile->iCurrentSDS != 0) { iRet = SDreadattr (pFile->iCurrentSDS, iNew, pData); } else { - iRet = SDreadattr (pFile->iSID, iNew, pData); + if(pFile->iCurrentVG == 0){ + iRet = SDreadattr (pFile->iSID, iNew, pData); + } else { + iRet = Vgetattr(pFile->iCurrentVG, iNew, pData); + } } if (iRet < 0) { sprintf (pBuffer, "ERROR: HDF could not read attribute data"); @@ -1646,7 +1793,8 @@ extern void *NXpData; } /* copy data to caller */ memset (data, 0, *datalen); - if ((*datalen <= iLen) && (*iType == DFNT_UINT8 || *iType == DFNT_CHAR8 || *iType == DFNT_UCHAR8)) { + if ((*datalen <= iLen) && + (*iType == DFNT_UINT8 || *iType == DFNT_CHAR8 || *iType == DFNT_UCHAR8)) { iLen = *datalen - 1; } memcpy (data, pData, iLen); @@ -1670,8 +1818,14 @@ extern void *NXpData; if (pFile->iCurrentSDS != 0) { /* SDS level */ iRet = SDgetinfo (pFile->iCurrentSDS, pNam, &iRank, iDim, &iType, &iAtt); - } else { /* global level */ - iRet = SDfileinfo (pFile->iSID, &iData, &iAtt); + } else { + if(pFile->iCurrentVG == 0){ + /* global level */ + iRet = SDfileinfo (pFile->iSID, &iData, &iAtt); + } else { + iRet = Vnattrs(pFile->iCurrentVG); + iAtt = iRet; + } } if (iRet < 0) { NXIReportError (NXpData, "NX_ERROR: HDF cannot read attribute numbers"); @@ -1793,6 +1947,7 @@ void NX4assignFunctions(pNexusFunction fHandle) fHandle->nxputslab=NX4putslab; fHandle->nxgetdataID=NX4getdataID; fHandle->nxmakelink=NX4makelink; + fHandle->nxmakenamedlink=NX4makenamedlink; fHandle->nxgetdata=NX4getdata; fHandle->nxgetinfo=NX4getinfo; fHandle->nxgetnextentry=NX4getnextentry; diff --git a/napi5.c b/napi5.c index a860f215..939b9972 100644 --- a/napi5.c +++ b/napi5.c @@ -3,7 +3,7 @@ Application Program Interface (HDF5) Routines - Copyright (C) 1997-2002 Mark Koennecke, Przemek Klosowski + Copyright (C) 1997-2006 Mark Koennecke, Przemek Klosowski This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -30,18 +30,17 @@ #include "napi.h" #include "napi5.h" +#define NX_UNKNOWN_GROUP "" /* for when no NX_class attr */ + extern void *NXpData; typedef struct __NexusFile5 { struct iStack5 { - int *iRefDir; - int *iTagDir; char irefn[1024]; int iVref; int iCurrentIDX; } iStack5[NXMAXSTACK]; struct iStack5 iAtt5; - int iVID; int iFID; int iCurrentG; int iCurrentD; @@ -82,14 +81,6 @@ static void ignoreError(void *data, char *text){ static void NXI5KillDir (pNexusFile5 self) { - if (self->iStack5[self->iStackPtr].iRefDir) { - free (self->iStack5[self->iStackPtr].iRefDir); - self->iStack5[self->iStackPtr].iRefDir = NULL; - } - if (self->iStack5[self->iStackPtr].iTagDir) { - free (self->iStack5[self->iStackPtr].iTagDir); - self->iStack5[self->iStackPtr].iTagDir = NULL; - } self->iStack5[self->iStackPtr].iCurrentIDX = 0; } @@ -97,17 +88,27 @@ static void ignoreError(void *data, char *text){ static void NXI5KillAttDir (pNexusFile5 self) { - if (self->iAtt5.iRefDir) { - free (self->iAtt5.iRefDir); - self->iAtt5.iRefDir = NULL; - } - if (self->iAtt5.iTagDir) { - free (self->iAtt5.iTagDir); - self->iAtt5.iTagDir = NULL; - } self->iAtt5.iCurrentIDX = 0; } +/*---------------------------------------------------------------------*/ +static void buildCurrentPath(pNexusFile5 self, char *pathBuffer, + int pathBufferLen){ + int length; + memset(pathBuffer,0,pathBufferLen); + if(self->iCurrentG != 0) { + strcpy(pathBuffer,"/"); + if(strlen(self->name_ref) + 1 < pathBufferLen){ + strcat(pathBuffer, self->name_ref); + } + } + if(self->iCurrentD != 0){ + strcat(pathBuffer,"/"); + if(strlen(self->iCurrentLD) + strlen(pathBuffer) < pathBufferLen){ + strcat(pathBuffer,self->iCurrentLD); + } + } +} /* ---------------------------------------------------------------------- Definition of NeXus API @@ -117,14 +118,14 @@ static void ignoreError(void *data, char *text){ NXstatus NX5open(CONSTCHAR *filename, NXaccess am, NXhandle* pHandle) { - hid_t attr1,aid1, aid2; + hid_t attr1,aid1, aid2, iVID; pNexusFile5 pNew = NULL; char pBuffer[512]; - char *time_buffer; + char *time_buffer = NULL; char version_nr[10]; int iRet; unsigned int vers_major, vers_minor, vers_release, am1 ; - hid_t fapl; + hid_t fapl = -1; int mdc_nelmts; #ifdef H5_WANT_H5_V1_4_COMPAT int rdcc_nelmts; @@ -149,7 +150,6 @@ static void ignoreError(void *data, char *text){ } memset (pNew, 0, sizeof (NexusFile5)); - time_buffer = NXIformatNeXusTime(); /* start HDF5 interface */ if (am == NXACC_CREATE5) { @@ -181,6 +181,9 @@ static void ignoreError(void *data, char *text){ free (pNew); return NX_ERROR; } + if(fapl != -1) { + H5Pclose(fapl); + } /* * need to create global attributes file_name file_time NeXus_version @@ -188,15 +191,15 @@ static void ignoreError(void *data, char *text){ */ if (am1 != H5F_ACC_RDONLY) { - pNew->iVID=H5Gopen(pNew->iFID,"/"); + iVID=H5Gopen(pNew->iFID,"/"); aid2 = H5Screate(H5S_SCALAR); aid1 = H5Tcopy(H5T_C_S1); H5Tset_size(aid1, strlen(NEXUS_VERSION)); if (am1 == H5F_ACC_RDWR) { - H5Adelete(pNew->iVID, "NeXus_version"); + H5Adelete(iVID, "NeXus_version"); } - attr1= H5Acreate(pNew->iVID, "NeXus_version", aid1, aid2, H5P_DEFAULT); + attr1= H5Acreate(iVID, "NeXus_version", aid1, aid2, H5P_DEFAULT); if (attr1<0) { NXIReportError (NXpData, @@ -214,15 +217,15 @@ static void ignoreError(void *data, char *text){ iRet = H5Sclose(aid2); /* Close attribute */ iRet = H5Aclose(attr1); - H5Gclose(pNew->iVID); + H5Gclose(iVID); } if (am1 == H5F_ACC_TRUNC) { - pNew->iVID=H5Gopen(pNew->iFID,"/"); + iVID=H5Gopen(pNew->iFID,"/"); aid2=H5Screate(H5S_SCALAR); aid1 = H5Tcopy(H5T_C_S1); H5Tset_size(aid1, strlen(filename)); - attr1= H5Acreate(pNew->iVID, "file_name", aid1, aid2, H5P_DEFAULT); + attr1= H5Acreate(iVID, "file_name", aid1, aid2, H5P_DEFAULT); if (attr1 < 0) { NXIReportError (NXpData, @@ -244,7 +247,7 @@ static void ignoreError(void *data, char *text){ aid2=H5Screate(H5S_SCALAR); aid1 = H5Tcopy(H5T_C_S1); H5Tset_size(aid1, strlen(version_nr)); - attr1= H5Acreate(pNew->iVID, "HDF5_Version", aid1, aid2, H5P_DEFAULT); + attr1= H5Acreate(iVID, "HDF5_Version", aid1, aid2, H5P_DEFAULT); if (attr1 < 0) { NXIReportError (NXpData, @@ -261,11 +264,12 @@ static void ignoreError(void *data, char *text){ iRet = H5Sclose(aid2); iRet = H5Aclose(attr1); /*----------- file time */ + time_buffer = NXIformatNeXusTime(); if(time_buffer != NULL){ aid2=H5Screate(H5S_SCALAR); aid1 = H5Tcopy(H5T_C_S1); H5Tset_size(aid1, strlen(time_buffer)); - attr1=H5Acreate(pNew->iVID, "file_time", aid1, aid2, H5P_DEFAULT); + attr1=H5Acreate(iVID, "file_time", aid1, aid2, H5P_DEFAULT); if (attr1 < 0) { NXIReportError (NXpData, @@ -287,7 +291,7 @@ static void ignoreError(void *data, char *text){ iRet = H5Aclose(attr1); free(time_buffer); } - H5Gclose(pNew->iVID); + H5Gclose(iVID); } /* Set HDFgroup access mode */ if (am1 == H5F_ACC_RDONLY) { @@ -316,9 +320,9 @@ static void ignoreError(void *data, char *text){ H5Fget_obj_count(pFile->iFID,H5F_OBJ_ALL)); */ iRet = H5Fclose(pFile->iFID); - - /* - Please leave this here, it helps debugging HDF5 resource leakages + + /* + leave this here: it helps in debugging leakage problems printf("HDF5 object count after close: %d\n", H5Fget_obj_count(H5F_OBJ_ALL,H5F_OBJ_ALL)); printf("HDF5 dataset count after close: %d\n", @@ -353,7 +357,7 @@ static void ignoreError(void *data, char *text){ NXstatus NX5makegroup (NXhandle fid, CONSTCHAR *name, CONSTCHAR *nxclass) { pNexusFile5 pFile; - hid_t iRet; + hid_t iRet, iVID; hid_t attr1,aid1, aid2; char pBuffer[1024] = ""; @@ -372,12 +376,12 @@ static void ignoreError(void *data, char *text){ NXIReportError (NXpData, "ERROR: HDF could not create Group"); return NX_ERROR; } - pFile->iVID = iRet; + iVID = iRet; strncpy(pFile->name_ref,pBuffer,1023); aid2 = H5Screate(H5S_SCALAR); aid1 = H5Tcopy(H5T_C_S1); H5Tset_size(aid1, strlen(nxclass)); - attr1= H5Acreate(pFile->iVID, "NX_class", aid1, aid2, H5P_DEFAULT); + attr1= H5Acreate(iVID, "NX_class", aid1, aid2, H5P_DEFAULT); if (attr1 < 0) { NXIReportError (NXpData, "ERROR: HDF failed to store class name!"); @@ -392,7 +396,7 @@ static void ignoreError(void *data, char *text){ iRet=H5Sclose(aid2); iRet=H5Tclose(aid1); iRet=H5Aclose(attr1); - iRet=H5Gclose(pFile->iVID); + iRet=H5Gclose(iVID); return NX_OK; } @@ -405,7 +409,7 @@ static void ignoreError(void *data, char *text){ strcpy(attr_name,"NX_class"); return strstr(member_name, attr_name) ? 1 : 0; } - + /*------------------------------------------------------------------------*/ NXstatus NX5opengroup (NXhandle fid, CONSTCHAR *name, CONSTCHAR *nxclass) { @@ -417,93 +421,59 @@ static void ignoreError(void *data, char *text){ pFile = NXI5assert (fid); if (pFile->iCurrentG == 0) { - iRet = H5Gopen (pFile->iFID, (const char *)name); - if (iRet < 0) { - sprintf (pBuffer, "ERROR: Group %s does not exist!", name); - NXIReportError (NXpData, pBuffer); - return NX_ERROR; - } - pFile->iCurrentG = iRet; - /* check group attribute */ - iRet = H5Aiterate(pFile->iCurrentG,NULL,attr_check,NULL); - if (iRet < 0) { - NXIReportError (NXpData, "ERROR iterating through group!"); - return NX_ERROR; - } else if (iRet == 1) { - /* group attribute was found */ - } else { - /* no group attribute available */ - NXIReportError (NXpData, "No group attribute available"); - return NX_ERROR; - } - /* check contents of group attribute */ - attr1 = H5Aopen_name(pFile->iCurrentG, "NX_class"); - if (attr1 < 0) - { - NXIReportError (NXpData, "Error opening group attribute!"); - return NX_ERROR; - } - atype=H5Tcopy(H5T_C_S1); - H5Tset_size(atype,128); - iRet = H5Aread(attr1, atype, data); - if (strcmp(data, nxclass) == 0) { - /* test OK */ - } else { - NXIReportError (NXpData, "Group class is not identical!"); - iRet = H5Tclose(atype); - iRet = H5Aclose(attr1); - return NX_ERROR; - } - iRet = H5Tclose(atype); - iRet = H5Aclose(attr1); - pFile->iStack5[pFile->iStackPtr].iVref=0; - strcpy(pFile->iStack5[pFile->iStackPtr].irefn,""); - strcpy(pFile->name_ref,name); - strcpy(pFile->name_tmp,name); - } else { + strcpy(pBuffer,name); + } + else + { sprintf(pBuffer,"%s/%s",pFile->name_tmp,name); - iRet = H5Gopen (pFile->iFID,(const char *)pBuffer); - if (iRet < 0) { - sprintf (pBuffer, "ERROR: Group %s does not exist!", pFile->name_tmp); - NXIReportError (NXpData, pBuffer); - return NX_ERROR; - } - pFile->iCurrentG = iRet; - strcpy(pFile->name_tmp,pBuffer); - strcpy(pFile->name_ref,pBuffer); - /* check group attribute */ - iRet=H5Aiterate(pFile->iCurrentG,NULL,attr_check,NULL); - if (iRet < 0) { + } + iRet = H5Gopen (pFile->iFID,(const char *)pBuffer); + if (iRet < 0) { + sprintf (pBuffer, "ERROR: Group %s does not exist!", pFile->name_tmp); + NXIReportError (NXpData, pBuffer); + return NX_ERROR; + } + pFile->iCurrentG = iRet; + strcpy(pFile->name_tmp,pBuffer); + strcpy(pFile->name_ref,pBuffer); + + if ((nxclass != NULL) && (strcmp(nxclass, NX_UNKNOWN_GROUP) != 0)) + { + /* check group attribute */ + iRet=H5Aiterate(pFile->iCurrentG,NULL,attr_check,NULL); + if (iRet < 0) { NXIReportError (NXpData, "ERROR iterating through group!"); return NX_ERROR; - } else if (iRet == 1) { + } else if (iRet == 1) { /* group attribute was found */ - } else { + } else { /* no group attribute available */ NXIReportError (NXpData, "No group attribute available"); return NX_ERROR; - } - /* check contains of group attribute */ - attr1 = H5Aopen_name(pFile->iCurrentG, "NX_class"); - if (attr1 < 0) - { - NXIReportError (NXpData, "Error opening group attribute!"); - return NX_ERROR; - } - atype=H5Tcopy(H5T_C_S1); - H5Tset_size(atype,128); - iRet = H5Aread(attr1, atype, data); - if (strcmp(data, nxclass) == 0) { - /* test OK */ - } else { - NXIReportError (NXpData, "Group class is not identical!"); - iRet = H5Tclose(atype); - iRet = H5Aclose(attr1); - return NX_ERROR; - } - iRet = H5Tclose(atype); - iRet = H5Aclose(attr1); + } + /* check contents of group attribute */ + attr1 = H5Aopen_name(pFile->iCurrentG, "NX_class"); + if (attr1 < 0) + { + NXIReportError (NXpData, "Error opening NX_class group attribute!"); + return NX_ERROR; + } + atype=H5Tcopy(H5T_C_S1); + H5Tset_size(atype,128); + iRet = H5Aread(attr1, atype, data); + if (strcmp(data, nxclass) == 0) { + /* test OK */ + } else { + NXIReportError (NXpData, "Group class is not identical!"); + iRet = H5Tclose(atype); + iRet = H5Aclose(attr1); + return NX_ERROR; + } + iRet = H5Tclose(atype); + iRet = H5Aclose(attr1); } + + /* maintain stack */ pFile->iStackPtr++; pFile->iStack5[pFile->iStackPtr].iVref=pFile->iCurrentG; strcpy(pFile->iStack5[pFile->iStackPtr].irefn,name); @@ -573,30 +543,10 @@ static void ignoreError(void *data, char *text){ } return NX_OK; } - - /* --------------------------------------------------------------------- */ - - NXstatus NX5compmakedata (NXhandle fid, CONSTCHAR *name, - int datatype, - int rank, int dimensions[], - int compress_type, int chunk_size[]) - { - hid_t datatype1, dataspace, iNew, iRet; - hid_t type,cparms; - pNexusFile5 pFile; - char pBuffer[256]; - int i, byte_zahl; - hsize_t chunkdims[H5S_MAX_RANK]; - hsize_t mydim[H5S_MAX_RANK], mydim1[H5S_MAX_RANK]; - hsize_t size[2]; - hsize_t maxdims[1] = {H5S_UNLIMITED}; - - pFile = NXI5assert (fid); - - for (i = 0; i < rank; i++) - { - chunkdims[i]=chunk_size[i]; - } +/*-----------------------------------------------------------------------*/ +static int nxToHDF5Type(int datatype) +{ + int type; if (datatype == NX_CHAR) { type=H5T_C_S1; @@ -625,6 +575,14 @@ static void ignoreError(void *data, char *text){ { type=H5T_NATIVE_UINT; } + else if (datatype == NX_INT64) + { + type = H5T_NATIVE_INT64; + } + else if (datatype == NX_UINT64) + { + type = H5T_NATIVE_UINT64; + } else if (datatype == NX_FLOAT32) { type=H5T_NATIVE_FLOAT; @@ -633,6 +591,43 @@ static void ignoreError(void *data, char *text){ { type=H5T_NATIVE_DOUBLE; } + else + { + NXIReportError (NXpData, "ERROR: nxToHDF5Type: unknown type"); + type = -1; + } + return type; +} + + /* --------------------------------------------------------------------- */ + + NXstatus NX5compmakedata (NXhandle fid, CONSTCHAR *name, + int datatype, + int rank, int dimensions[], + int compress_type, int chunk_size[]) + { + hid_t datatype1, dataspace, iNew, iRet; + hid_t type, cparms = -1; + pNexusFile5 pFile; + char pBuffer[256]; + int i, byte_zahl = 0; + hsize_t chunkdims[H5S_MAX_RANK]; + hsize_t mydim[H5S_MAX_RANK], mydim1[H5S_MAX_RANK]; + hsize_t size[H5S_MAX_RANK]; + hsize_t maxdims[H5S_MAX_RANK]; + + pFile = NXI5assert (fid); + + for (i = 0; i < rank; i++) + { + chunkdims[i]=chunk_size[i]; + mydim[i] = dimensions[i]; + maxdims[i] = dimensions[i]; + size[i] = dimensions[i]; + } + + type = nxToHDF5Type(datatype); + if (rank <= 0) { sprintf (pBuffer, "ERROR: invalid rank specified %s", name); @@ -640,7 +635,7 @@ static void ignoreError(void *data, char *text){ return NX_ERROR; } /* - Check dimensions for consistency. The first dimension may be 0 + Check dimensions for consistency. The first dimension may be -1 thus denoting an unlimited dimension. */ for (i = 1; i < rank; i++) { @@ -654,24 +649,39 @@ static void ignoreError(void *data, char *text){ } if (datatype == NX_CHAR) { - byte_zahl=dimensions[0]; - dimensions[0]=rank; +/* + * This assumes string lenght is in the last dimensions and + * the logic must be the same as used in NX5getslab and NX5getinfo + * + * search for tests on H5T_STRING + */ + byte_zahl=dimensions[rank-1]; for(i = 0; i < rank; i++) { mydim1[i] = dimensions[i]; } - dimensions[0] = byte_zahl; - dataspace=H5Screate_simple(rank,mydim1,NULL); + mydim1[rank-1] = 1; + if (dimensions[rank-1] > 1) + { + mydim[rank-1] = maxdims[rank-1] = size[rank-1] = 1; + } + if (chunkdims[rank-1] > 1) + { + chunkdims[rank-1] = 1; + } + if (dimensions[0] == NX_UNLIMITED) + { + mydim1[0] = 1; + maxdims[0] = H5S_UNLIMITED; + } + dataspace=H5Screate_simple(rank,mydim1,maxdims); } else { if (dimensions[0] == NX_UNLIMITED) { - mydim[0]=0; + mydim[0] = 1; + maxdims[0] = H5S_UNLIMITED; dataspace=H5Screate_simple(rank, mydim, maxdims); } else { - for(i = 0; i < rank; i++) - { - mydim[i] = dimensions[i]; - } /* dataset creation */ dataspace=H5Screate_simple(rank, mydim, NULL); } @@ -680,6 +690,7 @@ static void ignoreError(void *data, char *text){ if (datatype == NX_CHAR) { H5Tset_size(datatype1, byte_zahl); +/* H5Tset_strpad(H5T_STR_SPACEPAD); */ } if(compress_type == NX_COMP_LZW) { @@ -722,7 +733,6 @@ static void ignoreError(void *data, char *text){ if (dimensions[0] == NX_UNLIMITED) { size[0] = 1; - size[1] = 1; iNew = H5Dextend (pFile->iCurrentD, size); if (iNew < 0) { sprintf (pBuffer, "ERROR: cannot create Dataset %s, check arguments", @@ -731,6 +741,9 @@ static void ignoreError(void *data, char *text){ return NX_ERROR; } } + if (cparms != -1) { + iRet = H5Pclose(cparms); + } iRet = H5Sclose(dataspace); iRet = H5Tclose(datatype1); iRet = H5Dclose(pFile->iCurrentD); @@ -860,7 +873,27 @@ static void ignoreError(void *data, char *text){ } return NX_OK; } - +/*------------------------------------------------------------------*/ +static int getAttVID(pNexusFile5 pFile){ + int vid; + if(pFile->iCurrentG == 0 && pFile->iCurrentD == 0){ + /* global attribute */ + vid = H5Gopen(pFile->iFID,"/"); + } else if(pFile->iCurrentD != 0) { + /* dataset attribute */ + vid = pFile->iCurrentD; + } else { + /* group attribute */; + vid = pFile->iCurrentG; + } + return vid; +} +/*---------------------------------------------------------------*/ +static void killAttVID(pNexusFile5 pFile, int vid){ + if(pFile->iCurrentG == 0 && pFile->iCurrentD == 0){ + H5Gclose(vid); + } +} /* ------------------------------------------------------------------- */ NXstatus NX5putattr (NXhandle fid, CONSTCHAR *name, void *data, @@ -870,111 +903,46 @@ static void ignoreError(void *data, char *text){ hid_t attr1, aid1, aid2; hid_t type; int iRet; - + int vid; + pFile = NXI5assert (fid); - if (iType == NX_CHAR) - { - type=H5T_C_S1; - } - else if (iType == NX_INT8) - { - type=H5T_NATIVE_CHAR; - } - else if (iType == NX_UINT8) - { - type=H5T_NATIVE_UCHAR; - } - else if (iType == NX_INT16) - { - type=H5T_NATIVE_SHORT; - } - else if (iType == NX_UINT16) - { - type=H5T_NATIVE_USHORT; - } - else if (iType == NX_INT32) - { - type=H5T_NATIVE_INT; - } - else if (iType == NX_UINT32) - { - type=H5T_NATIVE_UINT; - } - else if (iType == NX_FLOAT32) - { - type=H5T_NATIVE_FLOAT; - } - else if (iType == NX_FLOAT64) - { - type=H5T_NATIVE_DOUBLE; - } - if (pFile->iCurrentD != 0) { - /* Dataset attribute */ - aid2=H5Screate(H5S_SCALAR); - aid1=H5Tcopy(type); - if (iType == NX_CHAR) - { - H5Tset_size(aid1,datalen); - } - iRet = H5Aopen_name(pFile->iCurrentD, name); - if (iRet>0) { - H5Aclose(iRet); - iRet=H5Adelete(pFile->iCurrentD,name); - if (iRet<0) { - NXIReportError (NXpData, "ERROR: Old attribute cannot removed! "); - return NX_ERROR; - } - } - attr1 = H5Acreate(pFile->iCurrentD, name, aid1, aid2, H5P_DEFAULT); - if (attr1 < 0) { - NXIReportError (NXpData, "ERROR: Attribute cannot created! "); - return NX_ERROR; - } - if (H5Awrite(attr1,aid1,data) < 0) - { - NXIReportError (NXpData, "ERROR: HDF failed to store attribute "); - return NX_ERROR; - } - /* Close attribute dataspace */ - iRet=H5Tclose(aid1); - iRet=H5Sclose(aid2); - /* Close attribute */ - iRet=H5Aclose(attr1); - } else { - /* global attribute */ - pFile->iVID=H5Gopen(pFile->iFID,"/"); - aid2=H5Screate(H5S_SCALAR); - aid1=H5Tcopy(type); - if (iType == NX_CHAR) - { - H5Tset_size(aid1,datalen); - } - iRet = H5Aopen_name(pFile->iVID, name); - if (iRet>0) { - H5Aclose(iRet); - iRet=H5Adelete(pFile->iVID,name); - if (iRet<0) { - NXIReportError (NXpData, "ERROR: Old attribute cannot removed! "); - return NX_ERROR; - } - } - attr1 = H5Acreate(pFile->iVID, name, aid1, aid2, H5P_DEFAULT); - if (attr1 < 0) { - NXIReportError (NXpData, "ERROR: Attribute cannot created! "); - return NX_ERROR; - } - if (H5Awrite(attr1,aid1,data) < 0) - { - NXIReportError (NXpData, "ERROR: HDf failed to store attribute "); - return NX_ERROR; - } - /* Close attribute dataspace */ - iRet=H5Tclose(aid1); - iRet=H5Sclose(aid2); - /* Close attribute */ - iRet=H5Aclose(attr1); - H5Gclose(pFile->iVID); + + type = nxToHDF5Type(iType); + + /* determine vid */ + vid = getAttVID(pFile); + aid2=H5Screate(H5S_SCALAR); + aid1=H5Tcopy(type); + if (iType == NX_CHAR){ + H5Tset_size(aid1,datalen); + } + iRet = H5Aopen_name(vid, name); + if (iRet>0) { + H5Aclose(iRet); + iRet=H5Adelete(vid,name); + if (iRet<0) { + NXIReportError (NXpData, "ERROR: Old attribute cannot removed! "); + killAttVID(pFile,vid); + return NX_ERROR; + } + } + attr1 = H5Acreate(vid, name, aid1, aid2, H5P_DEFAULT); + if (attr1 < 0) { + NXIReportError (NXpData, "ERROR: Attribute cannot created! "); + killAttVID(pFile,vid); + return NX_ERROR; + } + if (H5Awrite(attr1,aid1,data) < 0) { + NXIReportError (NXpData, "ERROR: HDF failed to store attribute "); + killAttVID(pFile,vid); + return NX_ERROR; } + /* Close attribute dataspace */ + iRet=H5Tclose(aid1); + iRet=H5Sclose(aid2); + /* Close attribute */ + iRet=H5Aclose(attr1); + killAttVID(pFile,vid); return NX_OK; } @@ -985,9 +953,9 @@ static void ignoreError(void *data, char *text){ pNexusFile5 pFile; int iRet, i; int rank; - hssize_t myStart[H5S_MAX_RANK]; + hsize_t myStart[H5S_MAX_RANK]; hsize_t mySize[H5S_MAX_RANK]; - hsize_t size[1],maxdims[H5S_MAX_RANK]; + hsize_t size[H5S_MAX_RANK],maxdims[H5S_MAX_RANK]; hid_t filespace,dataspace; pFile = NXI5assert (fid); @@ -997,17 +965,29 @@ static void ignoreError(void *data, char *text){ return NX_ERROR; } rank = H5Sget_simple_extent_ndims(pFile->iCurrentS); + iRet = H5Sget_simple_extent_dims(pFile->iCurrentS, NULL, maxdims); for(i = 0; i < rank; i++) { myStart[i] = iStart[i]; mySize[i] = iSize[i]; + size[i] = iSize[i]; + } + if (H5Tget_class(pFile->iCurrentT) == H5T_STRING) + { + mySize[rank - 1] = 1; + myStart[rank - 1] = 0; } - iRet = H5Sget_simple_extent_dims(pFile->iCurrentS, NULL, maxdims); dataspace = H5Screate_simple (rank, mySize, NULL); if (maxdims[0] == NX_UNLIMITED) { size[0]=iStart[0] + iSize[0]; iRet = H5Dextend(pFile->iCurrentD, size); + if (iRet < 0) + { + NXIReportError (NXpData, "ERROR: extend slab failed"); + return NX_ERROR; + } + filespace = H5Dget_space(pFile->iCurrentD); /* define slab */ @@ -1022,6 +1002,10 @@ static void ignoreError(void *data, char *text){ /* write slab */ iRet = H5Dwrite(pFile->iCurrentD, pFile->iCurrentT, dataspace, filespace, H5P_DEFAULT,data); + if (iRet < 0) + { + NXIReportError (NXpData, "ERROR: writing slab failed"); + } iRet = H5Sclose(filespace); } else { /* define slab */ @@ -1036,12 +1020,16 @@ static void ignoreError(void *data, char *text){ /* write slab */ iRet = H5Dwrite(pFile->iCurrentD, pFile->iCurrentT, dataspace, pFile->iCurrentS, H5P_DEFAULT,data); + if (iRet < 0) + { + NXIReportError (NXpData, "ERROR: writing slab failed"); + } } /* deal with HDF errors */ iRet = H5Sclose(dataspace); if (iRet < 0) { - NXIReportError (NXpData, "ERROR: writing slab failed"); + NXIReportError (NXpData, "ERROR: closing slab failed"); return NX_ERROR; } return NX_OK; @@ -1063,15 +1051,21 @@ static void ignoreError(void *data, char *text){ if(pFile->iCurrentD <= 0){ return NX_ERROR; } - strcpy(sRes->iTag5,""); - strcpy(sRes->iRef5,"/"); - strcat(sRes->iRef5,pFile->name_ref); - strcpy(sRes->iRefd,pFile->iCurrentLD); + + /* + this means: if the item is already linked: use the target attribute else, + the path to the current node + */ oldErr = NXMGetError(); NXMSetError(NXpData, ignoreError); datalen = 1024; memset(&sRes->targetPath,0,datalen*sizeof(char)); - NX5getattr(fid,"target",&sRes->targetPath,&datalen,&type); + if(NX5getattr(fid,"target",&sRes->targetPath,&datalen,&type) != NX_OK) + { + buildCurrentPath(pFile, sRes->targetPath, 1024); + } + NXMSetError(NXpData,oldErr); + sRes->linkType = 1; return NX_OK; } @@ -1081,79 +1075,139 @@ static void ignoreError(void *data, char *text){ { pNexusFile5 pFile; pFile = NXI5assert (fid); - printf("HDF5 link: iTag5 = \"%s\", iRef5 = \"%s\", iRefd = \"%s\"\n", sLink->iTag5, sLink->iRef5, sLink->iRefd); + printf("HDF5 link: targetPath = \"%s\", linkType = \"%d\"\n", sLink->targetPath, sLink->linkType); return NX_OK; } +/*--------------------------------------------------------------------*/ +static NXstatus NX5settargetattribute(pNexusFile5 pFile, NXlink *sLink) +{ + herr_t length, dataID, status, aid2, aid1, attID; + int type = NX_CHAR; + char name[] = "target"; - /* ------------------------------------------------------------------- */ - - NXstatus NX5makelink (NXhandle fid, NXlink* sLink) - { + length = strlen(sLink->targetPath); + /* + set the target attribute + */ + if(sLink->linkType > 0) + { + dataID = H5Dopen(pFile->iFID,sLink->targetPath); + } else { + dataID = H5Gopen(pFile->iFID,sLink->targetPath); + } + if(dataID < 0) + { + NXIReportError(NXpData,"Internal error, path to link does not exist"); + return NX_ERROR; + } + status = H5Aopen_name(dataID,name); + if(status > 0) + { + H5Aclose(status); + status = H5Adelete(dataID,name); + if(status < 0) + { + return NX_OK; + } + } + aid2 = H5Screate(H5S_SCALAR); + aid1 = H5Tcopy(H5T_C_S1); + H5Tset_size(aid1,strlen(sLink->targetPath)); + attID = H5Acreate(dataID,name,aid1,aid2,H5P_DEFAULT); + if(attID < 0) + { + return NX_OK; + } + status = H5Awrite(attID,aid1,sLink->targetPath); + H5Tclose(aid1); + H5Sclose(aid2); + H5Aclose(attID); + if(sLink->linkType > 0){ + H5Dclose(dataID); + } else { + H5Gclose(dataID); + } + return NX_OK; +} +/*---------------------------------------------------------------------*/ +NXstatus NX5makenamedlink(NXhandle fid, CONSTCHAR *name, NXlink *sLink) +{ pNexusFile5 pFile; -/* int iRet; */ - herr_t status, dataID, aid1, aid2, attID; - int size_type; - char buffer[1024]; - char name[] = "target"; + char linkTarget[1024]; + int type = NX_CHAR, length; + int status; pFile = NXI5assert (fid); if (pFile->iCurrentG == 0) { /* root level, can not link here */ return NX_ERROR; } - size_type = strlen(sLink->iRefd); - if (size_type > 0) + + /* + build pathname to link from our current group and the name + of the thing to link + */ + if(strlen(pFile->name_ref) + strlen(name) + 2 < 1024) { - /* dataset link */ - strcpy(sLink->iTag5,pFile->name_ref); - } else { - /* group link */ - strcpy(buffer,pFile->name_ref); - strcat(buffer, sLink->iTag5); - strcpy(sLink->iTag5,"/"); - strcat(sLink->iTag5,buffer); + strcpy(linkTarget,"/"); + strcat(linkTarget,pFile->name_ref); + strcat(linkTarget,"/"); + strcat(linkTarget,name); } - if (size_type>0) + else { - strcat(sLink->iRef5,"/"); - strcat(sLink->iRef5,sLink->iRefd); + NXIReportError(NXpData,"Path string to long"); + return NX_ERROR; + } + + status = H5Glink(pFile->iFID, H5G_LINK_HARD, sLink->targetPath, linkTarget); + + return NX5settargetattribute(pFile,sLink); +} + /* ------------------------------------------------------------------- */ + + NXstatus NX5makelink (NXhandle fid, NXlink* sLink) + { + pNexusFile5 pFile; + char linkTarget[1024]; + int type = NX_CHAR, length; + char *itemName = NULL; + int status; + + pFile = NXI5assert (fid); + if (pFile->iCurrentG == 0) { /* root level, can not link here */ + return NX_ERROR; } - if (size_type>0) + + /* + locate name of the element to link + */ + itemName = strrchr(sLink->targetPath,'/'); + if(itemName == NULL){ + NXIReportError(NXpData,"Bad link structure"); + return NX_ERROR; + } + itemName++; + + /* + build pathname to link from our current group and the name + of the thing to link + */ + if(strlen(pFile->name_ref) + strlen(itemName) + 2 < 1024) { - strcat(sLink->iTag5,"/"); - strcat(sLink->iTag5,sLink->iRefd); + strcpy(linkTarget,"/"); + strcat(linkTarget,pFile->name_ref); + strcat(linkTarget,"/"); + strcat(linkTarget,itemName); } - status = H5Glink(pFile->iFID, H5G_LINK_HARD, sLink->iRef5, sLink->iTag5); - if(size_type > 0) + else { - dataID = H5Dopen(pFile->iFID,sLink->iRef5); - /* - remove old attribute if existing - */ - status = H5Aopen_name(dataID,name); - if(status > 0) - { - H5Aclose(status); - status = H5Adelete(dataID,name); - if(status < 0) - { - return NX_OK; - } - } - aid2 = H5Screate(H5S_SCALAR); - aid1 = H5Tcopy(H5T_C_S1); - H5Tset_size(aid1,strlen(sLink->iRef5)); - attID = H5Acreate(dataID,name,aid1,aid2,H5P_DEFAULT); - if(attID < 0) - { - return NX_OK; - } - H5Awrite(attID,aid1,sLink->iRef5); - H5Tclose(aid1); - H5Sclose(aid2); - H5Aclose(attID); - H5Dclose(dataID); - } - return NX_OK; + NXIReportError(NXpData,"Path string to long"); + return NX_ERROR; + } + + status = H5Glink(pFile->iFID, H5G_LINK_HARD, sLink->targetPath, linkTarget); + + return NX5settargetattribute(pFile,sLink); } /*----------------------------------------------------------------------*/ @@ -1231,6 +1285,8 @@ static void ignoreError(void *data, char *text){ self.iNX++; *((int*)opdata)=self.iNX; break; + default: + break; } return 0; } @@ -1257,7 +1313,7 @@ static void ignoreError(void *data, char *text){ strcpy (pName,pFile->name_ref); attr_id = H5Aopen_name(pFile->iCurrentG,"NX_class"); if (attr_id<0) { - strcpy(pClass,"non"); + strcpy(pClass, NX_UNKNOWN_GROUP); } else { atype=H5Tcopy(H5T_C_S1); H5Tset_size(atype,64); @@ -1271,87 +1327,13 @@ static void ignoreError(void *data, char *text){ } return NX_OK; } - - /*-------------------------------------------------------------------------*/ - NXstatus NX5getnextentry (NXhandle fid,NXname name, NXname nxclass, int *datatype) - { - pNexusFile5 pFile; - hid_t grp, attr1,type,atype; - int iRet,iPtype, i; - int idx,data_id,size_id, sign_id; - char data[128]; - char ph_name[1024]; - info_type op_data; - int iRet_iNX=-1; - char pBuffer[256]; - - pFile = NXI5assert (fid); - op_data.iname = NULL; - idx=pFile->iStack5[pFile->iStackPtr].iCurrentIDX; - if (strlen(pFile->name_ref) == 0) { - /* root group */ - strcpy(pFile->name_ref,"/"); - } - iRet=H5Giterate(pFile->iFID,pFile->name_ref,&idx,nxgroup_info,&op_data); - strcpy(nxclass,""); - - if (pFile->iCurrentG == 0) { - pFile->iNX=0; - iRet_iNX = H5Giterate(pFile->iFID,"/",0,group_info1,&pFile->iNX); - } else { - pFile->iNX=0; - iRet_iNX = H5Giterate(pFile->iFID,pFile->name_ref,0,group_info1, &pFile->iNX); - } - if (idx == pFile->iNX) { - iRet_iNX = 2; - } - - if (iRet > 0) - { - pFile->iStack5[pFile->iStackPtr].iCurrentIDX++; - if (op_data.iname != NULL) { - strcpy(name,op_data.iname); - free(op_data.iname); - } else { - pFile->iStack5[pFile->iStackPtr].iCurrentIDX = 0; - return NX_EOD; - } - if (op_data.type == H5G_GROUP) - { - strcpy(ph_name,""); - for(i = 1; i < (pFile->iStackPtr + 1); i++) - { - strcat(ph_name,pFile->iStack5[i].irefn); - strcat(ph_name,"/"); - } - strcat(ph_name,name); - grp=H5Gopen(pFile->iFID,ph_name); - if (grp < 0) { - sprintf (pBuffer, "ERROR: Group %s does not exist!", ph_name); - NXIReportError (NXpData, pBuffer); - return NX_ERROR; - } - attr1 = H5Aopen_name(grp, "NX_class"); - if (attr1 < 0) { - H5Gclose(grp); - NXIReportError (NXpData, "Error opening group class"); - return NX_ERROR; - } - type=H5T_C_S1; - atype=H5Tcopy(type); - H5Tset_size(atype,128); - iRet = H5Aread(attr1, atype, data); - strcpy(nxclass,data); - H5Tclose(atype); - H5Gclose(grp); - H5Aclose(attr1); - } else if (op_data.type==H5G_DATASET) - { - grp=H5Dopen(pFile->iCurrentG,name); - type=H5Dget_type(grp); - atype=H5Tcopy(type); - data_id = H5Tget_class(atype); +/*------------------------------------------------------------------------*/ +static int hdf5ToNXType(int data_id, hid_t atype) +{ + int iPtype = -1; + hid_t sign_id, size_id; + if (data_id==H5T_STRING) { iPtype=NX_CHAR; @@ -1386,6 +1368,15 @@ static void ignoreError(void *data, char *text){ } else { iPtype=NX_UINT32; } + } + else if(size_id == 8) + { + if (sign_id==H5T_SGN_2) + { + iPtype=NX_INT64; + } else { + iPtype=NX_UINT64; + } } } else if (data_id==H5T_FLOAT) { @@ -1399,6 +1390,170 @@ static void ignoreError(void *data, char *text){ iPtype=NX_FLOAT64; } } + if (iPtype == -1) + { + NXIReportError (NXpData, "ERROR: hdf5ToNXtype: invalid type"); + } + + return iPtype; +} +/*--------------------------------------------------------------------------*/ +static int h5MemType(hid_t atype) +{ + hid_t data_id, size_id, sign_id, memtype_id = -1; + + data_id = H5Tget_class(atype); + + if (data_id==H5T_INTEGER) + { + size_id=H5Tget_size(atype); + sign_id=H5Tget_sign(atype); + if (size_id==1) + { + if (sign_id==H5T_SGN_2) + { + memtype_id = H5T_NATIVE_INT8; + } else { + memtype_id = H5T_NATIVE_UINT8; + } + } + else if (size_id==2) + { + if (sign_id==H5T_SGN_2) + { + memtype_id = H5T_NATIVE_INT16; + } else { + memtype_id = H5T_NATIVE_UINT16; + } + } + else if (size_id==4) + { + if (sign_id==H5T_SGN_2) + { + memtype_id = H5T_NATIVE_INT32; + } else { + memtype_id = H5T_NATIVE_UINT32; + } + } + else if (size_id==8) + { + if (sign_id==H5T_SGN_2) + { + memtype_id = H5T_NATIVE_INT64; + } else { + memtype_id = H5T_NATIVE_UINT64; + } + } + } else if (data_id==H5T_FLOAT) + { + size_id=H5Tget_size(atype); + if (size_id==4) + { + memtype_id = H5T_NATIVE_FLOAT; + } else if (size_id==8) { + memtype_id = H5T_NATIVE_DOUBLE; + } + } + if (memtype_id == -1) + { + NXIReportError (NXpData, "ERROR: h5MemType: invalid type"); + } + return memtype_id; +} + /*-------------------------------------------------------------------------*/ + + NXstatus NX5getnextentry (NXhandle fid,NXname name, NXname nxclass, int *datatype) + { + pNexusFile5 pFile; + hid_t grp, attr1,type,atype; + int iRet,iPtype, i; + int idx,data_id,size_id, sign_id; + char data[128]; + char ph_name[1024]; + info_type op_data; + int iRet_iNX=-1; + char pBuffer[256]; + + pFile = NXI5assert (fid); + op_data.iname = NULL; + + /* + iterate to next entry in group list + */ + idx=pFile->iStack5[pFile->iStackPtr].iCurrentIDX; + if (strlen(pFile->name_ref) == 0) { + /* root group */ + strcpy(pFile->name_ref,"/"); + } + iRet=H5Giterate(pFile->iFID,pFile->name_ref,&idx,nxgroup_info,&op_data); + strcpy(nxclass, NX_UNKNOWN_GROUP); + + /* + figure out the number of items in the current group. We need this in order to + find out if we are at the end of the search. + */ + if (pFile->iCurrentG == 0) { + pFile->iNX=0; + iRet_iNX = H5Giterate(pFile->iFID,"/",0,group_info1,&pFile->iNX); + } else { + pFile->iNX=0; + iRet_iNX = H5Giterate(pFile->iFID,pFile->name_ref,0,group_info1, &pFile->iNX); + } + if (idx == pFile->iNX) { + iRet_iNX = 2; + } + + if (iRet > 0) + { + pFile->iStack5[pFile->iStackPtr].iCurrentIDX++; + if (op_data.iname != NULL) { + strcpy(name,op_data.iname); + free(op_data.iname); + } else { + pFile->iStack5[pFile->iStackPtr].iCurrentIDX = 0; + return NX_EOD; + } + if (op_data.type == H5G_GROUP) + { + /* + open group and find class name attribute + */ + strcpy(ph_name,""); + for(i = 1; i < (pFile->iStackPtr + 1); i++) + { + strcat(ph_name,pFile->iStack5[i].irefn); + strcat(ph_name,"/"); + } + strcat(ph_name,name); + grp=H5Gopen(pFile->iFID,ph_name); + if (grp < 0) { + sprintf (pBuffer, "ERROR: Group %s does not exist!", ph_name); + NXIReportError (NXpData, pBuffer); + return NX_ERROR; + } + attr1 = H5Aopen_name(grp, "NX_class"); + if (attr1 < 0) { + strcpy(nxclass, NX_UNKNOWN_GROUP); + } else { + type=H5T_C_S1; + atype=H5Tcopy(type); + H5Tset_size(atype,128); + iRet = H5Aread(attr1, atype, data); + strcpy(nxclass,data); + H5Tclose(atype); + H5Aclose(attr1); + } + H5Gclose(grp); + } else if (op_data.type==H5G_DATASET) + { + /* + open dataset and find type + */ + grp=H5Dopen(pFile->iCurrentG,name); + type=H5Dget_type(grp); + atype=H5Tcopy(type); + data_id = H5Tget_class(atype); + iPtype = hdf5ToNXType(data_id, atype); *datatype=iPtype; strcpy(nxclass, "SDS"); H5Tclose(atype); @@ -1409,6 +1564,10 @@ static void ignoreError(void *data, char *text){ } else { + /* + we are at the end of the search: clear the data structure and reset + iCurrentIDX to 0 + */ if (iRet_iNX == 2) { if (op_data.iname != NULL) { free(op_data.iname); @@ -1450,47 +1609,10 @@ static void ignoreError(void *data, char *text){ memtype_id = H5Tcopy(H5T_C_S1); H5Tset_size(memtype_id, dims); } - if (data_id==H5T_INTEGER) + else { - size_id=H5Tget_size(pFile->iCurrentT); - sign_id=H5Tget_sign(pFile->iCurrentT); - if (size_id==1) - { - if (sign_id==H5T_SGN_2) - { - memtype_id = H5T_NATIVE_INT8; - } else { - memtype_id = H5T_NATIVE_UINT8; - } - } - else if (size_id==2) - { - if (sign_id==H5T_SGN_2) - { - memtype_id = H5T_NATIVE_INT16; - } else { - memtype_id = H5T_NATIVE_UINT16; - } - } - else if (size_id==4) - { - if (sign_id==H5T_SGN_2) - { - memtype_id = H5T_NATIVE_INT32; - } else { - memtype_id = H5T_NATIVE_UINT32; - } - } - } else if (data_id==H5T_FLOAT) - { - size_id=H5Tget_size(pFile->iCurrentT); - if (size_id==4) - { - memtype_id = H5T_NATIVE_FLOAT; - } else if (size_id==8) { - memtype_id = H5T_NATIVE_DOUBLE; - } - } + memtype_id = h5MemType(pFile->iCurrentT); + } /* actually read */ status = H5Dread (pFile->iCurrentD, memtype_id, @@ -1526,62 +1648,13 @@ static void ignoreError(void *data, char *text){ /* read information */ data_id = H5Tget_class(pFile->iCurrentT); - if (data_id==H5T_STRING) - { - mType=NX_CHAR; - } - if (data_id==H5T_INTEGER) - { - size_id=H5Tget_size(pFile->iCurrentT); - sign_id=H5Tget_sign(pFile->iCurrentT); - if (size_id==1) - { - if (sign_id==H5T_SGN_2) - { - mType=NX_INT8; - } else { - mType=NX_UINT8; - } - } - else if (size_id==2) - { - if (sign_id==H5T_SGN_2) - { - mType=NX_INT16; - } else { - mType=NX_UINT16; - } - } - else if (size_id==4) - { - if (sign_id==H5T_SGN_2) - { - mType=NX_INT32; - } else { - mType=NX_UINT32; - } - } - } else if (data_id==H5T_FLOAT) - { - size_id=H5Tget_size(pFile->iCurrentT); - if (size_id==4) - { - mType=NX_FLOAT32; - } - else if (size_id==8) - { - mType=NX_FLOAT64; - } - } + mType = hdf5ToNXType(data_id,pFile->iCurrentT); iRank = H5Sget_simple_extent_ndims(pFile->iCurrentS); iRet = H5Sget_simple_extent_dims(pFile->iCurrentS, myDim, NULL); /* conversion to proper ints for the platform */ *iType = (int)mType; - if (data_id==H5T_STRING) { - for (i = 0; i < iRank; i++) - { - myDim[i] = H5Tget_size(pFile->iCurrentT); - } + if (data_id==H5T_STRING && myDim[iRank-1] == 1) { + myDim[iRank-1] = H5Tget_size(pFile->iCurrentT); } *rank = (int)iRank; for (i = 0; i < iRank; i++) @@ -1596,12 +1669,12 @@ static void ignoreError(void *data, char *text){ NXstatus NX5getslab (NXhandle fid, void *data, int iStart[], int iSize[]) { pNexusFile5 pFile; - hssize_t myStart[H5S_MAX_RANK]; + hsize_t myStart[H5S_MAX_RANK]; hsize_t mySize[H5S_MAX_RANK]; - hssize_t mStart[H5S_MAX_RANK]; + hsize_t mStart[H5S_MAX_RANK]; hid_t memspace, iRet, data_id; hid_t memtype_id, size_id, sign_id; - char *tmp_data; + char *tmp_data = NULL; char *data1; int i, dims, iRank, mtype = 0; @@ -1621,6 +1694,11 @@ static void ignoreError(void *data, char *text){ } data_id = H5Tget_class(pFile->iCurrentT); if (data_id == H5T_STRING) { +/* + * FAA 24/1/2007: I don't think this will work for multidimensional + * string arrays. + * MK 23/7/2007: You are right Freddie. +*/ mtype = NX_CHAR; if (mySize[0] == 1) { mySize[0] = H5Tget_size(pFile->iCurrentT); @@ -1656,47 +1734,10 @@ static void ignoreError(void *data, char *text){ memtype_id = H5Tcopy(H5T_C_S1); H5Tset_size(memtype_id, dims); } - if (data_id==H5T_INTEGER) + else { - size_id=H5Tget_size(pFile->iCurrentT); - sign_id=H5Tget_sign(pFile->iCurrentT); - if (size_id==1) - { - if (sign_id==H5T_SGN_2) - { - memtype_id = H5T_NATIVE_INT8; - } else { - memtype_id = H5T_NATIVE_UINT8; - } - } - else if (size_id==2) - { - if (sign_id==H5T_SGN_2) - { - memtype_id = H5T_NATIVE_INT16; - } else { - memtype_id = H5T_NATIVE_UINT16; - } - } - else if (size_id==4) - { - if (sign_id==H5T_SGN_2) - { - memtype_id = H5T_NATIVE_INT32; - } else { - memtype_id = H5T_NATIVE_UINT32; - } - } - } else if (data_id==H5T_FLOAT) - { - size_id=H5Tget_size(pFile->iCurrentT); - if (size_id==4) - { - memtype_id = H5T_NATIVE_FLOAT; - } else if (size_id==8) { - memtype_id = H5T_NATIVE_DOUBLE; - } - } + memtype_id = h5MemType(pFile->iCurrentT); + } /* read slab */ if (mtype == NX_CHAR) { @@ -1738,217 +1779,92 @@ static void ignoreError(void *data, char *text){ char *iname = NULL; unsigned int idx; int intern_idx=-1; + int vid; + pFile = NXI5assert (fileid); + + vid = getAttVID(pFile); + idx=pFile->iAtt5.iCurrentIDX; iRet=0; - if ((pFile->iCurrentD == 0) && (pFile->iCurrentG==0)) - { - /* global attribute */ - pFile->iVID=H5Gopen(pFile->iFID,"/"); - intern_idx=H5Aget_num_attrs(pFile->iVID); - if (intern_idx > idx) { - iRet=H5Aiterate(pFile->iVID,&idx,attr_info,&iname); - } - else - { - iRet=0; - } - intern_idx=-1; - if (iRet < 0) { - NXIReportError (NXpData, "ERROR iterating through ROOT Attr. list!"); - return NX_ERROR; - } - } else if (pFile->iCurrentD > 0) { - intern_idx=H5Aget_num_attrs(pFile->iCurrentD); - if (intern_idx > idx) { - iRet=H5Aiterate(pFile->iCurrentD,&idx,attr_info,&iname); - } - else - { - iRet=0; - } - intern_idx=-1; - if (iRet < 0) { - NXIReportError (NXpData, "ERROR iterating through data Attr. list!"); - return NX_ERROR; - } + intern_idx=H5Aget_num_attrs(vid); + if(intern_idx == idx) { + killAttVID(pFile,vid); + return NX_EOD; } - else - { - pFile->iAtt5.iCurrentIDX = 0; - return NX_EOD; /* no group attributes */ - } - if (iRet>0) - { - pFile->iAtt5.iCurrentIDX++; - strcpy(pName, iname); - if (iname != NULL) { - free(iname); - } - if ((pFile->iCurrentD == 0) && (pFile->iCurrentG==0)) { - /* global attribute */ - pFile->iCurrentA = H5Aopen_name(pFile->iVID, pName); - } else { - pFile->iCurrentA = H5Aopen_name(pFile->iCurrentD, pName); - } - atype = H5Aget_type(pFile->iCurrentA); - aspace = H5Aget_space(pFile->iCurrentA); - rank = H5Sget_simple_extent_ndims(aspace); - attr_id = H5Tget_class(atype); - if (attr_id==H5T_STRING) { - iPType=NX_CHAR; - rank = H5Tget_size(atype); - } - if (rank == 0) { - rank++; - } - if (attr_id==H5T_INTEGER) - { - size_id=H5Tget_size(atype); - sign_id=H5Tget_sign(atype); - if (size_id==1) - { - if (sign_id==H5T_SGN_2) - { - iPType=NX_INT8; - } else { - iPType=NX_UINT8; - } - } - else if (size_id==2) - { - if (sign_id==H5T_SGN_2) - { - iPType=NX_INT16; - } else { - iPType=NX_UINT16; - } - } - else if (size_id==4) - { - if (sign_id==H5T_SGN_2) - { - iPType=NX_INT32; - } else { - iPType=NX_UINT32; - } - } - } else if (attr_id==H5T_FLOAT) - { - size_id=H5Tget_size(atype); - if (size_id==4) - { - iPType=NX_FLOAT32; - } - else if (size_id==8) - { - iPType=NX_FLOAT64; - } - } - *iType=iPType; - *iLength=rank; - H5Tclose(atype); - H5Sclose(aspace); - H5Aclose(pFile->iCurrentA); - return NX_OK; - } - else - { - if ((pFile->iCurrentD == 0) && (pFile->iCurrentG==0)) - { - /* global attribute */ - intern_idx=H5Aget_num_attrs(pFile->iVID); - } else { - if (pFile->iCurrentD>0){ - intern_idx=H5Aget_num_attrs(pFile->iCurrentD); - } else { - intern_idx=H5Aget_num_attrs(pFile->iCurrentG); - } - } - if ((intern_idx == 0)||(intern_idx == idx)) { - pFile->iAtt5.iCurrentIDX = 0; - if (iname != NULL) { - free(iname); - } - return NX_EOD; - } - NXIReportError (NXpData, - "ERROR: Iteration was not successful"); - return NX_ERROR; + + if (intern_idx > idx) { + iRet=H5Aiterate(vid,&idx,attr_info,&iname); + } else { + iRet=0; + } + intern_idx=-1; + if (iRet < 0) { + NXIReportError (NXpData, "ERROR iterating through attribute list!"); + killAttVID(pFile,vid); + return NX_ERROR; + } + pFile->iAtt5.iCurrentIDX++; + if (iname != NULL) { + if(strcmp(iname, "NX_class") == 0 && pFile->iCurrentG != 0) { + /* + skip NXclass attribute which is internal + */ + killAttVID(pFile, vid); + return NX5getnextattr(fileid, pName, iLength, iType); } + strcpy(pName, iname); + free(iname); + iname = NULL; + } else { + strcpy(pName,"What is this?"); + } + pFile->iCurrentA = H5Aopen_name(vid, pName); + atype = H5Aget_type(pFile->iCurrentA); + aspace = H5Aget_space(pFile->iCurrentA); + rank = H5Sget_simple_extent_ndims(aspace); + attr_id = H5Tget_class(atype); + if (attr_id==H5T_STRING) { + iPType=NX_CHAR; + rank = H5Tget_size(atype); + } + if (rank == 0) { + rank++; + } + iPType = hdf5ToNXType(attr_id,atype); + *iType=iPType; + *iLength=rank; + H5Tclose(atype); + H5Sclose(aspace); + H5Aclose(pFile->iCurrentA); + + intern_idx=H5Aget_num_attrs(vid); + + killAttVID(pFile,vid); + return NX_OK; } + /*-------------------------------------------------------------------------*/ - - - /*-------------------------------------------------------------------------*/ - - NXstatus NX5getattr (NXhandle fid, char *name, void *data, int* datalen, int* iType) + NXstatus NX5getattr (NXhandle fid, char *name, + void *data, int* datalen, int* iType) { pNexusFile5 pFile; - int iNew, iRet; - hid_t type, atype, glob; + int iNew, iRet, vid; + hid_t type, atype = -1, glob; char pBuffer[256]; pFile = NXI5assert (fid); type = *iType; glob = 0; - if (type == NX_CHAR) - { - type=H5T_C_S1; - } - else if (type == NX_INT8) - { - type=H5T_NATIVE_CHAR; - } - else if (type == NX_UINT8) - { - type=H5T_NATIVE_UCHAR; - } - else if (type == NX_INT16) - { - type=H5T_NATIVE_SHORT; - } - else if (type == NX_UINT16) - { - type=H5T_NATIVE_USHORT; - } - else if (type == NX_INT32) - { - type=H5T_NATIVE_INT; - } - else if (type == NX_UINT32) - { - type=H5T_NATIVE_UINT; - } - else if (type == NX_FLOAT32) - { - type=H5T_NATIVE_FLOAT; - } - else if (type == NX_FLOAT64) - { - type=H5T_NATIVE_DOUBLE; - } - /* find attribute */ - if (pFile->iCurrentD != 0) - { - /* Dataset attribute */ - iNew = H5Aopen_name(pFile->iCurrentD, name); - } - else - { - /* globale and group attributes */ - if (pFile->iCurrentG != 0) { - /* group attribute */ - iNew = H5Aopen_name(pFile->iCurrentG, name); - } else { - /* global attributes */ - glob=H5Gopen(pFile->iFID,"/"); - iNew = H5Aopen_name(glob, name); - } - } + + type = nxToHDF5Type(type); + + vid = getAttVID(pFile); + iNew = H5Aopen_name(vid, name); if (iNew < 0) { sprintf (pBuffer, "ERROR: attribute %s not found", name); + killAttVID(pFile,vid); NXIReportError (NXpData, pBuffer); return NX_ERROR; } @@ -1968,14 +1884,13 @@ static void ignoreError(void *data, char *text){ if (iRet < 0) { sprintf (pBuffer, "ERROR: HDF could not read attribute data"); NXIReportError (NXpData, pBuffer); + killAttVID(pFile,vid); return NX_ERROR; } iRet = H5Aclose(pFile->iCurrentA); - if (glob > 0) - { - H5Gclose(glob); - } + + killAttVID(pFile,vid); if (type==H5T_C_S1) { H5Tclose(atype); @@ -1990,59 +1905,27 @@ static void ignoreError(void *data, char *text){ pNexusFile5 pFile; char *iname = NULL; unsigned int idx; + int vid; herr_t iRet; pFile = NXI5assert (fid); idx=0; *iN = idx; - if (pFile->iCurrentD == 0 && pFile->iCurrentG == 0) { - /* - global attribute - */ - pFile->iVID=H5Gopen(pFile->iFID,"/"); - iRet = H5Aiterate(pFile->iVID,&idx,attr_info,&iname); - if (iRet < 0) { - NXIReportError (NXpData, "iterating ERROR!"); - return NX_ERROR; - } - idx=H5Aget_num_attrs(pFile->iVID); - if (idx > 0) { - *iN = idx; - } else { - *iN = 1; - } - /* - if (iname != NULL) { - free(iname); - } - */ - return NX_OK; - } - else - { - if (pFile->iCurrentD>0) { - iRet=H5Aiterate(pFile->iCurrentD,&idx,attr_info,&iname); + vid = getAttVID(pFile); + + idx=H5Aget_num_attrs(vid); + if (idx > 0) { + if(pFile->iCurrentG > 0 && pFile->iCurrentD == 0){ + *iN = idx -1; } else { - iRet=H5Aiterate(pFile->iCurrentG,&idx,attr_info,&iname); + *iN = idx; } + } else { + *iN = 0; } - if (iRet<0) { - NXIReportError (NXpData, "Attribute number cannot be fixed!"); - return NX_ERROR; - } - if ((idx==0) && (iRet==0)) { - *iN=idx; - return NX_OK; - } - idx=H5Aget_num_attrs(pFile->iCurrentD); - if (idx > 0) { - *iN = idx; - } else { - *iN = 1; - } + killAttVID(pFile,vid); return NX_OK; - } @@ -2050,25 +1933,28 @@ static void ignoreError(void *data, char *text){ NXstatus NX5getgroupID (NXhandle fileid, NXlink* sRes) { pNexusFile5 pFile; - int u; + int u, datalen, type = NX_CHAR; char group_name[64], class_name[64]; + ErrFunc oldErr; pFile = NXI5assert (fileid); if (pFile->iCurrentG == 0) { return NX_ERROR; } else { - strcpy(sRes->iRef5,"/"); - NX5getgroupinfo(fileid, &u, group_name,class_name); - strcat(sRes->iRef5,group_name); - strcpy(sRes->iTag5,"/"); - strcat(sRes->iTag5, pFile->iCurrentLGG); - strcpy(sRes->iRefd,""); /* - TODO: once we have group attributes, this should be set to - the groups target attribute + this means: if the item is already linked: use the target attribute, else + the path to the current node */ - strcpy(sRes->targetPath, sRes->iTag5); + oldErr = NXMGetError(); + NXMSetError(NXpData, ignoreError); + datalen = 1024; + memset(sRes->targetPath,0,datalen*sizeof(char)); + if(NX5getattr(fileid,"target",sRes->targetPath,&datalen,&type) != NX_OK){ + buildCurrentPath(pFile,sRes->targetPath,1024); + } + NXMSetError(NXpData,oldErr); + sRes->linkType = 0; return NX_OK; } /* not reached */ @@ -2129,6 +2015,7 @@ void NX5assignFunctions(pNexusFunction fHandle) fHandle->nxputslab=NX5putslab; fHandle->nxgetdataID=NX5getdataID; fHandle->nxmakelink=NX5makelink; + fHandle->nxmakenamedlink=NX5makenamedlink; fHandle->nxgetdata=NX5getdata; fHandle->nxgetinfo=NX5getinfo; fHandle->nxgetnextentry=NX5getnextentry; diff --git a/napiconfig.h b/napiconfig.h new file mode 100755 index 00000000..5b238331 --- /dev/null +++ b/napiconfig.h @@ -0,0 +1,22 @@ +#ifndef NAPICONFIG_H +#define NAPICONFIG_H + +#include + +/* + * Type definitions + */ +#ifdef HAVE_STDINT_H +#include +#else +typedef signed char int8_t; +typedef short int int16_t; +typedef int int32_t; +typedef long int64_t; +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long uint64_t; + +#endif /* HAVE_STDINT_H */ +#endif /* NAPICONFIG_H */ diff --git a/napiu.c b/napiu.c index ec3c5ce0..4cb02093 100644 --- a/napiu.c +++ b/napiu.c @@ -41,7 +41,7 @@ static const char* rscid = "$Id$"; /* Revision interted by CVS */ } \ } - NXstatus CALLING_STYLE NXUwriteglobals(NXhandle file_id, const char* user, const char* affiliation, const char* address, const char* telephone_number, const char* fax_number, const char* email) + NXstatus NXUwriteglobals(NXhandle file_id, const char* user, const char* affiliation, const char* address, const char* telephone_number, const char* fax_number, const char* email) { DO_GLOBAL(user); DO_GLOBAL(affiliation); @@ -53,7 +53,7 @@ static const char* rscid = "$Id$"; /* Revision interted by CVS */ } /* NXUwritegroup creates and leaves open a group */ - NXstatus CALLING_STYLE NXUwritegroup(NXhandle file_id, const char* group_name, const char* group_class) + NXstatus NXUwritegroup(NXhandle file_id, const char* group_name, const char* group_class) { int status; status = NXmakegroup(file_id, group_name, group_class); @@ -64,22 +64,22 @@ static const char* rscid = "$Id$"; /* Revision interted by CVS */ return status; } - NXstatus CALLING_STYLE NXUwritedata(NXhandle file_id, const char* data_name, const void* data, int data_type, int rank, const int dim[], const char* units, const int start[], const int size[]) + NXstatus NXUwritedata(NXhandle file_id, const char* data_name, const void* data, int data_type, int rank, const int dim[], const char* units, const int start[], const int size[]) { return NX_OK; } - NXstatus CALLING_STYLE NXUreaddata(NXhandle file_id, const char* data_name, void* data, char* units, const int start[], const int size[]) + NXstatus NXUreaddata(NXhandle file_id, const char* data_name, void* data, char* units, const int start[], const int size[]) { return NX_OK; } - NXstatus CALLING_STYLE NXUwritehistogram(NXhandle file_id, const char* data_name, const void* data, const char* units) + NXstatus NXUwritehistogram(NXhandle file_id, const char* data_name, const void* data, const char* units) { return NX_OK; } - NXstatus CALLING_STYLE NXUreadhistogram(NXhandle file_id, const char* data_name, void* data, char* units) + NXstatus NXUreadhistogram(NXhandle file_id, const char* data_name, void* data, char* units) { return NX_OK; } @@ -88,7 +88,7 @@ static int NXcompress_type = 0; static int NXcompress_size = 0; /* NXUsetcompress sets the default compression type and minimum size */ - NXstatus CALLING_STYLE NXUsetcompress(NXhandle file_id, int comp_type, int comp_size) + NXstatus NXUsetcompress(NXhandle file_id, int comp_type, int comp_size) { int status; if (comp_type == NX_COMP_LZW || comp_type == NX_COMP_HUF || @@ -110,7 +110,7 @@ static int NXcompress_size = 0; } /* !NXUfindgroup finds if a NeXus group of the specified name exists */ - NXstatus CALLING_STYLE NXUfindgroup(NXhandle file_id, const char* group_name, char* group_class) + NXstatus NXUfindgroup(NXhandle file_id, const char* group_name, char* group_class) { int status, n; NXname vname, vclass; @@ -122,38 +122,38 @@ static int NXcompress_size = 0; return NX_OK; } - NXstatus CALLING_STYLE NXUfindclass(NXhandle file_id, const char* group_class, char* group_name, int find_index) + NXstatus NXUfindclass(NXhandle file_id, const char* group_class, char* group_name, int find_index) { return NX_OK; } /* NXUfinddata finds if a NeXus data item is in the current group */ - NXstatus CALLING_STYLE NXUfinddata(NXhandle file_id, const char* data_name) + NXstatus NXUfinddata(NXhandle file_id, const char* data_name) { return NX_OK; } - NXstatus CALLING_STYLE NXUfindattr(NXhandle file_id, const char* attr_name) + NXstatus NXUfindattr(NXhandle file_id, const char* attr_name) { return NX_OK; } - NXstatus CALLING_STYLE NXUfindsignal(NXhandle file_id, int signal, char* data_name, int* data_rank, int* data_type, int data_dimensions[]) + NXstatus NXUfindsignal(NXhandle file_id, int signal, char* data_name, int* data_rank, int* data_type, int data_dimensions[]) { return NX_OK; } - NXstatus CALLING_STYLE NXUfindaxis(NXhandle file_id, int axis, int primary, char* data_name, int* data_rank, int* data_type, int data_dimensions[]) + NXstatus NXUfindaxis(NXhandle file_id, int axis, int primary, char* data_name, int* data_rank, int* data_type, int data_dimensions[]) { return NX_OK; } - NXstatus CALLING_STYLE NXUfindlink(NXhandle file_id, NXlink* group_id, const char* group_class) + NXstatus NXUfindlink(NXhandle file_id, NXlink* group_id, const char* group_class) { return NX_OK; } - NXstatus CALLING_STYLE NXUresumelink(NXhandle file_id, NXlink group_id) + NXstatus NXUresumelink(NXhandle file_id, NXlink group_id) { return NX_OK; } diff --git a/napiu.h b/napiu.h index 3c93a83c..efc223f8 100644 --- a/napiu.h +++ b/napiu.h @@ -34,35 +34,35 @@ extern "C" { #endif /* __cplusplus */ -NX_EXTERNAL NXstatus CALLING_STYLE NXUwriteglobals(NXhandle file_id, const char* user, const char* affiliation, const char* address, const char* phone, const char* fax, const char* email); +extern NXstatus NXUwriteglobals(NXhandle file_id, const char* user, const char* affiliation, const char* address, const char* phone, const char* fax, const char* email); -NX_EXTERNAL NXstatus CALLING_STYLE NXUwritegroup(NXhandle file_id, const char* group_name, const char* group_class); +extern NXstatus NXUwritegroup(NXhandle file_id, const char* group_name, const char* group_class); -NX_EXTERNAL NXstatus CALLING_STYLE NXUwritedata(NXhandle file_id, const char* data_name, const void* data, int data_type, int rank, const int dim[], const char* units, const int start[], const int size[]); +extern NXstatus NXUwritedata(NXhandle file_id, const char* data_name, const void* data, int data_type, int rank, const int dim[], const char* units, const int start[], const int size[]); -NX_EXTERNAL NXstatus CALLING_STYLE NXUreaddata(NXhandle file_id, const char* data_name, void* data, char* units, const int start[], const int size[]); +extern NXstatus NXUreaddata(NXhandle file_id, const char* data_name, void* data, char* units, const int start[], const int size[]); -NX_EXTERNAL NXstatus CALLING_STYLE NXUwritehistogram(NXhandle file_id, const char* data_name, const void* data, const char* units); +extern NXstatus NXUwritehistogram(NXhandle file_id, const char* data_name, const void* data, const char* units); -NX_EXTERNAL NXstatus CALLING_STYLE NXUreadhistogram(NXhandle file_id, const char* data_name, void* data, char* units); +extern NXstatus NXUreadhistogram(NXhandle file_id, const char* data_name, void* data, char* units); -NX_EXTERNAL NXstatus CALLING_STYLE NXUsetcompress(NXhandle file_id, int comp_type, int comp_size); +extern NXstatus NXUsetcompress(NXhandle file_id, int comp_type, int comp_size); -NX_EXTERNAL NXstatus CALLING_STYLE NXUfindgroup(NXhandle file_id, const char* group_name, char* group_class); +extern NXstatus NXUfindgroup(NXhandle file_id, const char* group_name, char* group_class); -NX_EXTERNAL NXstatus CALLING_STYLE NXUfindclass(NXhandle file_id, const char* group_class, char* group_name, int find_index); +extern NXstatus NXUfindclass(NXhandle file_id, const char* group_class, char* group_name, int find_index); -NX_EXTERNAL NXstatus CALLING_STYLE NXUfinddata(NXhandle file_id, const char* data_name); +extern NXstatus NXUfinddata(NXhandle file_id, const char* data_name); -NX_EXTERNAL NXstatus CALLING_STYLE NXUfindattr(NXhandle file_id, const char* attr_name); +extern NXstatus NXUfindattr(NXhandle file_id, const char* attr_name); -NX_EXTERNAL NXstatus CALLING_STYLE NXUfindsignal(NXhandle file_id, int signal, char* data_name, int* data_rank, int* data_type, int data_dimensions[]); +extern NXstatus NXUfindsignal(NXhandle file_id, int signal, char* data_name, int* data_rank, int* data_type, int data_dimensions[]); -NX_EXTERNAL NXstatus CALLING_STYLE NXUfindaxis(NXhandle file_id, int axis, int primary, char* data_name, int* data_rank, int* data_type, int data_dimensions[]); +extern NXstatus NXUfindaxis(NXhandle file_id, int axis, int primary, char* data_name, int* data_rank, int* data_type, int data_dimensions[]); -NX_EXTERNAL NXstatus CALLING_STYLE NXUfindlink(NXhandle file_id, NXlink* group_id, const char* group_class); +extern NXstatus NXUfindlink(NXhandle file_id, NXlink* group_id, const char* group_class); -NX_EXTERNAL NXstatus CALLING_STYLE NXUresumelink(NXhandle file_id, NXlink group_id); +extern NXstatus NXUresumelink(NXhandle file_id, NXlink group_id); #ifdef __cplusplus } diff --git a/network.c b/network.c index 4cc86bcf..c8ae7ead 100644 --- a/network.c +++ b/network.c @@ -39,6 +39,7 @@ ----------------------------------------------------------------------------*/ #include "fortify.h" #include "network.h" +#include #include #include #include @@ -64,10 +65,15 @@ struct timeval lastclose={-1,0}; */ #include "Scommon.h" extern void SICSLogWrite(char *pText, OutCode eCode); /* servlog.c */ + + void WriteToCommandLog(char *p, char *t); static void NetError(char *pText) { +/* SICSLogWrite(pText,eError); +*/ + WriteToCommandLog("NET>", pText); } /* ---------------------------- Local ------------------------------------ @@ -170,7 +176,8 @@ CreateSocketAdress( int iRet; fd_set lMask; struct timeval tmo = {1,0}; - int iLen, i; + int i; + socklen_t iLen; struct linger lili; assert(self != NULL); @@ -378,7 +385,7 @@ CreateSocketAdress( { int t; - int len; + socklen_t len; struct sockaddr_in sin; struct hostent *host; @@ -403,6 +410,7 @@ CreateSocketAdress( else { strncpy(pCompost,host->h_name,iBufLen); + pCompost[iBufLen-1]='\0'; } } return 1; @@ -410,11 +418,15 @@ CreateSocketAdress( /*--------------------------------------------------------------------------*/ int NETWrite(mkChannel *self, char *buffer, long lLen) { - int iRet; + long iRet; fd_set lMask; - struct timeval tmo ={0,1}; - - + struct timeval tmo; + char buf[256]; + time_t expire, delta; + char *pos; + long l; + int disconnected; + if(!VerifyChannel(self)) { return 0; @@ -424,46 +436,87 @@ CreateSocketAdress( return 0; } -#ifndef CYGNUS - /* setup for select first */ - tmo.tv_usec = 100; - FD_ZERO(&lMask); - FD_SET(self->sockid,&lMask); + /* + * Check if the we can write to the socket first.... + * Well, this how it should be. However, on linux I observe that + * there is a problem with Java clients not reliably receiving data when + * this is active. + */ if((self->sockid >= FD_SETSIZE) || (self->sockid < 0) ) - /* invalid descriptor */ { - return -1; /* eof */ + /* invalid descriptor */ + return 0; } - iRet = select( (self->sockid + 1),NULL, &lMask, NULL,&tmo); - if( iRet <= 0) - { - /* failure, or no data */ - return -1; - } - /* blocking on write */ - if(!FD_ISSET(self->sockid,&lMask)) - { - return -2; - } -#endif +#ifdef DO_NOT_SELECT_BEFORE_SEND iRet = send(self->sockid,buffer,lLen,0); - if(iRet != lLen) - { - if(iRet < 0) - { - if (errno != EPIPE) { /* do not write "broken pipe" error */ - printf("System error: %s\n",strerror(errno)); - } - } else { - printf("Incomplete send: %d to %ld\n",iRet,lLen); - } + if (iRet != lLen) { + self->iType = 0; + if (iRet < 0) { + if (errno == EPIPE) { /* do not write "broken pipe" error */ return 0; - } - else - { - return 1; + } + snprintf(buf, sizeof buf, "NETWrite: send system error: %s (socket %d)", + strerror(errno), self->sockid); + } else { + snprintf(buf, sizeof buf, "NETWrite: only %ld of %ld bytes sent (socket %d)", + iRet, lLen, self->sockid); + } + NetError(buf); + return 0; + } else { + return 1; } - +#endif + pos = buffer; + l = lLen; + FD_ZERO(&lMask); + disconnected = 0; + +#define TIMEOUT 10 + expire = time(NULL) + TIMEOUT; + while (l > 0) { + delta = expire - time(NULL); + if (delta <= 0) break; + FD_SET(self->sockid,&lMask); + tmo.tv_usec = 0; + tmo.tv_sec = delta; + iRet = select( (self->sockid + 1),NULL, &lMask, NULL,&tmo); + if (iRet < 0) { + /* failure, or no data */ + self->iType = 0; + snprintf(buf, sizeof buf, + "NETWrite: failure on select before send: %s (socket %d)", + strerror(errno), self->sockid); + NetError(buf); + return 0; + } + if (!FD_ISSET(self->sockid,&lMask)) break; + + iRet = send(self->sockid,pos,l,0); + disconnected = (iRet == 0); + if (iRet < 0) { + self->iType = 0; + if (errno == EPIPE || errno == ECONNRESET) { /* do not write these errors */ + return 0; + } + snprintf(buf, sizeof buf, "NETWrite: send system error: %s (socket %d)", + strerror(errno), self->sockid); + NetError(buf); + return 0; + } + l -= iRet; + pos += iRet; + } + if (l > 0) { + self->iType = 0; + if (!disconnected) { /* do not write an error message on disconnect */ + snprintf(buf, sizeof buf, "NETWrite: timeout, only %ld of %ld bytes sent (socket %d) %ld.%6.6ld", + lLen - l, lLen, self->sockid, (long)tmo.tv_sec, (long)tmo.tv_usec); + NetError(buf); + } + return 0; + } + return 1; } /* -------------------------------------------------------------------------*/ long NETRead(mkChannel *self, char *buffer, long lLen, long timeout) @@ -555,7 +608,7 @@ int NETReadTillTerm(mkChannel *self, long timeout, char *pTerm, char *pBuffer, int iBufLen) { struct timeval start, now; - int bufPtr = 0, status, i, length; + int bufPtr = 0, status, i, length, matchIndex=1; char c; long dif; @@ -583,11 +636,29 @@ int NETReadTillTerm(mkChannel *self, long timeout, { return status; } - for(i = 0; i < length; i++) - { - if(c == pTerm[i]) + if ('&' != pTerm[0]) { + for(i = 0; i < length; i++) { - return bufPtr+1; + if(c == pTerm[i]) + { + return bufPtr+1; + } + } + } else { + if (matchIndex == 1 && c == pTerm[1]) { + matchIndex++; + } else { + if (c== pTerm[matchIndex] && matchIndex < length -1) { + matchIndex++; + } else { + if (c == pTerm[matchIndex] && matchIndex == length - 1) { + bufPtr = bufPtr - matchIndex + 1; + pBuffer[bufPtr] = '\0'; + return bufPtr; + } else { + matchIndex = 1; + } + } } } if (c == 0 && *pTerm != 0) @@ -644,6 +715,89 @@ int NETReadTillTerm(mkChannel *self, long timeout, return 1; } +/*---------------------------------------------------------------------------*/ +int NETReconnectWithFlags(mkChannel* self, int flags) +{ + int iRet; + int sock; + int oldopts; + + /* + * Get the flags and close the old socket + */ + oldopts = fcntl(self->sockid, F_GETFL, 0); + close(self->sockid); + /* Reopen and try to get it on the olf fd */ + sock = socket(AF_INET,SOCK_STREAM,0); + if (self->sockid != sock) { + iRet = fcntl(sock, F_DUPFD, self->sockid); + if (iRet != sock) + self->sockid = sock; + else + close(sock); + sock = self->sockid; + } + /* restore the old flags */ + fcntl(self->sockid, F_SETFL, oldopts); + /* set socket non-blocking */ + oldopts = fcntl(self->sockid, F_GETFL, 0); + if (/*(flags & 1) &&*/ !(oldopts & O_NONBLOCK)) + fcntl(self->sockid, F_SETFL, oldopts | O_NONBLOCK); + /* try to reconnect */ + iRet = connect(self->sockid, + (struct sockaddr *)&(self->adresse), + sizeof(struct sockaddr_in)); + if (iRet < 0) { + if (errno == EINPROGRESS) { + if ((flags & 1)) { + iRet = 0; /* in progress */ + } else { + fd_set rmask; + fd_set wmask; + struct timeval tmo = {1,0}; + + FD_ZERO(&rmask); + FD_ZERO(&wmask); + FD_SET(self->sockid, &rmask); + FD_SET(self->sockid, &wmask); + iRet = select(self->sockid+1, &rmask, &wmask, NULL, &tmo); + if (iRet < 0) /* error */ + iRet = -1; + else if (iRet == 0) /* timeout */ + iRet = 0; /* in progress */ + else { + char reply[1]; + if (FD_ISSET(self->sockid, &rmask)) { + iRet = recv(self->sockid, reply, 1, MSG_PEEK); + if (iRet <= 0) + iRet = -1; /* failure */ + } + if (FD_ISSET(self->sockid, &wmask)) { + iRet = send(self->sockid, NULL, 0, 0); + if (iRet < 0) + iRet = -1; /* failure */ + else + iRet = 1; /* success */ + } + } + } + } + else /* other error */ + iRet = -1; /* error */ + } + else + iRet = 1; /* success */ + + if (iRet != 0 && !(oldopts & O_NONBLOCK)) + fcntl(self->sockid, F_SETFL, oldopts); + return iRet; +} + +int NETReconnect(mkChannel* self) +{ + return NETReconnectWithFlags(self, 0); +} + /* ################### UDP -functions ######################################*/ mkChannel *UDPOpen(int iPort) { @@ -748,7 +902,7 @@ int NETReadTillTerm(mkChannel *self, long timeout, long lMask = 0L; struct timeval tmo ={0,1}; long iRet; - int iLang; + socklen_t iLang; if(!VerifyChannel(self)) { diff --git a/network.h b/network.h index 455d96f5..900d035a 100644 --- a/network.h +++ b/network.h @@ -76,10 +76,22 @@ of hostname are copied to pComposter */ + int NETReconnect(mkChannel* self); + /* If a connection has been lost, try to reconnect using the same + * socket id if possible. Blocks for up to one second. + * returns 0 if in progress, 1 on success, a negative value on error + */ + + int NETReconnectWithFlags(mkChannel* self, int flags); + /* If a connection has been lost, try to reconnect using the same + * socket id if possible. If (flags & 1) do not block, use + * NETConnectFinished to check success. + * returns 0 if in progress, 1 on success, a negative value on error + */ /* *********************** DATA TRANSFER ******************************** */ int NETWrite(mkChannel *self, char *buffer, long lLen); - /* writes data to socket self, returns True if succes, + /* writes data to socket self, returns True if success, false otherwise. */ diff --git a/nread.c b/nread.c index c6e17795..58dc0998 100644 --- a/nread.c +++ b/nread.c @@ -126,8 +126,10 @@ extern int VerifyChannel(mkChannel *self); /* defined in network.c */ int NetReadRegister(pNetRead self, mkChannel *pSock, eNRType eType, SConnection *pCon) { - NetItem sItem; - + NetItem sItem, sEntry; + char buffer[80]; + int iRet; + assert(self); if(!VerifyChannel(pSock)) { @@ -142,6 +144,20 @@ extern int VerifyChannel(mkChannel *self); /* defined in network.c */ sItem.iReadable = 0; memset(sItem.pHold,0,511); + /* check if the entry is already there */ + iRet = LLDnodePtr2First(self->iList); + while(iRet != 0) + { + LLDnodeDataTo(self->iList,&sEntry); + if(sEntry.pSock->sockid == pSock->sockid) + { + snprintf(buffer, sizeof buffer, "NetReadRegister twice %d type %d", pSock->sockid, eType); + WriteToCommandLog("SYS>",buffer); + return 1; + } + iRet = LLDnodePtr2Next(self->iList); + } + LLDnodeAppendFrom(self->iList, &sItem); return 1; } @@ -265,7 +281,7 @@ extern int VerifyChannel(mkChannel *self); /* defined in network.c */ "ERROR: insufficient privilege to invoke Interrupt", eError); } - return 1; + return 0; } /* split into command lines @@ -686,6 +702,12 @@ extern int VerifyChannel(mkChannel *self); /* defined in network.c */ int iRet, iStatus; int iCount; NetItem NItem; + int conCount = 0; + char num[50]; + IPair *options = NULL; + char buffer[1024]; + int bufferLen; + static int bufferFull=0; self = (pNetRead)pData; assert(self); @@ -699,11 +721,13 @@ extern int VerifyChannel(mkChannel *self); /* defined in network.c */ { return 0; } - + /* build the select mask */ FD_ZERO(&lMask); iRet = LLDnodePtr2First(self->iList); iCount = 0; + buffer[0] = '\0'; + bufferLen = 0; while(iRet != 0) { LLDnodeDataTo(self->iList,&NItem); @@ -711,14 +735,33 @@ extern int VerifyChannel(mkChannel *self); /* defined in network.c */ { break; } + snprintf(num,sizeof num, "%d, type %d:", NItem.pSock->sockid, NItem.eType); + if (bufferLen + strlen(num) < sizeof buffer) { + strcpy(buffer + bufferLen, num); + bufferLen += strlen(num); + } else { + if (bufferFull == 0) { + bufferFull = 1; + } + } FD_SET(NItem.pSock->sockid,&lMask); if(NItem.pSock->sockid > iCount) { iCount = NItem.pSock->sockid; } + conCount++; iRet = LLDnodePtr2Next(self->iList); } + snprintf(num,sizeof num,"%d", conCount); + IFSetOption(pSICSOptions,"ConnectionCount",num); + IFSetOption(pSICSOptions,"ConMask",buffer); + + if (bufferFull == 1) { + bufferFull = 2; + WriteToCommandLog("BUFFERFULL>",buffer); + } + /* the select itself */ tmo.tv_usec = self->iReadTimeout; iCount++; diff --git a/nserver.c b/nserver.c index fddfde42..de27ba07 100644 --- a/nserver.c +++ b/nserver.c @@ -24,6 +24,7 @@ #include "network.h" #include "ifile.h" #include "status.h" +#include "statusfile.h" #include "devexec.h" #include "passwd.h" #include "lld.h" @@ -35,6 +36,7 @@ #include "site.h" #include "tcldrivable.h" #include "nserver.h" +#include "sicshipadaba.h" int ServerSetupInterrupt(int iPort, pNetRead pNet, pTaskMan pTasker); /* @@ -42,6 +44,10 @@ */ extern void StopExit(void); /* in SICSmain.c */ + + extern int openDevexecLog(); /* in devexec.c */ + + extern void NetWatchInit(void); /* in nwatch.c */ /* ========================= Less dreadful file statics =================== */ #define DEFAULTINIFILE "servo.tcl" @@ -106,6 +112,12 @@ /* initialise tasker */ assert(TaskerInit(&self->pTasker)); + pSICSOptions = IFAddOption(pSICSOptions, "ConnectionCount","0"); + pSICSOptions = IFAddOption(pSICSOptions, "ConMask","0"); + + /* initialize the network watcher */ + NetWatchInit(); + /* initialise the server from script */ if(file == NULL) { @@ -127,6 +139,7 @@ } return 0; } + self->dummyCon = SCCreateDummyConnection(self->pSics); /* check for option RedirectFile and redirect stout/sterr to it @@ -211,10 +224,8 @@ NetReadRegister(pReader, self->pServerPort, naccept, NULL); /* the device executor */ - pCom = FindCommand(self->pSics,"stopexe"); - assert(pCom); - assert(pCom->pData); - self->pExecutor = (pExeList)pCom->pData; + openDevexecLog(); + DevexecLog("START","SICS"); /* initialize Interrupt Port */ @@ -250,30 +261,6 @@ EnvMonSignal, NULL, self->pMonitor,1); - - - /* initialize the last saved status of the system */ - strcpy(pBueffel,"Restore "); - pText = IFindOption(pSICSOptions,"statusfile"); - if(pText) - { - strcat(pBueffel,pText); - } - else - { - strcat(pBueffel,DEFAULTSTATUSFILE); - IFAddOption(pSICSOptions,"statusfile", - DEFAULTSTATUSFILE); - } - self->dummyCon = pCon = SCCreateDummyConnection(self->pSics); - if(pCon) - { - InterpExecute(self->pSics,pCon,pBueffel); - } - else - { - printf("ERROR: Cannot allocate dummy connection, status NOT loaded"); - } /* install performance monitor */ pMon = CreatePerfMon(20); @@ -287,6 +274,13 @@ /* install telnet port */ InstallTelnet(); + /* If the restore file has not been loaded, do so now */ + if(!hasRestored()) + { + strcpy(pBueffel,"restore"); + SCInvoke(self->dummyCon,self->pSics,pBueffel); + } + /* exit handlers need to be installed here */ atexit(StopExit); Fortify_CheckAllMemory(); @@ -304,6 +298,7 @@ /* clear all pending bullshit */ ClearExecutor(self->pExecutor); + DevexecLog("STOP","SICS"); /* shut telnet down */ KillTelnet(); @@ -391,11 +386,12 @@ */ killTclDrivable(); - /* close the List system */ - LLDsystemClose(); - KillFreeConnections(); + killSICSHipadaba(); + + /* close the List system */ + LLDsystemClose(); /* make fortify print his findings */ Fortify_DumpAllMemory(iFortifyScope); @@ -569,4 +565,8 @@ TaskSignal(pTasker,SICSBROADCAST,pMessage); } - +/*-------------------------------------------------------------------------*/ + int ServerIsStarting(pServer self) + { + return self->pReader == NULL; + } diff --git a/nserver.h b/nserver.h index 2b9551ed..3c644041 100644 --- a/nserver.h +++ b/nserver.h @@ -50,4 +50,5 @@ int argc, char *argv[]); int SicsWait(long lTime); + int ServerIsStarting(pServer self); #endif diff --git a/nwatch.c b/nwatch.c new file mode 100644 index 00000000..bf024ccc --- /dev/null +++ b/nwatch.c @@ -0,0 +1,464 @@ +/* + * N E T W A T C H E R + * + * This module watches network connections for sockets becoming readable or + * writeable and invokes callbacks. It also provides a timer mechanism. + * + * Douglas Clowes, February 2007 + * + */ + +#include +#include +#ifdef CYGNUS +#include +#else +#include +#endif +#include +#include "fortify.h" +#include "nwatch.h" +#include "sics.h" + +#define NWMAGIC 51966 + +/* Net Watcher control structure */ +typedef struct __netwatcher_s { + pNWContext cq_head; /* head of socket context queue */ + pNWContext cq_tail; /* tail of socket context queue */ + int nInvalid; /* number of invalidated entries */ + pNWTimer tq_head; /* head of timer context queue */ + pNWTimer tq_tail; /* tail of timer context queue */ + long lMagic; /* integrity check */ +} NetWatch, *pNetWatch; + +/* Singleton pattern */ +static pNetWatch instance = NULL; + +static int NetWatchTask(void* pData); + +/** + * \brief Initialises the Net Watcher singleton and starts the task + * + * \return 1=success, 0=failure + */ +int NetWatchInit(void) { + /* + * If the singleton has not yet been created, do so now + */ + if (instance == NULL) { + instance = (pNetWatch) malloc(sizeof(NetWatch)); + if (instance == NULL) + return 0; + memset(instance, 0, sizeof(NetWatch)); + instance->lMagic = NWMAGIC; + TaskRegister(pServ->pTasker, NetWatchTask, NULL, NULL, NULL, 1); + } + return 1; +} + +/* + * The timer context object private definition + */ +typedef struct __netwatchtimer { + pNWTimer next; /* chain to next event */ + struct timeval tv; /* time when event is due */ + pNWCallback func; /* function to call */ + void* cntx; /* abstract context to pass to callback */ + long int tick; /* millisecond repeat rate */ + long int vrfy; /* integrity check */ +} NWTimer; + +/* + * \brief private function to insert an entry into the sorted timer queue. + * + * \param self singleton + * \param handle new timer to insert + */ +static int NetWatchTimerInsQue(pNetWatch self, pNWTimer handle) +{ + /* if the queue is empty, just stick new one in */ + if (self->tq_head == NULL) { + self->tq_head = self->tq_tail = handle; + handle->next = NULL; + return 1; + } + /* if new one is not earlier than latest one, insert after latest */ + if (handle->tv.tv_sec > self->tq_tail->tv.tv_sec || + (handle->tv.tv_sec == self->tq_tail->tv.tv_sec && + handle->tv.tv_usec >= self->tq_tail->tv.tv_usec)) { + self->tq_tail->next = handle; + self->tq_tail = handle; + handle->next = NULL; + return 1; + } + /* if new one is not later than earliest one, insert before earliest */ + if (handle->tv.tv_sec < self->tq_head->tv.tv_sec || + (handle->tv.tv_sec == self->tq_head->tv.tv_sec && + handle->tv.tv_usec <= self->tq_head->tv.tv_usec)) { + handle->next = self->tq_head; + self->tq_head = handle; + return 1; + } + else + { + /* must be in between two so start at the first entry */ + pNWTimer pNxt = self->tq_head; + /* follow chain until the one after this one is greater than new one */ + while (pNxt->next && + (handle->tv.tv_sec > pNxt->next->tv.tv_sec || + (handle->tv.tv_sec == pNxt->next->tv.tv_sec && + handle->tv.tv_usec > pNxt->next->tv.tv_usec))) + pNxt = pNxt->next; + /* slip new one in between this one and the next one */ + handle->next = pNxt->next; + pNxt->next = handle ; + } + return 1; +} + +/* + * \brief private function to remove an entry from the sorted timer queue. + * + * \param self singleton + * \param handle existing timer to remove + */ +static int NetWatchTimerRemQue(pNetWatch self, pNWTimer handle) +{ + /* handle the case of first and possibly only */ + if (handle == self->tq_head) { + self->tq_head = self->tq_head->next; /* may be NULL */ + if (handle == self->tq_tail) + self->tq_tail = NULL; + } + /* handle general case */ + else { + pNWTimer pNxt = self->tq_head; + while (pNxt) { + if (handle == pNxt->next) { + pNxt->next = pNxt->next->next; + break; + } + pNxt = pNxt->next; + } + /* It it was the last entry, point tail to its predecessor */ + if (handle == self->tq_tail) + self->tq_tail = pNxt; + } + return 1; +} + +int NetWatchRegisterTimer(pNWTimer* handle, int mSec, + pNWCallback callback, void* context) +{ + pNetWatch self = instance; + if(!self || self->lMagic != NWMAGIC) + return 0; + pNWTimer pNew = (pNWTimer) malloc(sizeof(NWTimer)); + if (pNew == NULL) + return 0; + memset(pNew, 0, sizeof(NWTimer)); + gettimeofday(&pNew->tv, NULL); + pNew->tv.tv_sec += mSec / 1000; + pNew->tv.tv_usec += 1000 * (mSec % 1000); + if (pNew->tv.tv_usec > 1000000) { + pNew->tv.tv_sec ++; + pNew->tv.tv_usec -= 1000000; + } + pNew->tick = 0; + pNew->func = callback; + pNew->cntx = context; + pNew->vrfy = NWMAGIC; + NetWatchTimerInsQue(self, pNew); + *handle = pNew; + return 1; +} + +int NetWatchRegisterTimerPeriodic(pNWTimer* handle, int mSecInitial, int mSecPeriod, + pNWCallback callback, void* context) +{ + if (NetWatchRegisterTimer(handle, mSecInitial, callback, context)) { + pNWTimer pNew = *handle; + if (pNew == NULL) + return 0; + if (mSecPeriod > 0) + pNew->tick = mSecPeriod; + return 1; + } + return 0; +} + +int NetWatchGetTimerPeriod(pNWTimer handle) +{ + if (handle == NULL || handle->vrfy != NWMAGIC) + return 0; + return handle->tick; +} + +int NetWatchSetTimerPeriod(pNWTimer handle, int mSecPeriod) +{ + if (handle == NULL || handle->vrfy != NWMAGIC) + return 0; + handle->tick = mSecPeriod; + return 1; +} + +int NetWatchRemoveTimer(pNWTimer handle) +{ + pNetWatch self = instance; + if (!self || self->lMagic != NWMAGIC)\ + return 0; + NetWatchTimerRemQue(self, handle); + handle->vrfy = 0; + free(handle); + return 1; +} + +/* private data */ +typedef struct __netwatchcontext { + pNWContext next; /* chain pointer */ + int sock; /* socket to watch */ + int mode; /* read or write */ + pNWCallback func; /* user supplied callback function */ + void* cntx; /* user supplied callback context */ + long vrfy; /* integrity check */ +} NWContext; + +/** + * \brief private function to insert entry into unsorted queue + * + * \param self singleton + * \param handle entry to insert + */ +static int NetWatchContextInsQue(pNetWatch self, pNWContext handle) +{ + if (self->cq_head == NULL) /* empty */ + self->cq_head = self->cq_tail = handle; + else { + self->cq_tail->next = handle; + self->cq_tail = handle; + } + return 1; +} + +/** + * \brief private function to remove entry from unsorted queue + * + * \param self singleton + * \param handle entry to insert + */ +static void NetWatchContextRemQue(pNetWatch self, pNWContext handle) +{ + if (handle == self->cq_head) { /* if first */ + self->cq_head = self->cq_head->next; + if (handle == self->cq_tail) /* if also last */ + self->cq_tail = NULL; + } + else { + pNWContext pNxt = self->cq_head; + while (pNxt) { + if (handle == pNxt->next) { + pNxt->next = pNxt->next->next; + break; + } + pNxt = pNxt->next; + } + if (handle == self->cq_tail) /* if last */ + self->cq_tail = pNxt; + } + return; +} + +/** + * \brief private function to purge invalid entries + * + * \param self singleton + */ +static void NetWatchContextPrgQue(pNetWatch self) +{ + pNWContext pNxt = NULL; + /* while the first entry is invalid remove it */ + while (self->cq_head && self->cq_head->sock < 0) { + pNWContext tmp = NULL; + tmp = self->cq_head; + self->cq_head = self->cq_head->next; + tmp->vrfy = 0; + free(tmp); + } + pNxt = self->cq_head; + while (pNxt) { + if (pNxt->next && pNxt->next->sock < 0) { + pNWContext tmp = NULL; + tmp = pNxt->next; + pNxt->next = pNxt->next->next; + tmp->vrfy = 0; + free(tmp); + } + pNxt = pNxt->next; + } + /* if the queue is empty clear the tail */ + if (self->cq_head == NULL) + self->cq_tail = pNxt; + self->nInvalid = 0; + return; +} + +int NetWatchRegisterCallback(pNWContext* handle, int iSocket, + pNWCallback callback, void* context) +{ + pNWContext pNew = NULL; + pNetWatch self = instance; + if(!self || self->lMagic != NWMAGIC) + return 0; + if (iSocket < 0 || iSocket > 65535) + return 0; + pNew = (pNWContext) malloc(sizeof(NWContext)); + if (pNew == NULL) + return 0; + memset(pNew, 0, sizeof(NWContext)); + pNew->sock = iSocket; + pNew->mode = nwatch_read; + pNew->func = callback; + pNew->cntx = context; + pNew->vrfy = NWMAGIC; + *handle = pNew; + NetWatchContextInsQue(self, pNew); + return 1; +} + +int NetWatchRemoveCallback(pNWContext handle) +{ + pNetWatch self = instance; + if (handle == NULL || handle->vrfy != NWMAGIC) + return 0; + if(!self || self->lMagic != NWMAGIC) + return 0; + handle->sock = -1; + self->nInvalid++; + return 1; +} + +int NetWatchGetMode(pNWContext handle) +{ + if (handle == NULL || handle->vrfy != NWMAGIC) + return 0; + return handle->mode; +} + +int NetWatchSetMode(pNWContext handle, int mode) +{ + if (handle == NULL || handle->vrfy != NWMAGIC) + return 0; + handle->mode = mode; + return 1; +} + +/** + * \brief the registered SICS Task to drive all this + */ +int NetWatchTask (void* pData) +{ + pNetWatch self = NULL; + pNWContext pNWC = NULL; + fd_set rMask; + fd_set wMask; + struct timeval tmo = {0,0}; + int iRet; + int iCount; + + /* Check the singleton */ + self = (pNetWatch) instance; + if(!self || self->lMagic != NWMAGIC) + return 0; + + /* Purge the invalidated */ + if (self->nInvalid > 0) + NetWatchContextPrgQue(self); + + /* build the select mask */ + FD_ZERO(&rMask); + FD_ZERO(&wMask); + pNWC = self->cq_head; + iCount = -1; + while(pNWC) { + if (pNWC->sock >= 0 && pNWC->sock <= 65535) { + if (pNWC->mode & nwatch_read) + FD_SET(pNWC->sock,&rMask); + if (pNWC->mode & nwatch_write) + FD_SET(pNWC->sock,&wMask); + if(pNWC->sock > iCount) { + iCount = pNWC->sock; + } + } + pNWC = pNWC->next; + } + + iRet = 0; + if (iCount >= 0) + iRet = select(iCount+1, &rMask, &wMask, NULL, &tmo); + + if(iRet > 0) { + /* invoke the active callbacks */ + iCount = 0; + pNWC = self->cq_head; + while(pNWC) + { + if (pNWC->sock >= 0 && pNWC->sock <= 65535) { + int action_mode = 0; + if ((pNWC->mode & nwatch_read) && FD_ISSET(pNWC->sock, &rMask)) + action_mode |= nwatch_read; + if ((pNWC->mode & nwatch_write) && FD_ISSET(pNWC->sock, &wMask)) + action_mode |= nwatch_write; + if (action_mode != 0) { + int iStatus; + iStatus = (*pNWC->func)(pNWC->cntx, action_mode); + } + } + pNWC = pNWC->next; + } + } + + /* Now do the timers */ + if (self->tq_head) { + int iStatus; + struct timeval tv; + gettimeofday(&tv, NULL); + while (self->tq_head) { + pNWTimer pNew = self->tq_head; + if (tv.tv_sec < pNew->tv.tv_sec || + (tv.tv_sec == pNew->tv.tv_sec && + tv.tv_usec < pNew->tv.tv_usec)) { + break; + } + NetWatchTimerRemQue(self, pNew); + iStatus = pNew->func(pNew->cntx, 0); + /* + * If this is a recurrent timer and the function + * indicates to keep it going, put it back in + */ + if (pNew->tick && iStatus == 1) { + /* + * While the expiration time is in the past, increment + */ + gettimeofday(&tv, NULL); + while (tv.tv_sec > pNew->tv.tv_sec || + (tv.tv_sec == pNew->tv.tv_sec && + tv.tv_usec > pNew->tv.tv_usec)) { + pNew->tv.tv_usec += 1000 * pNew->tick; + if (pNew->tv.tv_usec > 1000000) { + pNew->tv.tv_sec += pNew->tv.tv_usec / 1000000; + pNew->tv.tv_usec %= 1000000; + } + } + NetWatchTimerInsQue(self, pNew); + } + else { + pNew->vrfy = 0; + free(pNew); + } + } + } + + /* done, finally */ + return 1; +} diff --git a/nwatch.h b/nwatch.h new file mode 100644 index 00000000..fbaeda14 --- /dev/null +++ b/nwatch.h @@ -0,0 +1,105 @@ +/* + * N E T W A T C H E R + * + * This module watches network connections for sockets becoming readable or + * writeable and invokes callbacks. It also provides a timer mechanism. + * + * Douglas Clowes, February 2007 + * + */ +#ifndef SICSNETWATCHER +#define SICSNETWATCHER + +#define nwatch_read 1 +#define nwatch_write 2 +/** + * \brief network or timer callback function + * + * \param context from the network/timer registration + * \param mode + * for network, nwatch_read or nwatch_write + * for timer, zero, reserved for future use + * + * \return normally zero, for future use + */ +typedef int (*pNWCallback)(void* context, int mode); + +/* the abstract timer object handle */ +typedef struct __netwatchtimer *pNWTimer; + +/** + * \brief register a one-shot timer event + * + * \param handle pointer to location to receive the timer object handle + * \param mSec milliseconds after which the timer should expire + * \param callback function when timer expires + * \param context abstract context passed to callback function + * \return success=1, failure=0 + */ +int NetWatchRegisterTimer(pNWTimer* handle, int mSec, + pNWCallback callback, void* context); + +/** + * \brief register a periodic timer + * + * \param handle pointer to location to receive the timer object handle + * \param mSec milliseconds after which the timer should expire + * \param mSecPeriod milliseconds after which the timer should repeat + * \param callback function when timer expires + * \param context abstract context passed to callback function + * \return success=1, failure=0 + */ +int NetWatchRegisterTimerPeriodic(pNWTimer* handle, int mSecInitial, int mSecPeriod, + pNWCallback callback, void* context); + +int NetWatchGetTimerPeriod(pNWTimer handle); +int NetWatchSetTimerPeriod(pNWTimer handle, int mSecPeriod); +/** + * \brief remove a registered timer event + * + * \param handle from the timer registration + * \return success=1, failure=0 + */ +int NetWatchRemoveTimer(pNWTimer handle); + +/* the abstract socket object handle */ +typedef struct __netwatchcontext *pNWContext; + +/** + * \brief register a socket to be watched in read mode + * + * \param handle pointer to location to receive the socket object handle + * \param iSocket file descriptor number of the socket to watch + * \param callback function when socket readable/writeable + * \param context abstract context passed to callback function + * \return success=1, failure=0 + */ +int NetWatchRegisterCallback(pNWContext* handle, int iSocket, + pNWCallback callback, void* context); + +/** + * \brief remove a socket callback registration + * + * \param handle from the socket registration + * \return success=1, failure=0 + */ +int NetWatchRemoveCallback(pNWContext handle); + +/** + * \brief retrieve the mode of a socket callback registration + * + * \param handle from the socket registration + * \return 0=failure else the mode (read and/or write) + */ +int NetWatchGetMode(pNWContext handle); + +/** + * \brief set the mode of a socket callback registration + * + * \param handle from the socket registration + * \param mode read and/or write + * \return 0=failure, 1=success + */ +int NetWatchSetMode(pNWContext handle, int mode); + +#endif /* SICSNETWATCHER */ diff --git a/nxconfig.h b/nxconfig.h index 2e5df705..b532efd6 100644 --- a/nxconfig.h +++ b/nxconfig.h @@ -14,16 +14,22 @@ #define HAVE_INTTYPES_H 1 /* Define to 1 if you have the `df' library (-ldf). */ -#define HAVE_LIBDF 1 +/* #undef HAVE_LIBDF */ + +/* Define to 1 if you have the `dl' library (-ldl). */ +#define HAVE_LIBDL 1 /* Define to 1 if you have the `hdf5' library (-lhdf5). */ -#define HAVE_LIBHDF5 1 +/* #undef HAVE_LIBHDF5 */ /* Define to 1 if you have the `jpeg' library (-ljpeg). */ #define HAVE_LIBJPEG 1 +/* Define to 1 if you have the `m' library (-lm). */ +#define HAVE_LIBM 1 + /* Define to 1 if you have the `mfhdf' library (-lmfhdf). */ -#define HAVE_LIBMFHDF 1 +/* #undef HAVE_LIBMFHDF */ /* Define to 1 if you have the `rpc' library (-lrpc). */ /* #undef HAVE_LIBRPC */ @@ -32,7 +38,7 @@ /* #undef HAVE_LIBSYSTEMSTUBS */ /* Define to 1 if you have the `sz' library (-lsz). */ -#define HAVE_LIBSZ 1 +/* #undef HAVE_LIBSZ */ /* Define to 1 if you have the `xml2' library (-lxml2). */ #define HAVE_LIBXML2 1 @@ -96,19 +102,25 @@ #define PACKAGE "nexus" /* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "nexus-developers@anl.gov" +#define PACKAGE_BUGREPORT "nexus-developers@nexusformat.org" /* Define to the full name of this package. */ #define PACKAGE_NAME "NeXus Library" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "NeXus Library 3.0.1" +#define PACKAGE_STRING "NeXus Library 4.1.0" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "nexus" /* Define to the version of this package. */ -#define PACKAGE_VERSION "3.0.1" +#define PACKAGE_VERSION "4.1.0" + +/* Set to printf format for int64_t */ +#define PRINTF_INT64 "lld" + +/* Set to printf format for uint64_t */ +#define PRINTF_UINT64 "llu" /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 @@ -120,7 +132,7 @@ /* #undef TM_IN_SYS_TIME */ /* Version number of package */ -#define VERSION "3.0.1" +#define VERSION "4.1.0" /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ diff --git a/nxcopy.c b/nxcopy.c new file mode 100644 index 00000000..116b00b1 --- /dev/null +++ b/nxcopy.c @@ -0,0 +1,294 @@ +/** + * This defines a couple of Tcl functions which allow to copy data from the + * nxdataset's returned from the swig Tcl interface to NeXus files into + * SICS data structures. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, October 2007 + */ +#include +#include "nxdataset.h" +#include "sicshipadaba.h" +#include "sicsdata.h" +#include "nxcopy.h" +#include "HistMem.h" +/*------------------------------------------------------------------------------- + * decode a SWIG style pointer into the real pointer value. + * Stolen from SWIG generated code. + --------------------------------------------------------------------------------*/ +static const char *SWIG_UnpackData(const char *c, void *ptr, size_t sz) { + register unsigned char *u = (unsigned char *) ptr; + register const unsigned char *eu = u + sz; + for (; u != eu; ++u) { + register char d = *(c++); + register unsigned char uu; + if ((d >= '0') && (d <= '9')) + uu = ((d - '0') << 4); + else if ((d >= 'a') && (d <= 'f')) + uu = ((d - ('a'-10)) << 4); + else + return (char *) 0; + d = *(c++); + if ((d >= '0') && (d <= '9')) + uu |= (d - '0'); + else if ((d >= 'a') && (d <= 'f')) + uu |= (d - ('a'-10)); + else + return (char *) 0; + *u = uu; + } + return c; +} +/*-------------------------------------------------------------------------------*/ +int isTypeAllowed(int type){ + switch(type){ + case NX_INT32: + case NX_UINT32: + case NX_FLOAT32: + return 1; + break; + default: + return 0; + break; + } +} +/*-------------------------------------------------------------------------------- + * copy an nxdataset to a sicsdata object + --------------------------------------------------------------------------------*/ +static int NXDataToSicsdata(ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[]){ + Tcl_Obj *resultPtr; + const char *text; + pNXDS data = NULL; + pSICSData sicsData; + int length, i; + + if(objc < 3) { + Tcl_WrongNumArgs(interp,objc,objv,"Usage: nxdatatosicsdata nxdata sicsdata"); + return TCL_ERROR; + } + resultPtr = Tcl_GetObjResult(interp); + + /* + * get nxdataset pointer + */ + text = Tcl_GetStringFromObj(objv[1],NULL); + if(strstr(text,"NULL") != NULL){ + Tcl_SetStringObj(resultPtr,"nxdata argument is NULL pointer",38); + return TCL_ERROR; + } + text++; + SWIG_UnpackData(text,&data,sizeof(void *)); + + if(data->magic != MAGIC){ + Tcl_SetStringObj(resultPtr,"nxdata argument is no valid nxdataset", + strlen("nxdata argument is no valid nxdataset")); + return TCL_ERROR; + } + length = getNXDatasetLength(data); + if(!isTypeAllowed(data->type)){ + Tcl_SetStringObj(resultPtr,"can only copy int and float types", + strlen("can only copy int and float types")); + return TCL_ERROR; + } + + /* + * locate sicsdata + */ + text = Tcl_GetStringFromObj(objv[2],NULL); + sicsData = (pSICSData)FindCommandData(pServ->pSics,(char *)text,"SICSData"); + if(sicsData == NULL){ + Tcl_SetStringObj(resultPtr,"sicsdata argument is no valid SICSData", + strlen("sicsdata argument is no valid SICSData")); + return TCL_ERROR; + } + + /* + * copy the data + */ + if(data->type == NX_FLOAT32){ + for(i = 0; i < length; i++){ + setSICSDataFloat(sicsData,i,data->u.fPtr[i]); + } + } else { + for(i = 0; i < length; i++){ + setSICSDataInt(sicsData,i,data->u.iPtr[i]); + } + } + + return TCL_OK; +} +/*--------------------------------------------------------------------------*/ +static hdbValue NXDatasetToHdbValue(pNXDS data){ + hdbValue val; + int length, i; + + length = getNXDatasetLength(data); + switch(getNXDatasetType(data)){ + case NX_INT32: + case NX_UINT32: + if(length == 1){ + val = MakeHdbInt(data->u.iPtr[0]); + } else { + val = MakeHdbIntArray(length,data->u.iPtr); + val.dataType = HIPINTVARAR; + } + break; + case NX_FLOAT32: + if(length == 1){ + val = MakeHdbFloat((double)data->u.fPtr[0]); + } else { + val = makeHdbValue(HIPFLOATAR,length); + for(i = 0; i < length; i++){ + val.v.floatArray[i] = (double)data->u.fPtr[i]; + } + val.dataType = HIPFLOATVARAR; + } + break; + case NX_FLOAT64: + if(length == 1){ + val = MakeHdbFloat(data->u.dPtr[0]); + } else { + val = MakeHdbFloatArray(length,data->u.dPtr); + } + break; + case NX_CHAR: + case NX_INT8: + case NX_UINT8: + val = MakeHdbText(data->u.cPtr); + break; + case NX_INT16: + case NX_UINT16: + if(length == 1){ + val = MakeHdbInt((int)data->u.sPtr[0]); + } else { + val = makeHdbValue(HIPINTAR,length); + for(i = 0; i < length; i++){ + val.v.intArray[i] = (int)data->u.sPtr[i]; + } + } + break; + } + return val; +} +/*--------------------------------------------------------------------------- + * copy the content of an nxdataset onto a Hipadaba node + *---------------------------------------------------------------------------*/ +static int NXDataToHdbNode(ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[]){ + pNXDS data = NULL; + Tcl_Obj *resultPtr; + int length, status; + const char *text; + pHdb node = NULL; + hdbValue val; + + if(objc < 3) { + Tcl_WrongNumArgs(interp,objc,objv,"Usage: nxdatatohdb nxdata path"); + return TCL_ERROR; + } + resultPtr = Tcl_GetObjResult(interp); + + /* + * get nxdataset pointer + */ + text = Tcl_GetStringFromObj(objv[1],NULL); + if(strstr(text,"NULL") != NULL){ + Tcl_SetStringObj(resultPtr,"nxdata argument is NULL pointer",38); + return TCL_ERROR; + } + text++; + SWIG_UnpackData(text,&data,sizeof(void *)); + + if(data->magic != MAGIC){ + Tcl_SetStringObj(resultPtr,"nxdata argument is no valid nxdataset", + strlen("nxdata argument is no valid nxdataset")); + return TCL_ERROR; + } + length = getNXDatasetLength(data); + + /* + * locate node + */ + text = Tcl_GetStringFromObj(objv[2],NULL); + node = GetHipadabaNode(GetHipadabaRoot(),(char *)text); + if(node == NULL){ + Tcl_SetStringObj(resultPtr,"path does not point to a valid node", + strlen("path does not point to a valid node")); + return TCL_ERROR; + } + + val = NXDatasetToHdbValue(data); + status = UpdateHipadabaPar(node,val,NULL); + if(status != 1){ + Tcl_SetStringObj(resultPtr,"data type mismatch", + strlen("data type mismatch")); + return TCL_ERROR; + } + return TCL_OK; +} +/*---------------------------------------------------------------------------*/ +static int NXDataToHM(ClientData clientData, Tcl_Interp *interp, + int objc, Tcl_Obj *CONST objv[]){ + pNXDS data = NULL; + Tcl_Obj *resultPtr; + int length, status; + const char *text; + pHistMem pHM = NULL; + SConnection *pCon = NULL; + + if(objc < 3) { + Tcl_WrongNumArgs(interp,objc,objv,"Usage: nxdatatohdb nxdata path"); + return TCL_ERROR; + } + resultPtr = Tcl_GetObjResult(interp); + + /* + * get nxdataset pointer + */ + text = Tcl_GetStringFromObj(objv[1],NULL); + if(strstr(text,"NULL") != NULL){ + Tcl_SetStringObj(resultPtr,"nxdata argument is NULL pointer",38); + return TCL_ERROR; + } + text++; + SWIG_UnpackData(text,&data,sizeof(void *)); + + if(data->magic != MAGIC){ + Tcl_SetStringObj(resultPtr,"nxdata argument is no valid nxdataset", + strlen("nxdata argument is no valid nxdataset")); + return TCL_ERROR; + } + length = getNXDatasetLength(data); + + /* + * get HM + */ + text = Tcl_GetStringFromObj(objv[2],NULL); + pHM = (pHistMem)FindCommandData(pServ->pSics,(char *)text,"HistMem"); + if(pHM == NULL){ + Tcl_SetStringObj(resultPtr,"hm argument is not histogram memory", + strlen("hm argument is not histogram memory")); + return TCL_ERROR; + } + + if(getNXDatasetType(data) != NX_INT32 || GetHistLength(pHM) < length){ + Tcl_SetStringObj(resultPtr,"data to hm type or length mismatch", + strlen("data to hm type or length mismatch")); + return TCL_ERROR; + } + + pCon = SCCreateDummyConnection(pServ->pSics); + SetHistogram(pHM, pCon, 0,0,length, data->u.iPtr); + SCDeleteConnection(pCon); + + return TCL_OK; +} +/*---------------------------------------------------------------------------*/ +int NXcopy_Init(Tcl_Interp *pInter){ + Tcl_CreateObjCommand(pInter,"nxToSicsData",NXDataToSicsdata,NULL,NULL); + Tcl_CreateObjCommand(pInter,"nxToHdb",NXDataToHdbNode,NULL,NULL); + Tcl_CreateObjCommand(pInter,"nxToHM",NXDataToHM,NULL,NULL); + return 1; +} diff --git a/nxcopy.h b/nxcopy.h new file mode 100644 index 00000000..539fae36 --- /dev/null +++ b/nxcopy.h @@ -0,0 +1,16 @@ +/** + * This defines a couple of Tcl functions which allow to copy data from the + * nxdataset's returned from the swig Tcl interface to NeXus files into + * SICS data structures. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, October 2007 + */ +#ifndef NXCOPY_H_ +#define NXCOPY_H_ +#include + +int NXcopy_Init(Tcl_Interp *pTcl); + +#endif /*NXCOPY_H_*/ diff --git a/nxdataset.c b/nxdataset.c index c64c1321..6d419196 100644 --- a/nxdataset.c +++ b/nxdataset.c @@ -7,6 +7,7 @@ Mark Koennecke, October 2002 */ #include +#include #include #include "nxdataset.h" @@ -19,6 +20,8 @@ static int getTypeSize(int typecode){ return 4; break; case NX_FLOAT64: + case NX_INT64: + case NX_UINT64: return 8; break; case NX_INT16: @@ -208,6 +211,10 @@ double getNXDatasetValueAt(pNXDS dataset, int address){ case NX_UINT32: value = (double)dataset->u.iPtr[address]; break; + case NX_INT64: + case NX_UINT64: + value = (double)dataset->u.lPtr[address]; + break; case NX_INT16: case NX_UINT16: value = (double)dataset->u.sPtr[address]; @@ -224,10 +231,10 @@ char *getNXDatasetText(pNXDS dataset){ int length, status = 1; if(dataset == NULL){ - status = 0; + return strdup("NULL"); } if(dataset->magic != MAGIC){ - status = 0; + return strdup("NULL"); } if(dataset->rank > 1){ status = 0; @@ -236,6 +243,8 @@ char *getNXDatasetText(pNXDS dataset){ dataset->type == NX_FLOAT64 || dataset->type == NX_INT32 || dataset->type == NX_UINT32 || + dataset->type == NX_INT64 || + dataset->type == NX_UINT64 || dataset->type == NX_INT16 || dataset->type == NX_UINT16 ) { status = 0; @@ -284,6 +293,10 @@ int putNXDatasetValueAt(pNXDS dataset, int address, double value){ case NX_UINT32: dataset->u.iPtr[address] = (int)value; break; + case NX_INT64: + case NX_UINT64: + dataset->u.lPtr[address] = (int64_t)value; + break; case NX_INT16: case NX_UINT16: dataset->u.sPtr[address] = (short int)value; @@ -294,6 +307,140 @@ int putNXDatasetValueAt(pNXDS dataset, int address, double value){ } return 1; } +/*---------------------------------------------------------------------- + This is working recursively through the dimensions. When at the last: + actual copying takes place. + -----------------------------------------------------------------------*/ +static void copyCutData(pNXDS source, pNXDS target, int sourceDim[], + int targetDim[], int start[], int end[], + int dim){ + int i, length; + double val; + targetDim[dim] = 0; + length = end[dim] - start[dim]; + if(dim == source->rank -1){ + for(i = 0; i < length; i++){ + sourceDim[dim] = start[dim] + i; + val = getNXDatasetValue(source,sourceDim); + targetDim[dim] = i; + putNXDatasetValue(target, targetDim, val); + } + } else { + for(i = 0; i < length; i++){ + sourceDim[dim] = start[dim] + i; + targetDim[dim] = i; + copyCutData(source,target, sourceDim, targetDim, start, end, dim+1); + } + } +} +/*-----------------------------------------------------------------------*/ +pNXDS cutNXDataset(pNXDS source, int start[], int end[]){ + pNXDS result = NULL; + int newDim[NX_MAXRANK], i; + int sourceDim[NX_MAXRANK], targetDim[NX_MAXRANK]; + for(i = 0; i < source->rank; i++){ + if(start[i] < 0 || end[i] > source->dim[i]){ + fprintf(stderr,"ERROR: invalid boundaries specified for cutting"); + return NULL; + } + newDim[i] = end[i] - start[i]; + if(newDim[i] <= 0){ + fprintf(stderr,"ERROR: invalid cut limits specified for cutting dataset"); + return NULL; + } + } + result = createNXDataset(source->rank, source->type, newDim); + if(result == NULL){ + fprintf(stderr,"ERROR: out of memory creating result dataset"); + return NULL; + } + + copyCutData(source, result, sourceDim, targetDim, start, end, 0); + return result; +} +/*---------------------------------------------------------------------- + This recurses through all dimesnions, thereby skipping the summed one. + At the end of the rescusion the actual summing is performed. + ----------------------------------------------------------------------*/ +static void sumData(pNXDS source, pNXDS target, int sourceDim[], + int targetDim[], int targetDimCount, int dimNo, + int start, int end, int currentDim){ + int i, length; + double val, sumVal; + + /* + when we have recursed through all dimensions + we actually do the sums... + */ + if(currentDim == source->rank){ + length = end - start; + sumVal = getNXDatasetValue(target, targetDim); + for(i = 0; i < length; i++){ + sourceDim[dimNo] = start + i; + val = getNXDatasetValue(source,sourceDim); + sumVal += val; + } + putNXDatasetValue(target, targetDim, sumVal); + } else { + /* + jump over the summed dimension while recursing + through the dimensions + */ + if(currentDim == dimNo){ + sumData(source,target,sourceDim, + targetDim,targetDimCount, + dimNo,start,end,currentDim+1); + } else { + /* + loop over all values of the non summed dimension + */ + for(i = 0; i < source->dim[currentDim]; i++){ + /* + the problem here is that we have to jump over the summed + dimension here. This why we have to maintain a separate + dimension count for the target array. Jumping is done + above. + */ + targetDim[targetDimCount] = i; + targetDimCount++; + + sourceDim[currentDim] = i; + sumData(source,target,sourceDim,targetDim,targetDimCount, + dimNo,start,end,currentDim+1); + targetDimCount--; + } + } + } +} +/*-----------------------------------------------------------------------*/ +pNXDS sumNXDataset(pNXDS source, int dimNo, int start, int end){ + int newDim[NX_MAXRANK], targetDim[NX_MAXRANK], sourceDim[NX_MAXRANK]; + pNXDS result = NULL; + int i, count; + + if(dimNo < 0 || dimNo > source->rank-1) { + fprintf(stderr,"ERROR: invalid dimension for summing requested"); + return NULL; + } + + /* + make result dataset with missing summed dimension + */ + for(i = 0, count = 0; i < source->rank; i++){ + if(i != dimNo){ + newDim[count] = source->dim[i]; + count++; + } + } + result = createNXDataset(source->rank-1, source->type, newDim); + if(result == NULL){ + fprintf(stderr,"ERROR: out of memory creating result dataset"); + return NULL; + } + sumData(source,result,sourceDim,targetDim,0, + dimNo, start, end, 0); + return result; +} diff --git a/nxdataset.h b/nxdataset.h index c8613aa1..ab5d33a9 100644 --- a/nxdataset.h +++ b/nxdataset.h @@ -12,6 +12,8 @@ #define MAGIC 7776622 +#include "napiconfig.h" + typedef struct { int magic; int rank; @@ -25,6 +27,7 @@ typedef struct { int *iPtr; short int *sPtr; char *cPtr; + int64_t *lPtr; } u; }*pNXDS, NXDS; @@ -41,6 +44,8 @@ typedef struct { #define NX_UINT16 23 #define NX_INT32 24 #define NX_UINT32 25 +#define NX_INT64 26 +#define NX_UINT64 27 #define NX_CHAR 4 #define NX_MAXRANK 32 @@ -66,4 +71,7 @@ char *getNXDatasetText(pNXDS dataset); int putNXDatasetValue(pNXDS dataset, int pos[], double value); int putNXDatasetValueAt(pNXDS dataset, int address, double value); +pNXDS cutNXDataset(pNXDS source, int start[], int end[]); +pNXDS sumNXDataset(pNXDS source, int dimNo, int start, int end); + #endif diff --git a/nxdict.c b/nxdict.c index 7453988d..25737a7e 100644 --- a/nxdict.c +++ b/nxdict.c @@ -1164,11 +1164,14 @@ */ if(iChunkDefined == 0) { - for(i = 0; i < iRank; i++) - { - iChunk[i] = iDim[i]; + for(i = 0; i < iRank; i++) + { + iChunk[i] = iDim[i]; } } + if(iChunk[0] < 0) { + iChunk[0] = 1; + } /* first install dummy error handler, try open it, then deinstall again and create if allowed @@ -1349,19 +1352,8 @@ iRet = NXDIUnwind(hfil,pParse.iDepth); return NX_ERROR; } - - /* try rewinding the hierarchy */ - if(pParse.iTerminal == TERMSDS) - { - iStat = NXDIUnwind(hfil,pParse.iDepth); - if(iStat != NX_OK) - { - return NX_ERROR; - } - } - /* do not rewind on links */ - return iRet; + return iRet; } /*------------------------------------------------------------------------*/ NXstatus NXDopenalias(NXhandle hfil, NXdict dict, char *pAlias) diff --git a/nxinter_wrap.c b/nxinter_wrap.c new file mode 100644 index 00000000..a2dda998 --- /dev/null +++ b/nxinter_wrap.c @@ -0,0 +1,3516 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version 1.3.31 + * + * This file is not intended to be easily readable and contains a number of + * coding conventions designed to improve portability and efficiency. Do not make + * changes to this file unless you know what you are doing--modify the SWIG + * interface file instead. + * ----------------------------------------------------------------------------- */ + +/* ----------------------------------------------------------------------------- + * This section contains generic SWIG labels for method/variable + * declarations/attributes, and other compiler dependent labels. + * ----------------------------------------------------------------------------- */ + +/* template workaround for compilers that cannot correctly implement the C++ standard */ +#ifndef SWIGTEMPLATEDISAMBIGUATOR +# if defined(__SUNPRO_CC) +# if (__SUNPRO_CC <= 0x560) +# define SWIGTEMPLATEDISAMBIGUATOR template +# else +# define SWIGTEMPLATEDISAMBIGUATOR +# endif +# else +# define SWIGTEMPLATEDISAMBIGUATOR +# endif +#endif + +/* inline attribute */ +#ifndef SWIGINLINE +# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__)) +# define SWIGINLINE inline +# else +# define SWIGINLINE +# endif +#endif + +/* attribute recognised by some compilers to avoid 'unused' warnings */ +#ifndef SWIGUNUSED +# if defined(__GNUC__) +# if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +# elif defined(__ICC) +# define SWIGUNUSED __attribute__ ((__unused__)) +# else +# define SWIGUNUSED +# endif +#endif + +#ifndef SWIGUNUSEDPARM +# ifdef __cplusplus +# define SWIGUNUSEDPARM(p) +# else +# define SWIGUNUSEDPARM(p) p SWIGUNUSED +# endif +#endif + +/* internal SWIG method */ +#ifndef SWIGINTERN +# define SWIGINTERN static SWIGUNUSED +#endif + +/* internal inline SWIG method */ +#ifndef SWIGINTERNINLINE +# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE +#endif + +/* exporting methods */ +#if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) +# ifndef GCC_HASCLASSVISIBILITY +# define GCC_HASCLASSVISIBILITY +# endif +#endif + +#ifndef SWIGEXPORT +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# if defined(STATIC_LINKED) +# define SWIGEXPORT +# else +# define SWIGEXPORT __declspec(dllexport) +# endif +# else +# if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY) +# define SWIGEXPORT __attribute__ ((visibility("default"))) +# else +# define SWIGEXPORT +# endif +# endif +#endif + +/* calling conventions for Windows */ +#ifndef SWIGSTDCALL +# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) +# define SWIGSTDCALL __stdcall +# else +# define SWIGSTDCALL +# endif +#endif + +/* Deal with Microsoft's attempt at deprecating C standard runtime functions */ +#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE) +# define _CRT_SECURE_NO_DEPRECATE +#endif + + +#include +#include +#include +#include +#include +#include + +/* ----------------------------------------------------------------------------- + * swigrun.swg + * + * This file contains generic CAPI SWIG runtime support for pointer + * type checking. + * ----------------------------------------------------------------------------- */ + +/* This should only be incremented when either the layout of swig_type_info changes, + or for whatever reason, the runtime changes incompatibly */ +#define SWIG_RUNTIME_VERSION "3" + +/* define SWIG_TYPE_TABLE_NAME as "SWIG_TYPE_TABLE" */ +#ifdef SWIG_TYPE_TABLE +# define SWIG_QUOTE_STRING(x) #x +# define SWIG_EXPAND_AND_QUOTE_STRING(x) SWIG_QUOTE_STRING(x) +# define SWIG_TYPE_TABLE_NAME SWIG_EXPAND_AND_QUOTE_STRING(SWIG_TYPE_TABLE) +#else +# define SWIG_TYPE_TABLE_NAME +#endif + +/* + You can use the SWIGRUNTIME and SWIGRUNTIMEINLINE macros for + creating a static or dynamic library from the swig runtime code. + In 99.9% of the cases, swig just needs to declare them as 'static'. + + But only do this if is strictly necessary, ie, if you have problems + with your compiler or so. +*/ + +#ifndef SWIGRUNTIME +# define SWIGRUNTIME SWIGINTERN +#endif + +#ifndef SWIGRUNTIMEINLINE +# define SWIGRUNTIMEINLINE SWIGRUNTIME SWIGINLINE +#endif + +/* Generic buffer size */ +#ifndef SWIG_BUFFER_SIZE +# define SWIG_BUFFER_SIZE 1024 +#endif + +/* Flags for pointer conversions */ +#define SWIG_POINTER_DISOWN 0x1 + +/* Flags for new pointer objects */ +#define SWIG_POINTER_OWN 0x1 + + +/* + Flags/methods for returning states. + + The swig conversion methods, as ConvertPtr, return and integer + that tells if the conversion was successful or not. And if not, + an error code can be returned (see swigerrors.swg for the codes). + + Use the following macros/flags to set or process the returning + states. + + In old swig versions, you usually write code as: + + if (SWIG_ConvertPtr(obj,vptr,ty.flags) != -1) { + // success code + } else { + //fail code + } + + Now you can be more explicit as: + + int res = SWIG_ConvertPtr(obj,vptr,ty.flags); + if (SWIG_IsOK(res)) { + // success code + } else { + // fail code + } + + that seems to be the same, but now you can also do + + Type *ptr; + int res = SWIG_ConvertPtr(obj,(void **)(&ptr),ty.flags); + if (SWIG_IsOK(res)) { + // success code + if (SWIG_IsNewObj(res) { + ... + delete *ptr; + } else { + ... + } + } else { + // fail code + } + + I.e., now SWIG_ConvertPtr can return new objects and you can + identify the case and take care of the deallocation. Of course that + requires also to SWIG_ConvertPtr to return new result values, as + + int SWIG_ConvertPtr(obj, ptr,...) { + if () { + if () { + *ptr = ; + return SWIG_NEWOBJ; + } else { + *ptr = ; + return SWIG_OLDOBJ; + } + } else { + return SWIG_BADOBJ; + } + } + + Of course, returning the plain '0(success)/-1(fail)' still works, but you can be + more explicit by returning SWIG_BADOBJ, SWIG_ERROR or any of the + swig errors code. + + Finally, if the SWIG_CASTRANK_MODE is enabled, the result code + allows to return the 'cast rank', for example, if you have this + + int food(double) + int fooi(int); + + and you call + + food(1) // cast rank '1' (1 -> 1.0) + fooi(1) // cast rank '0' + + just use the SWIG_AddCast()/SWIG_CheckState() + + + */ +#define SWIG_OK (0) +#define SWIG_ERROR (-1) +#define SWIG_IsOK(r) (r >= 0) +#define SWIG_ArgError(r) ((r != SWIG_ERROR) ? r : SWIG_TypeError) + +/* The CastRankLimit says how many bits are used for the cast rank */ +#define SWIG_CASTRANKLIMIT (1 << 8) +/* The NewMask denotes the object was created (using new/malloc) */ +#define SWIG_NEWOBJMASK (SWIG_CASTRANKLIMIT << 1) +/* The TmpMask is for in/out typemaps that use temporal objects */ +#define SWIG_TMPOBJMASK (SWIG_NEWOBJMASK << 1) +/* Simple returning values */ +#define SWIG_BADOBJ (SWIG_ERROR) +#define SWIG_OLDOBJ (SWIG_OK) +#define SWIG_NEWOBJ (SWIG_OK | SWIG_NEWOBJMASK) +#define SWIG_TMPOBJ (SWIG_OK | SWIG_TMPOBJMASK) +/* Check, add and del mask methods */ +#define SWIG_AddNewMask(r) (SWIG_IsOK(r) ? (r | SWIG_NEWOBJMASK) : r) +#define SWIG_DelNewMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_NEWOBJMASK) : r) +#define SWIG_IsNewObj(r) (SWIG_IsOK(r) && (r & SWIG_NEWOBJMASK)) +#define SWIG_AddTmpMask(r) (SWIG_IsOK(r) ? (r | SWIG_TMPOBJMASK) : r) +#define SWIG_DelTmpMask(r) (SWIG_IsOK(r) ? (r & ~SWIG_TMPOBJMASK) : r) +#define SWIG_IsTmpObj(r) (SWIG_IsOK(r) && (r & SWIG_TMPOBJMASK)) + + +/* Cast-Rank Mode */ +#if defined(SWIG_CASTRANK_MODE) +# ifndef SWIG_TypeRank +# define SWIG_TypeRank unsigned long +# endif +# ifndef SWIG_MAXCASTRANK /* Default cast allowed */ +# define SWIG_MAXCASTRANK (2) +# endif +# define SWIG_CASTRANKMASK ((SWIG_CASTRANKLIMIT) -1) +# define SWIG_CastRank(r) (r & SWIG_CASTRANKMASK) +SWIGINTERNINLINE int SWIG_AddCast(int r) { + return SWIG_IsOK(r) ? ((SWIG_CastRank(r) < SWIG_MAXCASTRANK) ? (r + 1) : SWIG_ERROR) : r; +} +SWIGINTERNINLINE int SWIG_CheckState(int r) { + return SWIG_IsOK(r) ? SWIG_CastRank(r) + 1 : 0; +} +#else /* no cast-rank mode */ +# define SWIG_AddCast +# define SWIG_CheckState(r) (SWIG_IsOK(r) ? 1 : 0) +#endif + + + + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *(*swig_converter_func)(void *); +typedef struct swig_type_info *(*swig_dycast_func)(void **); + +/* Structure to store inforomation on one type */ +typedef struct swig_type_info { + const char *name; /* mangled name of this type */ + const char *str; /* human readable name of this type */ + swig_dycast_func dcast; /* dynamic cast function down a hierarchy */ + struct swig_cast_info *cast; /* linked list of types that can cast into this type */ + void *clientdata; /* language specific type data */ + int owndata; /* flag if the structure owns the clientdata */ +} swig_type_info; + +/* Structure to store a type and conversion function used for casting */ +typedef struct swig_cast_info { + swig_type_info *type; /* pointer to type that is equivalent to this type */ + swig_converter_func converter; /* function to cast the void pointers */ + struct swig_cast_info *next; /* pointer to next cast in linked list */ + struct swig_cast_info *prev; /* pointer to the previous cast */ +} swig_cast_info; + +/* Structure used to store module information + * Each module generates one structure like this, and the runtime collects + * all of these structures and stores them in a circularly linked list.*/ +typedef struct swig_module_info { + swig_type_info **types; /* Array of pointers to swig_type_info structures that are in this module */ + size_t size; /* Number of types in this module */ + struct swig_module_info *next; /* Pointer to next element in circularly linked list */ + swig_type_info **type_initial; /* Array of initially generated type structures */ + swig_cast_info **cast_initial; /* Array of initially generated casting structures */ + void *clientdata; /* Language specific module data */ +} swig_module_info; + +/* + Compare two type names skipping the space characters, therefore + "char*" == "char *" and "Class" == "Class", etc. + + Return 0 when the two name types are equivalent, as in + strncmp, but skipping ' '. +*/ +SWIGRUNTIME int +SWIG_TypeNameComp(const char *f1, const char *l1, + const char *f2, const char *l2) { + for (;(f1 != l1) && (f2 != l2); ++f1, ++f2) { + while ((*f1 == ' ') && (f1 != l1)) ++f1; + while ((*f2 == ' ') && (f2 != l2)) ++f2; + if (*f1 != *f2) return (*f1 > *f2) ? 1 : -1; + } + return (l1 - f1) - (l2 - f2); +} + +/* + Check type equivalence in a name list like ||... + Return 0 if not equal, 1 if equal +*/ +SWIGRUNTIME int +SWIG_TypeEquiv(const char *nb, const char *tb) { + int equiv = 0; + const char* te = tb + strlen(tb); + const char* ne = nb; + while (!equiv && *ne) { + for (nb = ne; *ne; ++ne) { + if (*ne == '|') break; + } + equiv = (SWIG_TypeNameComp(nb, ne, tb, te) == 0) ? 1 : 0; + if (*ne) ++ne; + } + return equiv; +} + +/* + Check type equivalence in a name list like ||... + Return 0 if equal, -1 if nb < tb, 1 if nb > tb +*/ +SWIGRUNTIME int +SWIG_TypeCompare(const char *nb, const char *tb) { + int equiv = 0; + const char* te = tb + strlen(tb); + const char* ne = nb; + while (!equiv && *ne) { + for (nb = ne; *ne; ++ne) { + if (*ne == '|') break; + } + equiv = (SWIG_TypeNameComp(nb, ne, tb, te) == 0) ? 1 : 0; + if (*ne) ++ne; + } + return equiv; +} + + +/* think of this as a c++ template<> or a scheme macro */ +#define SWIG_TypeCheck_Template(comparison, ty) \ + if (ty) { \ + swig_cast_info *iter = ty->cast; \ + while (iter) { \ + if (comparison) { \ + if (iter == ty->cast) return iter; \ + /* Move iter to the top of the linked list */ \ + iter->prev->next = iter->next; \ + if (iter->next) \ + iter->next->prev = iter->prev; \ + iter->next = ty->cast; \ + iter->prev = 0; \ + if (ty->cast) ty->cast->prev = iter; \ + ty->cast = iter; \ + return iter; \ + } \ + iter = iter->next; \ + } \ + } \ + return 0 + +/* + Check the typename +*/ +SWIGRUNTIME swig_cast_info * +SWIG_TypeCheck(const char *c, swig_type_info *ty) { + SWIG_TypeCheck_Template(strcmp(iter->type->name, c) == 0, ty); +} + +/* Same as previous function, except strcmp is replaced with a pointer comparison */ +SWIGRUNTIME swig_cast_info * +SWIG_TypeCheckStruct(swig_type_info *from, swig_type_info *into) { + SWIG_TypeCheck_Template(iter->type == from, into); +} + +/* + Cast a pointer up an inheritance hierarchy +*/ +SWIGRUNTIMEINLINE void * +SWIG_TypeCast(swig_cast_info *ty, void *ptr) { + return ((!ty) || (!ty->converter)) ? ptr : (*ty->converter)(ptr); +} + +/* + Dynamic pointer casting. Down an inheritance hierarchy +*/ +SWIGRUNTIME swig_type_info * +SWIG_TypeDynamicCast(swig_type_info *ty, void **ptr) { + swig_type_info *lastty = ty; + if (!ty || !ty->dcast) return ty; + while (ty && (ty->dcast)) { + ty = (*ty->dcast)(ptr); + if (ty) lastty = ty; + } + return lastty; +} + +/* + Return the name associated with this type +*/ +SWIGRUNTIMEINLINE const char * +SWIG_TypeName(const swig_type_info *ty) { + return ty->name; +} + +/* + Return the pretty name associated with this type, + that is an unmangled type name in a form presentable to the user. +*/ +SWIGRUNTIME const char * +SWIG_TypePrettyName(const swig_type_info *type) { + /* The "str" field contains the equivalent pretty names of the + type, separated by vertical-bar characters. We choose + to print the last name, as it is often (?) the most + specific. */ + if (!type) return NULL; + if (type->str != NULL) { + const char *last_name = type->str; + const char *s; + for (s = type->str; *s; s++) + if (*s == '|') last_name = s+1; + return last_name; + } + else + return type->name; +} + +/* + Set the clientdata field for a type +*/ +SWIGRUNTIME void +SWIG_TypeClientData(swig_type_info *ti, void *clientdata) { + swig_cast_info *cast = ti->cast; + /* if (ti->clientdata == clientdata) return; */ + ti->clientdata = clientdata; + + while (cast) { + if (!cast->converter) { + swig_type_info *tc = cast->type; + if (!tc->clientdata) { + SWIG_TypeClientData(tc, clientdata); + } + } + cast = cast->next; + } +} +SWIGRUNTIME void +SWIG_TypeNewClientData(swig_type_info *ti, void *clientdata) { + SWIG_TypeClientData(ti, clientdata); + ti->owndata = 1; +} + +/* + Search for a swig_type_info structure only by mangled name + Search is a O(log #types) + + We start searching at module start, and finish searching when start == end. + Note: if start == end at the beginning of the function, we go all the way around + the circular list. +*/ +SWIGRUNTIME swig_type_info * +SWIG_MangledTypeQueryModule(swig_module_info *start, + swig_module_info *end, + const char *name) { + swig_module_info *iter = start; + do { + if (iter->size) { + register size_t l = 0; + register size_t r = iter->size - 1; + do { + /* since l+r >= 0, we can (>> 1) instead (/ 2) */ + register size_t i = (l + r) >> 1; + const char *iname = iter->types[i]->name; + if (iname) { + register int compare = strcmp(name, iname); + if (compare == 0) { + return iter->types[i]; + } else if (compare < 0) { + if (i) { + r = i - 1; + } else { + break; + } + } else if (compare > 0) { + l = i + 1; + } + } else { + break; /* should never happen */ + } + } while (l <= r); + } + iter = iter->next; + } while (iter != end); + return 0; +} + +/* + Search for a swig_type_info structure for either a mangled name or a human readable name. + It first searches the mangled names of the types, which is a O(log #types) + If a type is not found it then searches the human readable names, which is O(#types). + + We start searching at module start, and finish searching when start == end. + Note: if start == end at the beginning of the function, we go all the way around + the circular list. +*/ +SWIGRUNTIME swig_type_info * +SWIG_TypeQueryModule(swig_module_info *start, + swig_module_info *end, + const char *name) { + /* STEP 1: Search the name field using binary search */ + swig_type_info *ret = SWIG_MangledTypeQueryModule(start, end, name); + if (ret) { + return ret; + } else { + /* STEP 2: If the type hasn't been found, do a complete search + of the str field (the human readable name) */ + swig_module_info *iter = start; + do { + register size_t i = 0; + for (; i < iter->size; ++i) { + if (iter->types[i]->str && (SWIG_TypeEquiv(iter->types[i]->str, name))) + return iter->types[i]; + } + iter = iter->next; + } while (iter != end); + } + + /* neither found a match */ + return 0; +} + +/* + Pack binary data into a string +*/ +SWIGRUNTIME char * +SWIG_PackData(char *c, void *ptr, size_t sz) { + static const char hex[17] = "0123456789abcdef"; + register const unsigned char *u = (unsigned char *) ptr; + register const unsigned char *eu = u + sz; + for (; u != eu; ++u) { + register unsigned char uu = *u; + *(c++) = hex[(uu & 0xf0) >> 4]; + *(c++) = hex[uu & 0xf]; + } + return c; +} + +/* + Unpack binary data from a string +*/ +SWIGRUNTIME const char * +SWIG_UnpackData(const char *c, void *ptr, size_t sz) { + register unsigned char *u = (unsigned char *) ptr; + register const unsigned char *eu = u + sz; + for (; u != eu; ++u) { + register char d = *(c++); + register unsigned char uu; + if ((d >= '0') && (d <= '9')) + uu = ((d - '0') << 4); + else if ((d >= 'a') && (d <= 'f')) + uu = ((d - ('a'-10)) << 4); + else + return (char *) 0; + d = *(c++); + if ((d >= '0') && (d <= '9')) + uu |= (d - '0'); + else if ((d >= 'a') && (d <= 'f')) + uu |= (d - ('a'-10)); + else + return (char *) 0; + *u = uu; + } + return c; +} + +/* + Pack 'void *' into a string buffer. +*/ +SWIGRUNTIME char * +SWIG_PackVoidPtr(char *buff, void *ptr, const char *name, size_t bsz) { + char *r = buff; + if ((2*sizeof(void *) + 2) > bsz) return 0; + *(r++) = '_'; + r = SWIG_PackData(r,&ptr,sizeof(void *)); + if (strlen(name) + 1 > (bsz - (r - buff))) return 0; + strcpy(r,name); + return buff; +} + +SWIGRUNTIME const char * +SWIG_UnpackVoidPtr(const char *c, void **ptr, const char *name) { + if (*c != '_') { + if (strcmp(c,"NULL") == 0) { + *ptr = (void *) 0; + return name; + } else { + return 0; + } + } + return SWIG_UnpackData(++c,ptr,sizeof(void *)); +} + +SWIGRUNTIME char * +SWIG_PackDataName(char *buff, void *ptr, size_t sz, const char *name, size_t bsz) { + char *r = buff; + size_t lname = (name ? strlen(name) : 0); + if ((2*sz + 2 + lname) > bsz) return 0; + *(r++) = '_'; + r = SWIG_PackData(r,ptr,sz); + if (lname) { + strncpy(r,name,lname+1); + } else { + *r = 0; + } + return buff; +} + +SWIGRUNTIME const char * +SWIG_UnpackDataName(const char *c, void *ptr, size_t sz, const char *name) { + if (*c != '_') { + if (strcmp(c,"NULL") == 0) { + memset(ptr,0,sz); + return name; + } else { + return 0; + } + } + return SWIG_UnpackData(++c,ptr,sz); +} + +#ifdef __cplusplus +} +#endif + +/* Errors in SWIG */ +#define SWIG_UnknownError -1 +#define SWIG_IOError -2 +#define SWIG_RuntimeError -3 +#define SWIG_IndexError -4 +#define SWIG_TypeError -5 +#define SWIG_DivisionByZero -6 +#define SWIG_OverflowError -7 +#define SWIG_SyntaxError -8 +#define SWIG_ValueError -9 +#define SWIG_SystemError -10 +#define SWIG_AttributeError -11 +#define SWIG_MemoryError -12 +#define SWIG_NullReferenceError -13 + + + +/* ----------------------------------------------------------------------------- + * error manipulation + * ----------------------------------------------------------------------------- */ + +SWIGINTERN const char* +SWIG_Tcl_ErrorType(int code) { + const char* type = 0; + switch(code) { + case SWIG_MemoryError: + type = "MemoryError"; + break; + case SWIG_IOError: + type = "IOError"; + break; + case SWIG_RuntimeError: + type = "RuntimeError"; + break; + case SWIG_IndexError: + type = "IndexError"; + break; + case SWIG_TypeError: + type = "TypeError"; + break; + case SWIG_DivisionByZero: + type = "ZeroDivisionError"; + break; + case SWIG_OverflowError: + type = "OverflowError"; + break; + case SWIG_SyntaxError: + type = "SyntaxError"; + break; + case SWIG_ValueError: + type = "ValueError"; + break; + case SWIG_SystemError: + type = "SystemError"; + break; + case SWIG_AttributeError: + type = "AttributeError"; + break; + default: + type = "RuntimeError"; + } + return type; +} + + +SWIGINTERN void +SWIG_Tcl_SetErrorObj(Tcl_Interp *interp, const char *ctype, Tcl_Obj *obj) +{ + Tcl_ResetResult(interp); + Tcl_SetObjResult(interp, obj); + Tcl_SetErrorCode(interp, "SWIG", ctype, NULL); +} + +SWIGINTERN void +SWIG_Tcl_SetErrorMsg(Tcl_Interp *interp, const char *ctype, const char *mesg) +{ + Tcl_ResetResult(interp); + Tcl_SetErrorCode(interp, "SWIG", ctype, NULL); + Tcl_AppendResult(interp, ctype, " ", mesg, NULL); + /* + Tcl_AddErrorInfo(interp, ctype); + Tcl_AddErrorInfo(interp, " "); + Tcl_AddErrorInfo(interp, mesg); + */ +} + +SWIGINTERNINLINE void +SWIG_Tcl_AddErrorMsg(Tcl_Interp *interp, const char* mesg) +{ + Tcl_AddErrorInfo(interp, mesg); +} + + + +/* ----------------------------------------------------------------------------- + * SWIG API. Portion that goes into the runtime + * ----------------------------------------------------------------------------- */ +#ifdef __cplusplus +extern "C" { +#endif + +/* ----------------------------------------------------------------------------- + * Constant declarations + * ----------------------------------------------------------------------------- */ + +/* Constant Types */ +#define SWIG_TCL_POINTER 4 +#define SWIG_TCL_BINARY 5 + +/* Constant information structure */ +typedef struct swig_const_info { + int type; + char *name; + long lvalue; + double dvalue; + void *pvalue; + swig_type_info **ptype; +} swig_const_info; + +typedef int (*swig_wrapper)(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); +typedef int (*swig_wrapper_func)(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); +typedef char *(*swig_variable_func)(ClientData, Tcl_Interp *, char *, char *, int); +typedef void (*swig_delete_func)(ClientData); + +typedef struct swig_method { + const char *name; + swig_wrapper method; +} swig_method; + +typedef struct swig_attribute { + const char *name; + swig_wrapper getmethod; + swig_wrapper setmethod; +} swig_attribute; + +typedef struct swig_class { + const char *name; + swig_type_info **type; + swig_wrapper constructor; + void (*destructor)(void *); + swig_method *methods; + swig_attribute *attributes; + struct swig_class **bases; + char **base_names; + swig_module_info *module; +} swig_class; + +typedef struct swig_instance { + Tcl_Obj *thisptr; + void *thisvalue; + swig_class *classptr; + int destroy; + Tcl_Command cmdtok; +} swig_instance; + +/* Structure for command table */ +typedef struct { + const char *name; + int (*wrapper)(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []); + ClientData clientdata; +} swig_command_info; + +/* Structure for variable linking table */ +typedef struct { + const char *name; + void *addr; + char * (*get)(ClientData, Tcl_Interp *, char *, char *, int); + char * (*set)(ClientData, Tcl_Interp *, char *, char *, int); +} swig_var_info; + + +/* -----------------------------------------------------------------------------* + * Install a constant object + * -----------------------------------------------------------------------------*/ + +static Tcl_HashTable swigconstTable; +static int swigconstTableinit = 0; + +SWIGINTERN void +SWIG_Tcl_SetConstantObj(Tcl_Interp *interp, const char* name, Tcl_Obj *obj) { + int newobj; + Tcl_ObjSetVar2(interp,Tcl_NewStringObj(name,-1), NULL, obj, TCL_GLOBAL_ONLY); + Tcl_SetHashValue(Tcl_CreateHashEntry(&swigconstTable, name, &newobj), (ClientData) obj); +} + +SWIGINTERN Tcl_Obj * +SWIG_Tcl_GetConstantObj(const char *key) { + Tcl_HashEntry *entryPtr; + if (!swigconstTableinit) return 0; + entryPtr = Tcl_FindHashEntry(&swigconstTable, key); + if (entryPtr) { + return (Tcl_Obj *) Tcl_GetHashValue(entryPtr); + } + return 0; +} + +#ifdef __cplusplus +} +#endif + + + +/* ----------------------------------------------------------------------------- + * See the LICENSE file for information on copyright, usage and redistribution + * of SWIG, and the README file for authors - http://www.swig.org/release.html. + * + * tclrun.swg + * + * This file contains the runtime support for Tcl modules and includes + * code for managing global variables and pointer type checking. + * ----------------------------------------------------------------------------- */ + +/* Common SWIG API */ + +/* for raw pointers */ +#define SWIG_ConvertPtr(oc, ptr, ty, flags) SWIG_Tcl_ConvertPtr(interp, oc, ptr, ty, flags) +#define SWIG_NewPointerObj(ptr, type, flags) SWIG_Tcl_NewPointerObj(ptr, type, flags) + +/* for raw packed data */ +#define SWIG_ConvertPacked(obj, ptr, sz, ty) SWIG_Tcl_ConvertPacked(interp, obj, ptr, sz, ty) +#define SWIG_NewPackedObj(ptr, sz, type) SWIG_Tcl_NewPackedObj(ptr, sz, type) + +/* for class or struct pointers */ +#define SWIG_ConvertInstance(obj, pptr, type, flags) SWIG_Tcl_ConvertPtr(interp, obj, pptr, type, flags) +#define SWIG_NewInstanceObj(thisvalue, type, flags) SWIG_Tcl_NewInstanceObj(interp, thisvalue, type, flags) + +/* for C or C++ function pointers */ +#define SWIG_ConvertFunctionPtr(obj, pptr, type) SWIG_Tcl_ConvertPtr(interp, obj, pptr, type, 0) +#define SWIG_NewFunctionPtrObj(ptr, type) SWIG_Tcl_NewPointerObj(ptr, type, 0) + +/* for C++ member pointers, ie, member methods */ +#define SWIG_ConvertMember(obj, ptr, sz, ty) SWIG_Tcl_ConvertPacked(interp,obj, ptr, sz, ty) +#define SWIG_NewMemberObj(ptr, sz, type) SWIG_Tcl_NewPackedObj(ptr, sz, type) + + +/* Runtime API */ + +#define SWIG_GetModule(clientdata) SWIG_Tcl_GetModule((Tcl_Interp *) (clientdata)) +#define SWIG_SetModule(clientdata, pointer) SWIG_Tcl_SetModule((Tcl_Interp *) (clientdata), pointer) + + +/* Error manipulation */ + +#define SWIG_ErrorType(code) SWIG_Tcl_ErrorType(code) +#define SWIG_Error(code, msg) SWIG_Tcl_SetErrorMsg(interp, SWIG_Tcl_ErrorType(code), msg) +#define SWIG_fail goto fail + + +/* Tcl-specific SWIG API */ + +#define SWIG_Acquire(ptr) SWIG_Tcl_Acquire(ptr) +#define SWIG_MethodCommand SWIG_Tcl_MethodCommand +#define SWIG_Disown(ptr) SWIG_Tcl_Disown(ptr) +#define SWIG_ConvertPtrFromString(c, ptr, ty, flags) SWIG_Tcl_ConvertPtrFromString(interp, c, ptr, ty, flags) +#define SWIG_MakePtr(c, ptr, ty, flags) SWIG_Tcl_MakePtr(c, ptr, ty, flags) +#define SWIG_PointerTypeFromString(c) SWIG_Tcl_PointerTypeFromString(c) +#define SWIG_GetArgs SWIG_Tcl_GetArgs +#define SWIG_GetConstantObj(key) SWIG_Tcl_GetConstantObj(key) +#define SWIG_ObjectConstructor SWIG_Tcl_ObjectConstructor +#define SWIG_Thisown(ptr) SWIG_Tcl_Thisown(ptr) +#define SWIG_ObjectDelete SWIG_Tcl_ObjectDelete + + +#define SWIG_TCL_DECL_ARGS_2(arg1, arg2) (Tcl_Interp *interp SWIGUNUSED, arg1, arg2) +#define SWIG_TCL_CALL_ARGS_2(arg1, arg2) (interp, arg1, arg2) +/* ----------------------------------------------------------------------------- + * pointers/data manipulation + * ----------------------------------------------------------------------------- */ + +/* For backward compatibility only */ +#define SWIG_POINTER_EXCEPTION 0 +#define SWIG_GetConstant SWIG_GetConstantObj +#define SWIG_Tcl_GetConstant SWIG_Tcl_GetConstantObj + + +#ifdef __cplusplus +extern "C" { +#if 0 +} /* cc-mode */ +#endif +#endif + +/* Object support */ + +SWIGRUNTIME Tcl_HashTable* +SWIG_Tcl_ObjectTable(void) { + static Tcl_HashTable swigobjectTable; + static int swigobjectTableinit = 0; + if (!swigobjectTableinit) { + Tcl_InitHashTable(&swigobjectTable, TCL_ONE_WORD_KEYS); + swigobjectTableinit = 1; + } + return &swigobjectTable; +} + +/* Acquire ownership of a pointer */ +SWIGRUNTIME void +SWIG_Tcl_Acquire(void *ptr) { + int newobj; + Tcl_CreateHashEntry(SWIG_Tcl_ObjectTable(), (char *) ptr, &newobj); +} + +SWIGRUNTIME int +SWIG_Tcl_Thisown(void *ptr) { + if (Tcl_FindHashEntry(SWIG_Tcl_ObjectTable(), (char *) ptr)) { + return 1; + } + return 0; +} + +/* Disown a pointer. Returns 1 if we owned it to begin with */ +SWIGRUNTIME int +SWIG_Tcl_Disown(void *ptr) { + Tcl_HashEntry *entryPtr = Tcl_FindHashEntry(SWIG_Tcl_ObjectTable(), (char *) ptr); + if (entryPtr) { + Tcl_DeleteHashEntry(entryPtr); + return 1; + } + return 0; +} + +/* Convert a pointer value */ +SWIGRUNTIME int +SWIG_Tcl_ConvertPtrFromString(Tcl_Interp *interp, const char *c, void **ptr, swig_type_info *ty, int flags) { + swig_cast_info *tc; + /* Pointer values must start with leading underscore */ + while (*c != '_') { + *ptr = (void *) 0; + if (strcmp(c,"NULL") == 0) return SWIG_OK; + /* Hmmm. It could be an object name. */ + if (Tcl_VarEval(interp,c," cget -this", (char *) NULL) == TCL_OK) { + Tcl_Obj *result = Tcl_GetObjResult(interp); + c = Tcl_GetStringFromObj(result, NULL); + continue; + } + Tcl_ResetResult(interp); + return SWIG_ERROR; + } + c++; + c = SWIG_UnpackData(c,ptr,sizeof(void *)); + if (ty) { + tc = c ? SWIG_TypeCheck(c,ty) : 0; + if (!tc) { + return SWIG_ERROR; + } + if (flags & SWIG_POINTER_DISOWN) { + SWIG_Disown((void *) *ptr); + } + *ptr = SWIG_TypeCast(tc,(void *) *ptr); + } + return SWIG_OK; +} + +/* Convert a pointer value */ +SWIGRUNTIMEINLINE int +SWIG_Tcl_ConvertPtr(Tcl_Interp *interp, Tcl_Obj *oc, void **ptr, swig_type_info *ty, int flags) { + return SWIG_Tcl_ConvertPtrFromString(interp, Tcl_GetStringFromObj(oc,NULL), ptr, ty, flags); +} + +/* Convert a pointer value */ +SWIGRUNTIME char * +SWIG_Tcl_PointerTypeFromString(char *c) { + char d; + /* Pointer values must start with leading underscore. NULL has no type */ + if (*c != '_') { + return 0; + } + c++; + /* Extract hex value from pointer */ + while ((d = *c)) { + if (!(((d >= '0') && (d <= '9')) || ((d >= 'a') && (d <= 'f')))) break; + c++; + } + return c; +} + +/* Convert a packed value value */ +SWIGRUNTIME int +SWIG_Tcl_ConvertPacked(Tcl_Interp *SWIGUNUSEDPARM(interp) , Tcl_Obj *obj, void *ptr, int sz, swig_type_info *ty) { + swig_cast_info *tc; + const char *c; + + if (!obj) goto type_error; + c = Tcl_GetStringFromObj(obj,NULL); + /* Pointer values must start with leading underscore */ + if (*c != '_') goto type_error; + c++; + c = SWIG_UnpackData(c,ptr,sz); + if (ty) { + tc = SWIG_TypeCheck(c,ty); + if (!tc) goto type_error; + } + return SWIG_OK; + + type_error: + + return SWIG_ERROR; +} + + +/* Take a pointer and convert it to a string */ +SWIGRUNTIME void +SWIG_Tcl_MakePtr(char *c, void *ptr, swig_type_info *ty, int flags) { + if (ptr) { + *(c++) = '_'; + c = SWIG_PackData(c,&ptr,sizeof(void *)); + strcpy(c,ty->name); + } else { + strcpy(c,(char *)"NULL"); + } + flags = 0; +} + +/* Create a new pointer object */ +SWIGRUNTIMEINLINE Tcl_Obj * +SWIG_Tcl_NewPointerObj(void *ptr, swig_type_info *type, int flags) { + Tcl_Obj *robj; + char result[SWIG_BUFFER_SIZE]; + SWIG_MakePtr(result,ptr,type,flags); + robj = Tcl_NewStringObj(result,-1); + return robj; +} + +SWIGRUNTIME Tcl_Obj * +SWIG_Tcl_NewPackedObj(void *ptr, int sz, swig_type_info *type) { + char result[1024]; + char *r = result; + if ((2*sz + 1 + strlen(type->name)) > 1000) return 0; + *(r++) = '_'; + r = SWIG_PackData(r,ptr,sz); + strcpy(r,type->name); + return Tcl_NewStringObj(result,-1); +} + +/* -----------------------------------------------------------------------------* + * Get type list + * -----------------------------------------------------------------------------*/ + +SWIGRUNTIME swig_module_info * +SWIG_Tcl_GetModule(Tcl_Interp *interp) { + const char *data; + swig_module_info *ret = 0; + + /* first check if pointer already created */ + data = Tcl_GetVar(interp, (char *)"swig_runtime_data_type_pointer" SWIG_RUNTIME_VERSION SWIG_TYPE_TABLE_NAME, TCL_GLOBAL_ONLY); + if (data) { + SWIG_UnpackData(data, &ret, sizeof(swig_type_info **)); + } + + return ret; +} + +SWIGRUNTIME void +SWIG_Tcl_SetModule(Tcl_Interp *interp, swig_module_info *module) { + char buf[SWIG_BUFFER_SIZE]; + char *data; + + /* create a new pointer */ + data = SWIG_PackData(buf, &module, sizeof(swig_type_info **)); + *data = 0; + Tcl_SetVar(interp, (char *)"swig_runtime_data_type_pointer" SWIG_RUNTIME_VERSION SWIG_TYPE_TABLE_NAME, buf, 0); +} + +/* -----------------------------------------------------------------------------* + * Object auxiliars + * -----------------------------------------------------------------------------*/ + + +SWIGRUNTIME void +SWIG_Tcl_ObjectDelete(ClientData clientData) { + swig_instance *si = (swig_instance *) clientData; + if ((si) && (si->destroy) && (SWIG_Disown(si->thisvalue))) { + if (si->classptr->destructor) { + (si->classptr->destructor)(si->thisvalue); + } + } + Tcl_DecrRefCount(si->thisptr); + free(si); +} + +/* Function to invoke object methods given an instance */ +SWIGRUNTIME int +SWIG_Tcl_MethodCommand(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST _objv[]) { + char *method, *attrname; + swig_instance *inst = (swig_instance *) clientData; + swig_method *meth; + swig_attribute *attr; + Tcl_Obj *oldarg; + Tcl_Obj **objv; + int rcode; + swig_class *cls; + swig_class *cls_stack[64]; + int cls_stack_bi[64]; + int cls_stack_top = 0; + int numconf = 2; + int bi; + + objv = (Tcl_Obj **) _objv; + if (objc < 2) { + Tcl_SetResult(interp, (char *) "wrong # args.", TCL_STATIC); + return TCL_ERROR; + } + method = Tcl_GetStringFromObj(objv[1],NULL); + if (strcmp(method,"-acquire") == 0) { + inst->destroy = 1; + SWIG_Acquire(inst->thisvalue); + return TCL_OK; + } + if (strcmp(method,"-disown") == 0) { + if (inst->destroy) { + SWIG_Disown(inst->thisvalue); + } + inst->destroy = 0; + return TCL_OK; + } + if (strcmp(method,"-delete") == 0) { + Tcl_DeleteCommandFromToken(interp,inst->cmdtok); + return TCL_OK; + } + cls_stack[cls_stack_top] = inst->classptr; + cls_stack_bi[cls_stack_top] = -1; + cls = inst->classptr; + while (1) { + bi = cls_stack_bi[cls_stack_top]; + cls = cls_stack[cls_stack_top]; + if (bi != -1) { + if (!cls->bases[bi] && cls->base_names[bi]) { + /* lookup and cache the base class */ + swig_type_info *info = SWIG_TypeQueryModule(cls->module, cls->module, cls->base_names[bi]); + if (info) cls->bases[bi] = (swig_class *) info->clientdata; + } + cls = cls->bases[bi]; + if (cls) { + cls_stack_bi[cls_stack_top]++; + cls_stack_top++; + cls_stack[cls_stack_top] = cls; + cls_stack_bi[cls_stack_top] = -1; + continue; + } + } + if (!cls) { + cls_stack_top--; + if (cls_stack_top < 0) break; + else continue; + } + cls_stack_bi[cls_stack_top]++; + + meth = cls->methods; + /* Check for methods */ + while (meth && meth->name) { + if (strcmp(meth->name,method) == 0) { + oldarg = objv[1]; + objv[1] = inst->thisptr; + Tcl_IncrRefCount(inst->thisptr); + rcode = (*meth->method)(clientData,interp,objc,objv); + objv[1] = oldarg; + Tcl_DecrRefCount(inst->thisptr); + return rcode; + } + meth++; + } + /* Check class methods for a match */ + if (strcmp(method,"cget") == 0) { + if (objc < 3) { + Tcl_SetResult(interp, (char *) "wrong # args.", TCL_STATIC); + return TCL_ERROR; + } + attrname = Tcl_GetStringFromObj(objv[2],NULL); + attr = cls->attributes; + while (attr && attr->name) { + if ((strcmp(attr->name, attrname) == 0) && (attr->getmethod)) { + oldarg = objv[1]; + objv[1] = inst->thisptr; + Tcl_IncrRefCount(inst->thisptr); + rcode = (*attr->getmethod)(clientData,interp,2, objv); + objv[1] = oldarg; + Tcl_DecrRefCount(inst->thisptr); + return rcode; + } + attr++; + } + if (strcmp(attrname, "-this") == 0) { + Tcl_SetObjResult(interp, Tcl_DuplicateObj(inst->thisptr)); + return TCL_OK; + } + if (strcmp(attrname, "-thisown") == 0) { + if (SWIG_Thisown(inst->thisvalue)) { + Tcl_SetResult(interp,(char*)"1",TCL_STATIC); + } else { + Tcl_SetResult(interp,(char*)"0",TCL_STATIC); + } + return TCL_OK; + } + } else if (strcmp(method, "configure") == 0) { + int i; + if (objc < 4) { + Tcl_SetResult(interp, (char *) "wrong # args.", TCL_STATIC); + return TCL_ERROR; + } + i = 2; + while (i < objc) { + attrname = Tcl_GetStringFromObj(objv[i],NULL); + attr = cls->attributes; + while (attr && attr->name) { + if ((strcmp(attr->name, attrname) == 0) && (attr->setmethod)) { + oldarg = objv[i]; + objv[i] = inst->thisptr; + Tcl_IncrRefCount(inst->thisptr); + rcode = (*attr->setmethod)(clientData,interp,3, &objv[i-1]); + objv[i] = oldarg; + Tcl_DecrRefCount(inst->thisptr); + if (rcode != TCL_OK) return rcode; + numconf += 2; + } + attr++; + } + i+=2; + } + } + } + if (strcmp(method,"configure") == 0) { + if (numconf >= objc) { + return TCL_OK; + } else { + Tcl_SetResult(interp,(char *) "Invalid attribute name.", TCL_STATIC); + return TCL_ERROR; + } + } + if (strcmp(method,"cget") == 0) { + Tcl_SetResult(interp,(char *) "Invalid attribute name.", TCL_STATIC); + return TCL_ERROR; + } + Tcl_SetResult(interp, (char *) "Invalid method. Must be one of: configure cget -acquire -disown -delete", TCL_STATIC); + cls = inst->classptr; + bi = 0; + while (cls) { + meth = cls->methods; + while (meth && meth->name) { + char *cr = (char *) Tcl_GetStringResult(interp); + int meth_len = strlen(meth->name); + char* where = strchr(cr,':'); + while(where) { + where = strstr(where, meth->name); + if(where) { + if(where[-1] == ' ' && (where[meth_len] == ' ' || where[meth_len]==0)) { + break; + } else { + where++; + } + } + } + + if (!where) + Tcl_AppendElement(interp, (char *) meth->name); + meth++; + } + cls = inst->classptr->bases[bi++]; + } + return TCL_ERROR; +} + +/* This function takes the current result and turns it into an object command */ +SWIGRUNTIME Tcl_Obj * +SWIG_Tcl_NewInstanceObj(Tcl_Interp *interp, void *thisvalue, swig_type_info *type, int flags) { + Tcl_Obj *robj = SWIG_NewPointerObj(thisvalue, type,0); + /* Check to see if this pointer belongs to a class or not */ + if ((type->clientdata) && (interp)) { + Tcl_CmdInfo ci; + char *name; + name = Tcl_GetStringFromObj(robj,NULL); + if (!Tcl_GetCommandInfo(interp,name, &ci) || (flags)) { + swig_instance *newinst = (swig_instance *) malloc(sizeof(swig_instance)); + newinst->thisptr = Tcl_DuplicateObj(robj); + Tcl_IncrRefCount(newinst->thisptr); + newinst->thisvalue = thisvalue; + newinst->classptr = (swig_class *) type->clientdata; + newinst->destroy = flags; + newinst->cmdtok = Tcl_CreateObjCommand(interp, Tcl_GetStringFromObj(robj,NULL), (swig_wrapper_func) SWIG_MethodCommand, (ClientData) newinst, (swig_delete_func) SWIG_ObjectDelete); + if (flags) { + SWIG_Acquire(thisvalue); + } + } + } + return robj; +} + +/* Function to create objects */ +SWIGRUNTIME int +SWIG_Tcl_ObjectConstructor(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + Tcl_Obj *newObj = 0; + void *thisvalue = 0; + swig_instance *newinst = 0; + swig_class *classptr = (swig_class *) clientData; + swig_wrapper cons = 0; + char *name = 0; + int firstarg = 0; + int thisarg = 0; + int destroy = 1; + + if (!classptr) { + Tcl_SetResult(interp, (char *) "swig: internal runtime error. No class object defined.", TCL_STATIC); + return TCL_ERROR; + } + cons = classptr->constructor; + if (objc > 1) { + char *s = Tcl_GetStringFromObj(objv[1],NULL); + if (strcmp(s,"-this") == 0) { + thisarg = 2; + cons = 0; + } else if (strcmp(s,"-args") == 0) { + firstarg = 1; + } else if (objc == 2) { + firstarg = 1; + name = s; + } else if (objc >= 3) { + char *s1; + name = s; + s1 = Tcl_GetStringFromObj(objv[2],NULL); + if (strcmp(s1,"-this") == 0) { + thisarg = 3; + cons = 0; + } else { + firstarg = 1; + } + } + } + if (cons) { + int result; + result = (*cons)(0, interp, objc-firstarg, &objv[firstarg]); + if (result != TCL_OK) { + return result; + } + newObj = Tcl_DuplicateObj(Tcl_GetObjResult(interp)); + if (!name) name = Tcl_GetStringFromObj(newObj,NULL); + } else if (thisarg > 0) { + if (thisarg < objc) { + destroy = 0; + newObj = Tcl_DuplicateObj(objv[thisarg]); + if (!name) name = Tcl_GetStringFromObj(newObj,NULL); + } else { + Tcl_SetResult(interp, (char *) "wrong # args.", TCL_STATIC); + return TCL_ERROR; + } + } else { + Tcl_SetResult(interp, (char *) "No constructor available.", TCL_STATIC); + return TCL_ERROR; + } + if (SWIG_Tcl_ConvertPtr(interp,newObj, (void **) &thisvalue, *(classptr->type), 0) != SWIG_OK) { + Tcl_DecrRefCount(newObj); + return TCL_ERROR; + } + newinst = (swig_instance *) malloc(sizeof(swig_instance)); + newinst->thisptr = newObj; + Tcl_IncrRefCount(newObj); + newinst->thisvalue = thisvalue; + newinst->classptr = classptr; + newinst->destroy = destroy; + if (destroy) { + SWIG_Acquire(thisvalue); + } + newinst->cmdtok = Tcl_CreateObjCommand(interp,name, (swig_wrapper) SWIG_MethodCommand, (ClientData) newinst, (swig_delete_func) SWIG_ObjectDelete); + return TCL_OK; +} + +/* -----------------------------------------------------------------------------* + * Get arguments + * -----------------------------------------------------------------------------*/ +SWIGRUNTIME int +SWIG_Tcl_GetArgs(Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], const char *fmt, ...) { + int argno = 0, opt = 0; + long tempi; + double tempd; + const char *c; + va_list ap; + void *vptr; + Tcl_Obj *obj = 0; + swig_type_info *ty; + + va_start(ap,fmt); + for (c = fmt; (*c && (*c != ':') && (*c != ';')); c++,argno++) { + if (*c == '|') { + opt = 1; + c++; + } + if (argno >= (objc-1)) { + if (!opt) { + Tcl_SetResult(interp, (char *) "Wrong number of arguments ", TCL_STATIC); + goto argerror; + } else { + va_end(ap); + return TCL_OK; + } + } + + vptr = va_arg(ap,void *); + if (vptr) { + if (isupper(*c)) { + obj = SWIG_Tcl_GetConstantObj(Tcl_GetStringFromObj(objv[argno+1],0)); + if (!obj) obj = objv[argno+1]; + } else { + obj = objv[argno+1]; + } + switch(*c) { + case 'i': case 'I': + case 'l': case 'L': + case 'h': case 'H': + case 'b': case 'B': + if (Tcl_GetLongFromObj(interp,obj,&tempi) != TCL_OK) goto argerror; + if ((*c == 'i') || (*c == 'I')) *((int *)vptr) = (int)tempi; + else if ((*c == 'l') || (*c == 'L')) *((long *)vptr) = (long)tempi; + else if ((*c == 'h') || (*c == 'H')) *((short*)vptr) = (short)tempi; + else if ((*c == 'b') || (*c == 'B')) *((unsigned char *)vptr) = (unsigned char)tempi; + break; + case 'f': case 'F': + case 'd': case 'D': + if (Tcl_GetDoubleFromObj(interp,obj,&tempd) != TCL_OK) goto argerror; + if ((*c == 'f') || (*c == 'F')) *((float *) vptr) = (float)tempd; + else if ((*c == 'd') || (*c == 'D')) *((double*) vptr) = tempd; + break; + case 's': case 'S': + if (*(c+1) == '#') { + int *vlptr = (int *) va_arg(ap, void *); + *((char **) vptr) = Tcl_GetStringFromObj(obj, vlptr); + c++; + } else { + *((char **)vptr) = Tcl_GetStringFromObj(obj,NULL); + } + break; + case 'c': case 'C': + *((char *)vptr) = *(Tcl_GetStringFromObj(obj,NULL)); + break; + case 'p': case 'P': + ty = (swig_type_info *) va_arg(ap, void *); + if (SWIG_Tcl_ConvertPtr(interp, obj, (void **) vptr, ty, 0) != SWIG_OK) goto argerror; + break; + case 'o': case 'O': + *((Tcl_Obj **)vptr) = objv[argno+1]; + break; + default: + break; + } + } + } + + if ((*c != ';') && ((objc-1) > argno)) { + Tcl_SetResult(interp, (char *) "Wrong # args.", TCL_STATIC); + goto argerror; + } + va_end(ap); + return TCL_OK; + + argerror: + { + char temp[32]; + sprintf(temp,"%d", argno+1); + c = strchr(fmt,':'); + if (!c) c = strchr(fmt,';'); + if (!c) c = (char *)""; + Tcl_AppendResult(interp,c," argument ", temp, NULL); + va_end(ap); + return TCL_ERROR; + } +} + +#ifdef __cplusplus +#if 0 +{ /* cc-mode */ +#endif +} +#endif + + + +#define SWIG_exception_fail(code, msg) do { SWIG_Error(code, msg); SWIG_fail; } while(0) + +#define SWIG_contract_assert(expr, msg) if (!(expr)) { SWIG_Error(SWIG_RuntimeError, msg); SWIG_fail; } else + + + +/* -------- TYPES TABLE (BEGIN) -------- */ + +#define SWIGTYPE_p_char swig_types[0] +#define SWIGTYPE_p_void swig_types[1] +static swig_type_info *swig_types[3]; +static swig_module_info swig_module = {swig_types, 2, 0, 0, 0, 0}; +#define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name) +#define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name) + +/* -------- TYPES TABLE (END) -------- */ + +#define SWIG_init Nxinter_Init +#define SWIG_name "nxinter" +#define SWIG_prefix "" +#define SWIG_version "0.0" + +#define SWIGVERSION 0x010331 +#define SWIG_VERSION SWIGVERSION + + +#define SWIG_as_voidptr(a) (void *)((const void *)(a)) +#define SWIG_as_voidptrptr(a) ((void)SWIG_as_voidptr(*a),(void**)(a)) + + + +#ifdef __cplusplus +extern "C" { +#endif +#ifdef MAC_TCL +#pragma export on +#endif +SWIGEXPORT int SWIG_init(Tcl_Interp *); +#ifdef MAC_TCL +#pragma export off +#endif +#ifdef __cplusplus +} +#endif + + + +#include "nxinterhelper.h" + + + +#include +#ifndef LLONG_MIN +# define LLONG_MIN LONG_LONG_MIN +#endif +#ifndef LLONG_MAX +# define LLONG_MAX LONG_LONG_MAX +#endif +#ifndef ULLONG_MAX +# define ULLONG_MAX ULONG_LONG_MAX +#endif + + +SWIGINTERNINLINE Tcl_Obj* +SWIG_From_long (long value) +{ + if (((long) INT_MIN <= value) && (value <= (long) INT_MAX)) { + return Tcl_NewIntObj((int)(value)); + } else { + return Tcl_NewLongObj(value); + } +} + + +SWIGINTERNINLINE Tcl_Obj * +SWIG_From_int (int value) +{ + return SWIG_From_long (value); +} + + +#include "nxdataset.h" + +#define MAXDIM 7 + +void *create_nxds(int rank, int type, int dim0, int dim1, int dim2, + int dim3, int dim4, int dim5,int dim6){ + int dim[MAXDIM],i; + + dim[0] = dim0; + dim[1] = dim1; + dim[2] = dim2; + dim[3] = dim3; + dim[4] = dim4; + dim[5] = dim5; + dim[6] = dim6; + + return createNXDataset(rank,type,dim); +} +void *create_text_nxds(char *name){ + return (void *)createTextNXDataset(name); +} + +void drop_nxds(void *ptr){ + dropNXDataset( (pNXDS) ptr); +} + +int get_nxds_rank(void *ptr){ + return getNXDatasetRank((pNXDS) ptr); +} + +int get_nxds_type(void *ptr){ + return getNXDatasetType((pNXDS) ptr); +} + +int get_nxds_dim(void *ptr, int which){ + return getNXDatasetDim((pNXDS) ptr, which); +} + +double get_nxds_value(void *ptr,int dim0, int dim1, int dim2, + int dim3, int dim4, int dim5,int dim6){ + int dim[MAXDIM]; + + dim[0] = dim0; + dim[1] = dim1; + dim[2] = dim2; + dim[3] = dim3; + dim[4] = dim4; + dim[5] = dim5; + dim[6] = dim6; + + return getNXDatasetValue((pNXDS)ptr,dim); +} + +char *get_nxds_text(void *ptr){ + return getNXDatasetText((pNXDS) ptr); +} + +int put_nxds_value(void *ptr, double value, int dim0, int dim1, int dim2, + int dim3, int dim4, int dim5,int dim6){ + int dim[MAXDIM]; + + dim[0] = dim0; + dim[1] = dim1; + dim[2] = dim2; + dim[3] = dim3; + dim[4] = dim4; + dim[5] = dim5; + dim[6] = dim6; + + return putNXDatasetValue((pNXDS)ptr,dim,value); +} + + + + +SWIGINTERN int +SWIG_AsVal_long SWIG_TCL_DECL_ARGS_2(Tcl_Obj *obj, long* val) +{ + long v; + if (Tcl_GetLongFromObj(0,obj, &v) == TCL_OK) { + if (val) *val = (long) v; + return SWIG_OK; + } + return SWIG_TypeError; +} + + +SWIGINTERN int +SWIG_AsVal_int SWIG_TCL_DECL_ARGS_2(Tcl_Obj * obj, int *val) +{ + long v; + int res = SWIG_AsVal_long SWIG_TCL_CALL_ARGS_2(obj, &v); + if (SWIG_IsOK(res)) { + if ((v < INT_MIN || v > INT_MAX)) { + return SWIG_OverflowError; + } else { + if (val) *val = (int)(v); + } + } + return res; +} + + +SWIGINTERN int +SWIG_AsCharPtrAndSize(Tcl_Obj *obj, char** cptr, size_t* psize, int *alloc) +{ + int len = 0; + char *cstr = Tcl_GetStringFromObj(obj, &len); + if (cstr) { + if (cptr) *cptr = cstr; + if (psize) *psize = len + 1; + if (alloc) *alloc = SWIG_OLDOBJ; + return SWIG_OK; + } + return SWIG_TypeError; +} + + + + + + #define SWIG_From_double Tcl_NewDoubleObj + + +SWIGINTERNINLINE Tcl_Obj * +SWIG_FromCharPtrAndSize(const char* carray, size_t size) +{ + return (size < INT_MAX) ? Tcl_NewStringObj(carray, (int)(size)) : NULL; +} + + +SWIGINTERNINLINE Tcl_Obj * +SWIG_FromCharPtr(const char *cptr) +{ + return SWIG_FromCharPtrAndSize(cptr, (cptr ? strlen(cptr) : 0)); +} + + +SWIGINTERN int +SWIG_AsVal_double SWIG_TCL_DECL_ARGS_2(Tcl_Obj *obj, double *val) +{ + double v; + if (Tcl_GetDoubleFromObj(0, obj, &v) == TCL_OK) { + if (val) *val = v; + return SWIG_OK; + } + return SWIG_TypeError; +} + + +SWIGINTERN int +SWIG_AsCharArray(Tcl_Obj * obj, char *val, size_t size) +{ + char* cptr = 0; size_t csize = 0; int alloc = SWIG_OLDOBJ; + int res = SWIG_AsCharPtrAndSize(obj, &cptr, &csize, &alloc); + if (SWIG_IsOK(res)) { + if ((csize == size + 1) && cptr && !(cptr[csize-1])) --csize; + if (csize <= size) { + if (val) { + if (csize) memcpy(val, cptr, csize*sizeof(char)); + if (csize < size) memset(val + csize, 0, (size - csize)*sizeof(char)); + } + if (alloc == SWIG_NEWOBJ) { + free((char*)cptr); + res = SWIG_DelNewMask(res); + } + return res; + } + if (alloc == SWIG_NEWOBJ) free((char*)cptr); + } + return SWIG_TypeError; +} + + +SWIGINTERN int +SWIG_AsVal_char SWIG_TCL_DECL_ARGS_2(Tcl_Obj * obj, char *val) +{ + int res = SWIG_AsCharArray(obj, val, 1); + if (!SWIG_IsOK(res)) { + long v; + res = SWIG_AddCast(SWIG_AsVal_long SWIG_TCL_CALL_ARGS_2(obj, &v)); + if (SWIG_IsOK(res)) { + if ((CHAR_MIN <= v) && (v <= CHAR_MAX)) { + if (val) *val = (char)(v); + } else { + res = SWIG_OverflowError; + } + } + } + return res; +} + + + +/* A TCL_AppInit() function that lets you build a new copy + * of tclsh. + * + * The macro SWIG_init contains the name of the initialization + * function in the wrapper file. + */ + +#ifndef SWIG_RcFileName +char *SWIG_RcFileName = "~/.myapprc"; +#endif + + +#ifdef MAC_TCL +extern int MacintoshInit _ANSI_ARGS_((void)); +#endif + +int Tcl_AppInit(Tcl_Interp *interp){ + + if (Tcl_Init(interp) == TCL_ERROR) + return TCL_ERROR; + + /* Now initialize our functions */ + + if (SWIG_init(interp) == TCL_ERROR) + return TCL_ERROR; +#if TCL_MAJOR_VERSION > 7 || TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION >= 5 + Tcl_SetVar(interp, (char *) "tcl_rcFileName",SWIG_RcFileName,TCL_GLOBAL_ONLY); +#else + tcl_RcFileName = SWIG_RcFileName; +#endif +#ifdef SWIG_RcRsrcName + Tcl_SetVar(interp, (char *) "tcl_rcRsrcName",SWIG_RcRsrcName,TCL_GLOBAL); +#endif + + return TCL_OK; +} + + +#ifdef __cplusplus +extern "C" { +#endif +SWIGINTERN int +_wrap_create_nxds(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + int arg1 ; + int arg2 ; + int arg3 = (int) 0 ; + int arg4 = (int) 0 ; + int arg5 = (int) 0 ; + int arg6 = (int) 0 ; + int arg7 = (int) 0 ; + int arg8 = (int) 0 ; + int arg9 = (int) 0 ; + void *result = 0 ; + int val1 ; + int ecode1 = 0 ; + int val2 ; + int ecode2 = 0 ; + int val3 ; + int ecode3 = 0 ; + int val4 ; + int ecode4 = 0 ; + int val5 ; + int ecode5 = 0 ; + int val6 ; + int ecode6 = 0 ; + int val7 ; + int ecode7 = 0 ; + int val8 ; + int ecode8 = 0 ; + int val9 ; + int ecode9 = 0 ; + + if (SWIG_GetArgs(interp, objc, objv,"oo|ooooooo:create_nxds rank type ?dim0? ?dim1? ?dim2? ?dim3? ?dim4? ?dim5? ?dim6? ",(void *)0,(void *)0,(void *)0,(void *)0,(void *)0,(void *)0,(void *)0,(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + ecode1 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[1], &val1); + if (!SWIG_IsOK(ecode1)) { + SWIG_exception_fail(SWIG_ArgError(ecode1), "in method '" "create_nxds" "', argument " "1"" of type '" "int""'"); + } + arg1 = (int)(val1); + ecode2 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[2], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "create_nxds" "', argument " "2"" of type '" "int""'"); + } + arg2 = (int)(val2); + if (objc > 3) { + ecode3 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[3], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "create_nxds" "', argument " "3"" of type '" "int""'"); + } + arg3 = (int)(val3); + } + if (objc > 4) { + ecode4 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[4], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "create_nxds" "', argument " "4"" of type '" "int""'"); + } + arg4 = (int)(val4); + } + if (objc > 5) { + ecode5 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[5], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "create_nxds" "', argument " "5"" of type '" "int""'"); + } + arg5 = (int)(val5); + } + if (objc > 6) { + ecode6 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[6], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "create_nxds" "', argument " "6"" of type '" "int""'"); + } + arg6 = (int)(val6); + } + if (objc > 7) { + ecode7 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[7], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "create_nxds" "', argument " "7"" of type '" "int""'"); + } + arg7 = (int)(val7); + } + if (objc > 8) { + ecode8 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[8], &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "create_nxds" "', argument " "8"" of type '" "int""'"); + } + arg8 = (int)(val8); + } + if (objc > 9) { + ecode9 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[9], &val9); + if (!SWIG_IsOK(ecode9)) { + SWIG_exception_fail(SWIG_ArgError(ecode9), "in method '" "create_nxds" "', argument " "9"" of type '" "int""'"); + } + arg9 = (int)(val9); + } + result = (void *)create_nxds(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9); + Tcl_SetObjResult(interp, SWIG_NewInstanceObj( SWIG_as_voidptr(result), SWIGTYPE_p_void,0)); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_create_text_nxds(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + char *arg1 = (char *) 0 ; + void *result = 0 ; + int res1 ; + char *buf1 = 0 ; + int alloc1 = 0 ; + + if (SWIG_GetArgs(interp, objc, objv,"o:create_text_nxds name ",(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_AsCharPtrAndSize(objv[1], &buf1, NULL, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "create_text_nxds" "', argument " "1"" of type '" "char *""'"); + } + arg1 = (char *)(buf1); + result = (void *)create_text_nxds(arg1); + Tcl_SetObjResult(interp, SWIG_NewInstanceObj( SWIG_as_voidptr(result), SWIGTYPE_p_void,0)); + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + return TCL_OK; +fail: + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_drop_nxds(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + int res1 ; + + if (SWIG_GetArgs(interp, objc, objv,"o:drop_nxds ptr ",(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "drop_nxds" "', argument " "1"" of type '" "void *""'"); + } + drop_nxds(arg1); + + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_get_nxds_rank(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + int result; + int res1 ; + + if (SWIG_GetArgs(interp, objc, objv,"o:get_nxds_rank ptr ",(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "get_nxds_rank" "', argument " "1"" of type '" "void *""'"); + } + result = (int)get_nxds_rank(arg1); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_get_nxds_type(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + int result; + int res1 ; + + if (SWIG_GetArgs(interp, objc, objv,"o:get_nxds_type ptr ",(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "get_nxds_type" "', argument " "1"" of type '" "void *""'"); + } + result = (int)get_nxds_type(arg1); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_get_nxds_dim(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + int arg2 ; + int result; + int res1 ; + int val2 ; + int ecode2 = 0 ; + + if (SWIG_GetArgs(interp, objc, objv,"oo:get_nxds_dim ptr which ",(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "get_nxds_dim" "', argument " "1"" of type '" "void *""'"); + } + ecode2 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[2], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "get_nxds_dim" "', argument " "2"" of type '" "int""'"); + } + arg2 = (int)(val2); + result = (int)get_nxds_dim(arg1,arg2); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_get_nxds_value(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + int arg2 = (int) 0 ; + int arg3 = (int) 0 ; + int arg4 = (int) 0 ; + int arg5 = (int) 0 ; + int arg6 = (int) 0 ; + int arg7 = (int) 0 ; + int arg8 = (int) 0 ; + double result; + int res1 ; + int val2 ; + int ecode2 = 0 ; + int val3 ; + int ecode3 = 0 ; + int val4 ; + int ecode4 = 0 ; + int val5 ; + int ecode5 = 0 ; + int val6 ; + int ecode6 = 0 ; + int val7 ; + int ecode7 = 0 ; + int val8 ; + int ecode8 = 0 ; + + if (SWIG_GetArgs(interp, objc, objv,"o|ooooooo:get_nxds_value ptr ?dim0? ?dim1? ?dim2? ?dim3? ?dim4? ?dim5? ?dim6? ",(void *)0,(void *)0,(void *)0,(void *)0,(void *)0,(void *)0,(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "get_nxds_value" "', argument " "1"" of type '" "void *""'"); + } + if (objc > 2) { + ecode2 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[2], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "get_nxds_value" "', argument " "2"" of type '" "int""'"); + } + arg2 = (int)(val2); + } + if (objc > 3) { + ecode3 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[3], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "get_nxds_value" "', argument " "3"" of type '" "int""'"); + } + arg3 = (int)(val3); + } + if (objc > 4) { + ecode4 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[4], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "get_nxds_value" "', argument " "4"" of type '" "int""'"); + } + arg4 = (int)(val4); + } + if (objc > 5) { + ecode5 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[5], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "get_nxds_value" "', argument " "5"" of type '" "int""'"); + } + arg5 = (int)(val5); + } + if (objc > 6) { + ecode6 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[6], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "get_nxds_value" "', argument " "6"" of type '" "int""'"); + } + arg6 = (int)(val6); + } + if (objc > 7) { + ecode7 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[7], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "get_nxds_value" "', argument " "7"" of type '" "int""'"); + } + arg7 = (int)(val7); + } + if (objc > 8) { + ecode8 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[8], &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "get_nxds_value" "', argument " "8"" of type '" "int""'"); + } + arg8 = (int)(val8); + } + result = (double)get_nxds_value(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8); + Tcl_SetObjResult(interp,SWIG_From_double((double)(result))); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_get_nxds_text(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + char *result = 0 ; + int res1 ; + + if (SWIG_GetArgs(interp, objc, objv,"o:get_nxds_text ptr ",(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "get_nxds_text" "', argument " "1"" of type '" "void *""'"); + } + result = (char *)get_nxds_text(arg1); + Tcl_SetObjResult(interp,SWIG_FromCharPtr((const char *)result)); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_put_nxds_value(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + double arg2 ; + int arg3 = (int) 0 ; + int arg4 = (int) 0 ; + int arg5 = (int) 0 ; + int arg6 = (int) 0 ; + int arg7 = (int) 0 ; + int arg8 = (int) 0 ; + int arg9 = (int) 0 ; + int result; + int res1 ; + double val2 ; + int ecode2 = 0 ; + int val3 ; + int ecode3 = 0 ; + int val4 ; + int ecode4 = 0 ; + int val5 ; + int ecode5 = 0 ; + int val6 ; + int ecode6 = 0 ; + int val7 ; + int ecode7 = 0 ; + int val8 ; + int ecode8 = 0 ; + int val9 ; + int ecode9 = 0 ; + + if (SWIG_GetArgs(interp, objc, objv,"oo|ooooooo:put_nxds_value ptr value ?dim0? ?dim1? ?dim2? ?dim3? ?dim4? ?dim5? ?dim6? ",(void *)0,(void *)0,(void *)0,(void *)0,(void *)0,(void *)0,(void *)0,(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "put_nxds_value" "', argument " "1"" of type '" "void *""'"); + } + ecode2 = SWIG_AsVal_double SWIG_TCL_CALL_ARGS_2(objv[2], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "put_nxds_value" "', argument " "2"" of type '" "double""'"); + } + arg2 = (double)(val2); + if (objc > 3) { + ecode3 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[3], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "put_nxds_value" "', argument " "3"" of type '" "int""'"); + } + arg3 = (int)(val3); + } + if (objc > 4) { + ecode4 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[4], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "put_nxds_value" "', argument " "4"" of type '" "int""'"); + } + arg4 = (int)(val4); + } + if (objc > 5) { + ecode5 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[5], &val5); + if (!SWIG_IsOK(ecode5)) { + SWIG_exception_fail(SWIG_ArgError(ecode5), "in method '" "put_nxds_value" "', argument " "5"" of type '" "int""'"); + } + arg5 = (int)(val5); + } + if (objc > 6) { + ecode6 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[6], &val6); + if (!SWIG_IsOK(ecode6)) { + SWIG_exception_fail(SWIG_ArgError(ecode6), "in method '" "put_nxds_value" "', argument " "6"" of type '" "int""'"); + } + arg6 = (int)(val6); + } + if (objc > 7) { + ecode7 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[7], &val7); + if (!SWIG_IsOK(ecode7)) { + SWIG_exception_fail(SWIG_ArgError(ecode7), "in method '" "put_nxds_value" "', argument " "7"" of type '" "int""'"); + } + arg7 = (int)(val7); + } + if (objc > 8) { + ecode8 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[8], &val8); + if (!SWIG_IsOK(ecode8)) { + SWIG_exception_fail(SWIG_ArgError(ecode8), "in method '" "put_nxds_value" "', argument " "8"" of type '" "int""'"); + } + arg8 = (int)(val8); + } + if (objc > 9) { + ecode9 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[9], &val9); + if (!SWIG_IsOK(ecode9)) { + SWIG_exception_fail(SWIG_ArgError(ecode9), "in method '" "put_nxds_value" "', argument " "9"" of type '" "int""'"); + } + arg9 = (int)(val9); + } + result = (int)put_nxds_value(arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_getlasterror(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + char *result = 0 ; + + if (SWIG_GetArgs(interp, objc, objv,":nx_getlasterror ") == TCL_ERROR) SWIG_fail; + result = (char *)nx_getlasterror(); + Tcl_SetObjResult(interp,SWIG_FromCharPtr((const char *)result)); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_open(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + char *arg1 = (char *) 0 ; + int arg2 ; + void *result = 0 ; + int res1 ; + char *buf1 = 0 ; + int alloc1 = 0 ; + int val2 ; + int ecode2 = 0 ; + + if (SWIG_GetArgs(interp, objc, objv,"oo:nx_open filename accessCode ",(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_AsCharPtrAndSize(objv[1], &buf1, NULL, &alloc1); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_open" "', argument " "1"" of type '" "char *""'"); + } + arg1 = (char *)(buf1); + ecode2 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[2], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "nx_open" "', argument " "2"" of type '" "int""'"); + } + arg2 = (int)(val2); + result = (void *)nx_open(arg1,arg2); + Tcl_SetObjResult(interp, SWIG_NewInstanceObj( SWIG_as_voidptr(result), SWIGTYPE_p_void,0)); + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + return TCL_OK; +fail: + if (alloc1 == SWIG_NEWOBJ) free((char*)buf1); + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_flush(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + void *result = 0 ; + int res1 ; + + if (SWIG_GetArgs(interp, objc, objv,"o:nx_flush handle ",(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_flush" "', argument " "1"" of type '" "void *""'"); + } + result = (void *)nx_flush(arg1); + Tcl_SetObjResult(interp, SWIG_NewInstanceObj( SWIG_as_voidptr(result), SWIGTYPE_p_void,0)); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_close(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + int res1 ; + + if (SWIG_GetArgs(interp, objc, objv,"o:nx_close handle ",(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_close" "', argument " "1"" of type '" "void *""'"); + } + nx_close(arg1); + + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_makegroup(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + char *arg2 = (char *) 0 ; + char *arg3 = (char *) 0 ; + int result; + int res1 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + int res3 ; + char *buf3 = 0 ; + int alloc3 = 0 ; + + if (SWIG_GetArgs(interp, objc, objv,"ooo:nx_makegroup handle name nxclass ",(void *)0,(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_makegroup" "', argument " "1"" of type '" "void *""'"); + } + res2 = SWIG_AsCharPtrAndSize(objv[2], &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "nx_makegroup" "', argument " "2"" of type '" "char *""'"); + } + arg2 = (char *)(buf2); + res3 = SWIG_AsCharPtrAndSize(objv[3], &buf3, NULL, &alloc3); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "nx_makegroup" "', argument " "3"" of type '" "char *""'"); + } + arg3 = (char *)(buf3); + result = (int)nx_makegroup(arg1,arg2,arg3); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + if (alloc3 == SWIG_NEWOBJ) free((char*)buf3); + return TCL_OK; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + if (alloc3 == SWIG_NEWOBJ) free((char*)buf3); + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_opengroup(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + char *arg2 = (char *) 0 ; + char *arg3 = (char *) 0 ; + int result; + int res1 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + int res3 ; + char *buf3 = 0 ; + int alloc3 = 0 ; + + if (SWIG_GetArgs(interp, objc, objv,"ooo:nx_opengroup handle name nxclass ",(void *)0,(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_opengroup" "', argument " "1"" of type '" "void *""'"); + } + res2 = SWIG_AsCharPtrAndSize(objv[2], &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "nx_opengroup" "', argument " "2"" of type '" "char *""'"); + } + arg2 = (char *)(buf2); + res3 = SWIG_AsCharPtrAndSize(objv[3], &buf3, NULL, &alloc3); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "nx_opengroup" "', argument " "3"" of type '" "char *""'"); + } + arg3 = (char *)(buf3); + result = (int)nx_opengroup(arg1,arg2,arg3); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + if (alloc3 == SWIG_NEWOBJ) free((char*)buf3); + return TCL_OK; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + if (alloc3 == SWIG_NEWOBJ) free((char*)buf3); + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_openpath(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + char *arg2 = (char *) 0 ; + int result; + int res1 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + + if (SWIG_GetArgs(interp, objc, objv,"oo:nx_openpath handle path ",(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_openpath" "', argument " "1"" of type '" "void *""'"); + } + res2 = SWIG_AsCharPtrAndSize(objv[2], &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "nx_openpath" "', argument " "2"" of type '" "char *""'"); + } + arg2 = (char *)(buf2); + result = (int)nx_openpath(arg1,arg2); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_OK; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_opengrouppath(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + char *arg2 = (char *) 0 ; + int result; + int res1 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + + if (SWIG_GetArgs(interp, objc, objv,"oo:nx_opengrouppath handle path ",(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_opengrouppath" "', argument " "1"" of type '" "void *""'"); + } + res2 = SWIG_AsCharPtrAndSize(objv[2], &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "nx_opengrouppath" "', argument " "2"" of type '" "char *""'"); + } + arg2 = (char *)(buf2); + result = (int)nx_opengrouppath(arg1,arg2); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_OK; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_closegroup(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + int result; + int res1 ; + + if (SWIG_GetArgs(interp, objc, objv,"o:nx_closegroup handle ",(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_closegroup" "', argument " "1"" of type '" "void *""'"); + } + result = (int)nx_closegroup(arg1); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_getnextentry(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + char arg2 ; + char *result = 0 ; + int res1 ; + char val2 ; + int ecode2 = 0 ; + + if (SWIG_GetArgs(interp, objc, objv,"oo:nx_getnextentry handle separator ",(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_getnextentry" "', argument " "1"" of type '" "void *""'"); + } + ecode2 = SWIG_AsVal_char SWIG_TCL_CALL_ARGS_2(objv[2], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "nx_getnextentry" "', argument " "2"" of type '" "char""'"); + } + arg2 = (char)(val2); + result = (char *)nx_getnextentry(arg1,arg2); + Tcl_SetObjResult(interp,SWIG_FromCharPtr((const char *)result)); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_getgroupID(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + void *result = 0 ; + int res1 ; + + if (SWIG_GetArgs(interp, objc, objv,"o:nx_getgroupID handle ",(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_getgroupID" "', argument " "1"" of type '" "void *""'"); + } + result = (void *)nx_getgroupID(arg1); + Tcl_SetObjResult(interp, SWIG_NewInstanceObj( SWIG_as_voidptr(result), SWIGTYPE_p_void,0)); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_initgroupdir(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + int result; + int res1 ; + + if (SWIG_GetArgs(interp, objc, objv,"o:nx_initgroupdir handle ",(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_initgroupdir" "', argument " "1"" of type '" "void *""'"); + } + result = (int)nx_initgroupdir(arg1); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_makedata(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + char *arg2 = (char *) 0 ; + int arg3 ; + int arg4 ; + void *arg5 = (void *) 0 ; + int result; + int res1 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + int val3 ; + int ecode3 = 0 ; + int val4 ; + int ecode4 = 0 ; + int res5 ; + + if (SWIG_GetArgs(interp, objc, objv,"ooooo:nx_makedata handle name rank type dimPtr ",(void *)0,(void *)0,(void *)0,(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_makedata" "', argument " "1"" of type '" "void *""'"); + } + res2 = SWIG_AsCharPtrAndSize(objv[2], &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "nx_makedata" "', argument " "2"" of type '" "char *""'"); + } + arg2 = (char *)(buf2); + ecode3 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[3], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "nx_makedata" "', argument " "3"" of type '" "int""'"); + } + arg3 = (int)(val3); + ecode4 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[4], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "nx_makedata" "', argument " "4"" of type '" "int""'"); + } + arg4 = (int)(val4); + res5 = SWIG_ConvertPtr(objv[5],SWIG_as_voidptrptr(&arg5), 0, 0); + if (!SWIG_IsOK(res5)) { + SWIG_exception_fail(SWIG_ArgError(res5), "in method '" "nx_makedata" "', argument " "5"" of type '" "void *""'"); + } + result = (int)nx_makedata(arg1,arg2,arg3,arg4,arg5); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_OK; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_compmakedata(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + char *arg2 = (char *) 0 ; + int arg3 ; + int arg4 ; + void *arg5 = (void *) 0 ; + void *arg6 = (void *) 0 ; + int result; + int res1 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + int val3 ; + int ecode3 = 0 ; + int val4 ; + int ecode4 = 0 ; + int res5 ; + int res6 ; + + if (SWIG_GetArgs(interp, objc, objv,"oooooo:nx_compmakedata handle name rank type dimPtr bufPtr ",(void *)0,(void *)0,(void *)0,(void *)0,(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_compmakedata" "', argument " "1"" of type '" "void *""'"); + } + res2 = SWIG_AsCharPtrAndSize(objv[2], &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "nx_compmakedata" "', argument " "2"" of type '" "char *""'"); + } + arg2 = (char *)(buf2); + ecode3 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[3], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "nx_compmakedata" "', argument " "3"" of type '" "int""'"); + } + arg3 = (int)(val3); + ecode4 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[4], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "nx_compmakedata" "', argument " "4"" of type '" "int""'"); + } + arg4 = (int)(val4); + res5 = SWIG_ConvertPtr(objv[5],SWIG_as_voidptrptr(&arg5), 0, 0); + if (!SWIG_IsOK(res5)) { + SWIG_exception_fail(SWIG_ArgError(res5), "in method '" "nx_compmakedata" "', argument " "5"" of type '" "void *""'"); + } + res6 = SWIG_ConvertPtr(objv[6],SWIG_as_voidptrptr(&arg6), 0, 0); + if (!SWIG_IsOK(res6)) { + SWIG_exception_fail(SWIG_ArgError(res6), "in method '" "nx_compmakedata" "', argument " "6"" of type '" "void *""'"); + } + result = (int)nx_compmakedata(arg1,arg2,arg3,arg4,arg5,arg6); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_OK; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_opendata(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + char *arg2 = (char *) 0 ; + int result; + int res1 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + + if (SWIG_GetArgs(interp, objc, objv,"oo:nx_opendata handle name ",(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_opendata" "', argument " "1"" of type '" "void *""'"); + } + res2 = SWIG_AsCharPtrAndSize(objv[2], &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "nx_opendata" "', argument " "2"" of type '" "char *""'"); + } + arg2 = (char *)(buf2); + result = (int)nx_opendata(arg1,arg2); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_OK; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_closedata(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + int result; + int res1 ; + + if (SWIG_GetArgs(interp, objc, objv,"o:nx_closedata handle ",(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_closedata" "', argument " "1"" of type '" "void *""'"); + } + result = (int)nx_closedata(arg1); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_putslab(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + void *arg2 = (void *) 0 ; + void *arg3 = (void *) 0 ; + int result; + int res1 ; + int res2 ; + int res3 ; + + if (SWIG_GetArgs(interp, objc, objv,"ooo:nx_putslab handle dataset startDim ",(void *)0,(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_putslab" "', argument " "1"" of type '" "void *""'"); + } + res2 = SWIG_ConvertPtr(objv[2],SWIG_as_voidptrptr(&arg2), 0, 0); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "nx_putslab" "', argument " "2"" of type '" "void *""'"); + } + res3 = SWIG_ConvertPtr(objv[3],SWIG_as_voidptrptr(&arg3), 0, 0); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "nx_putslab" "', argument " "3"" of type '" "void *""'"); + } + result = (int)nx_putslab(arg1,arg2,arg3); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_getslab(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + void *arg2 = (void *) 0 ; + void *arg3 = (void *) 0 ; + void *result = 0 ; + int res1 ; + int res2 ; + int res3 ; + + if (SWIG_GetArgs(interp, objc, objv,"ooo:nx_getslab handle startdim size ",(void *)0,(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_getslab" "', argument " "1"" of type '" "void *""'"); + } + res2 = SWIG_ConvertPtr(objv[2],SWIG_as_voidptrptr(&arg2), 0, 0); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "nx_getslab" "', argument " "2"" of type '" "void *""'"); + } + res3 = SWIG_ConvertPtr(objv[3],SWIG_as_voidptrptr(&arg3), 0, 0); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "nx_getslab" "', argument " "3"" of type '" "void *""'"); + } + result = (void *)nx_getslab(arg1,arg2,arg3); + Tcl_SetObjResult(interp, SWIG_NewInstanceObj( SWIG_as_voidptr(result), SWIGTYPE_p_void,0)); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_getds(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + char *arg2 = (char *) 0 ; + void *result = 0 ; + int res1 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + + if (SWIG_GetArgs(interp, objc, objv,"oo:nx_getds handle name ",(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_getds" "', argument " "1"" of type '" "void *""'"); + } + res2 = SWIG_AsCharPtrAndSize(objv[2], &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "nx_getds" "', argument " "2"" of type '" "char *""'"); + } + arg2 = (char *)(buf2); + result = (void *)nx_getds(arg1,arg2); + Tcl_SetObjResult(interp, SWIG_NewInstanceObj( SWIG_as_voidptr(result), SWIGTYPE_p_void,0)); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_OK; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_putds(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + char *arg2 = (char *) 0 ; + void *arg3 = (void *) 0 ; + int result; + int res1 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + int res3 ; + + if (SWIG_GetArgs(interp, objc, objv,"ooo:nx_putds handle name dataset ",(void *)0,(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_putds" "', argument " "1"" of type '" "void *""'"); + } + res2 = SWIG_AsCharPtrAndSize(objv[2], &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "nx_putds" "', argument " "2"" of type '" "char *""'"); + } + arg2 = (char *)(buf2); + res3 = SWIG_ConvertPtr(objv[3],SWIG_as_voidptrptr(&arg3), 0, 0); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "nx_putds" "', argument " "3"" of type '" "void *""'"); + } + result = (int)nx_putds(arg1,arg2,arg3); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_OK; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_getdata(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + void *result = 0 ; + int res1 ; + + if (SWIG_GetArgs(interp, objc, objv,"o:nx_getdata handle ",(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_getdata" "', argument " "1"" of type '" "void *""'"); + } + result = (void *)nx_getdata(arg1); + Tcl_SetObjResult(interp, SWIG_NewInstanceObj( SWIG_as_voidptr(result), SWIGTYPE_p_void,0)); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_putdata(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + void *arg2 = (void *) 0 ; + int result; + int res1 ; + int res2 ; + + if (SWIG_GetArgs(interp, objc, objv,"oo:nx_putdata handle dataset ",(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_putdata" "', argument " "1"" of type '" "void *""'"); + } + res2 = SWIG_ConvertPtr(objv[2],SWIG_as_voidptrptr(&arg2), 0, 0); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "nx_putdata" "', argument " "2"" of type '" "void *""'"); + } + result = (int)nx_putdata(arg1,arg2); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_getinfo(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + void *result = 0 ; + int res1 ; + + if (SWIG_GetArgs(interp, objc, objv,"o:nx_getinfo handle ",(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_getinfo" "', argument " "1"" of type '" "void *""'"); + } + result = (void *)nx_getinfo(arg1); + Tcl_SetObjResult(interp, SWIG_NewInstanceObj( SWIG_as_voidptr(result), SWIGTYPE_p_void,0)); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_getdataID(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + void *result = 0 ; + int res1 ; + + if (SWIG_GetArgs(interp, objc, objv,"o:nx_getdataID handle ",(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_getdataID" "', argument " "1"" of type '" "void *""'"); + } + result = (void *)nx_getdataID(arg1); + Tcl_SetObjResult(interp, SWIG_NewInstanceObj( SWIG_as_voidptr(result), SWIGTYPE_p_void,0)); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_getnextattr(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + char arg2 ; + char *result = 0 ; + int res1 ; + char val2 ; + int ecode2 = 0 ; + + if (SWIG_GetArgs(interp, objc, objv,"oo:nx_getnextattr handle separator ",(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_getnextattr" "', argument " "1"" of type '" "void *""'"); + } + ecode2 = SWIG_AsVal_char SWIG_TCL_CALL_ARGS_2(objv[2], &val2); + if (!SWIG_IsOK(ecode2)) { + SWIG_exception_fail(SWIG_ArgError(ecode2), "in method '" "nx_getnextattr" "', argument " "2"" of type '" "char""'"); + } + arg2 = (char)(val2); + result = (char *)nx_getnextattr(arg1,arg2); + Tcl_SetObjResult(interp,SWIG_FromCharPtr((const char *)result)); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_putattr(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + char *arg2 = (char *) 0 ; + void *arg3 = (void *) 0 ; + int result; + int res1 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + int res3 ; + + if (SWIG_GetArgs(interp, objc, objv,"ooo:nx_putattr handle name ds ",(void *)0,(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_putattr" "', argument " "1"" of type '" "void *""'"); + } + res2 = SWIG_AsCharPtrAndSize(objv[2], &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "nx_putattr" "', argument " "2"" of type '" "char *""'"); + } + arg2 = (char *)(buf2); + res3 = SWIG_ConvertPtr(objv[3],SWIG_as_voidptrptr(&arg3), 0, 0); + if (!SWIG_IsOK(res3)) { + SWIG_exception_fail(SWIG_ArgError(res3), "in method '" "nx_putattr" "', argument " "3"" of type '" "void *""'"); + } + result = (int)nx_putattr(arg1,arg2,arg3); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_OK; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_getattr(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + char *arg2 = (char *) 0 ; + int arg3 ; + int arg4 ; + void *result = 0 ; + int res1 ; + int res2 ; + char *buf2 = 0 ; + int alloc2 = 0 ; + int val3 ; + int ecode3 = 0 ; + int val4 ; + int ecode4 = 0 ; + + if (SWIG_GetArgs(interp, objc, objv,"oooo:nx_getattr handle name type length ",(void *)0,(void *)0,(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_getattr" "', argument " "1"" of type '" "void *""'"); + } + res2 = SWIG_AsCharPtrAndSize(objv[2], &buf2, NULL, &alloc2); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "nx_getattr" "', argument " "2"" of type '" "char *""'"); + } + arg2 = (char *)(buf2); + ecode3 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[3], &val3); + if (!SWIG_IsOK(ecode3)) { + SWIG_exception_fail(SWIG_ArgError(ecode3), "in method '" "nx_getattr" "', argument " "3"" of type '" "int""'"); + } + arg3 = (int)(val3); + ecode4 = SWIG_AsVal_int SWIG_TCL_CALL_ARGS_2(objv[4], &val4); + if (!SWIG_IsOK(ecode4)) { + SWIG_exception_fail(SWIG_ArgError(ecode4), "in method '" "nx_getattr" "', argument " "4"" of type '" "int""'"); + } + arg4 = (int)(val4); + result = (void *)nx_getattr(arg1,arg2,arg3,arg4); + Tcl_SetObjResult(interp, SWIG_NewInstanceObj( SWIG_as_voidptr(result), SWIGTYPE_p_void,0)); + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_OK; +fail: + if (alloc2 == SWIG_NEWOBJ) free((char*)buf2); + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_makelink(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + void *arg2 = (void *) 0 ; + int result; + int res1 ; + int res2 ; + + if (SWIG_GetArgs(interp, objc, objv,"oo:nx_makelink handle link ",(void *)0,(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_makelink" "', argument " "1"" of type '" "void *""'"); + } + res2 = SWIG_ConvertPtr(objv[2],SWIG_as_voidptrptr(&arg2), 0, 0); + if (!SWIG_IsOK(res2)) { + SWIG_exception_fail(SWIG_ArgError(res2), "in method '" "nx_makelink" "', argument " "2"" of type '" "void *""'"); + } + result = (int)nx_makelink(arg1,arg2); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + return TCL_OK; +fail: + return TCL_ERROR; +} + + +SWIGINTERN int +_wrap_nx_opensourcegroup(ClientData clientData SWIGUNUSED, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + void *arg1 = (void *) 0 ; + int result; + int res1 ; + + if (SWIG_GetArgs(interp, objc, objv,"o:nx_opensourcegroup handle ",(void *)0) == TCL_ERROR) SWIG_fail; + res1 = SWIG_ConvertPtr(objv[1],SWIG_as_voidptrptr(&arg1), 0, 0); + if (!SWIG_IsOK(res1)) { + SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "nx_opensourcegroup" "', argument " "1"" of type '" "void *""'"); + } + result = (int)nx_opensourcegroup(arg1); + Tcl_SetObjResult(interp,SWIG_From_int((int)(result))); + return TCL_OK; +fail: + return TCL_ERROR; +} + + + +static swig_command_info swig_commands[] = { + { SWIG_prefix "create_nxds", (swig_wrapper_func) _wrap_create_nxds, NULL}, + { SWIG_prefix "create_text_nxds", (swig_wrapper_func) _wrap_create_text_nxds, NULL}, + { SWIG_prefix "drop_nxds", (swig_wrapper_func) _wrap_drop_nxds, NULL}, + { SWIG_prefix "get_nxds_rank", (swig_wrapper_func) _wrap_get_nxds_rank, NULL}, + { SWIG_prefix "get_nxds_type", (swig_wrapper_func) _wrap_get_nxds_type, NULL}, + { SWIG_prefix "get_nxds_dim", (swig_wrapper_func) _wrap_get_nxds_dim, NULL}, + { SWIG_prefix "get_nxds_value", (swig_wrapper_func) _wrap_get_nxds_value, NULL}, + { SWIG_prefix "get_nxds_text", (swig_wrapper_func) _wrap_get_nxds_text, NULL}, + { SWIG_prefix "put_nxds_value", (swig_wrapper_func) _wrap_put_nxds_value, NULL}, + { SWIG_prefix "nx_getlasterror", (swig_wrapper_func) _wrap_nx_getlasterror, NULL}, + { SWIG_prefix "nx_open", (swig_wrapper_func) _wrap_nx_open, NULL}, + { SWIG_prefix "nx_flush", (swig_wrapper_func) _wrap_nx_flush, NULL}, + { SWIG_prefix "nx_close", (swig_wrapper_func) _wrap_nx_close, NULL}, + { SWIG_prefix "nx_makegroup", (swig_wrapper_func) _wrap_nx_makegroup, NULL}, + { SWIG_prefix "nx_opengroup", (swig_wrapper_func) _wrap_nx_opengroup, NULL}, + { SWIG_prefix "nx_openpath", (swig_wrapper_func) _wrap_nx_openpath, NULL}, + { SWIG_prefix "nx_opengrouppath", (swig_wrapper_func) _wrap_nx_opengrouppath, NULL}, + { SWIG_prefix "nx_closegroup", (swig_wrapper_func) _wrap_nx_closegroup, NULL}, + { SWIG_prefix "nx_getnextentry", (swig_wrapper_func) _wrap_nx_getnextentry, NULL}, + { SWIG_prefix "nx_getgroupID", (swig_wrapper_func) _wrap_nx_getgroupID, NULL}, + { SWIG_prefix "nx_initgroupdir", (swig_wrapper_func) _wrap_nx_initgroupdir, NULL}, + { SWIG_prefix "nx_makedata", (swig_wrapper_func) _wrap_nx_makedata, NULL}, + { SWIG_prefix "nx_compmakedata", (swig_wrapper_func) _wrap_nx_compmakedata, NULL}, + { SWIG_prefix "nx_opendata", (swig_wrapper_func) _wrap_nx_opendata, NULL}, + { SWIG_prefix "nx_closedata", (swig_wrapper_func) _wrap_nx_closedata, NULL}, + { SWIG_prefix "nx_putslab", (swig_wrapper_func) _wrap_nx_putslab, NULL}, + { SWIG_prefix "nx_getslab", (swig_wrapper_func) _wrap_nx_getslab, NULL}, + { SWIG_prefix "nx_getds", (swig_wrapper_func) _wrap_nx_getds, NULL}, + { SWIG_prefix "nx_putds", (swig_wrapper_func) _wrap_nx_putds, NULL}, + { SWIG_prefix "nx_getdata", (swig_wrapper_func) _wrap_nx_getdata, NULL}, + { SWIG_prefix "nx_putdata", (swig_wrapper_func) _wrap_nx_putdata, NULL}, + { SWIG_prefix "nx_getinfo", (swig_wrapper_func) _wrap_nx_getinfo, NULL}, + { SWIG_prefix "nx_getdataID", (swig_wrapper_func) _wrap_nx_getdataID, NULL}, + { SWIG_prefix "nx_getnextattr", (swig_wrapper_func) _wrap_nx_getnextattr, NULL}, + { SWIG_prefix "nx_putattr", (swig_wrapper_func) _wrap_nx_putattr, NULL}, + { SWIG_prefix "nx_getattr", (swig_wrapper_func) _wrap_nx_getattr, NULL}, + { SWIG_prefix "nx_makelink", (swig_wrapper_func) _wrap_nx_makelink, NULL}, + { SWIG_prefix "nx_opensourcegroup", (swig_wrapper_func) _wrap_nx_opensourcegroup, NULL}, + {0, 0, 0} +}; + +static swig_var_info swig_variables[] = { + {0,0,0,0} +}; + +static swig_const_info swig_constants[] = { + {0,0,0,0,0,0} +}; + +/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */ + +static swig_type_info _swigt__p_char = {"_p_char", "char *", 0, 0, (void*)0, 0}; +static swig_type_info _swigt__p_void = {"_p_void", "void *", 0, 0, (void*)0, 0}; + +static swig_type_info *swig_type_initial[] = { + &_swigt__p_char, + &_swigt__p_void, +}; + +static swig_cast_info _swigc__p_char[] = { {&_swigt__p_char, 0, 0, 0},{0, 0, 0, 0}}; +static swig_cast_info _swigc__p_void[] = { {&_swigt__p_void, 0, 0, 0},{0, 0, 0, 0}}; + +static swig_cast_info *swig_cast_initial[] = { + _swigc__p_char, + _swigc__p_void, +}; + + +/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */ + +#ifdef __cplusplus +} +#endif +/* ----------------------------------------------------------------------------- + * Type initialization: + * This problem is tough by the requirement that no dynamic + * memory is used. Also, since swig_type_info structures store pointers to + * swig_cast_info structures and swig_cast_info structures store pointers back + * to swig_type_info structures, we need some lookup code at initialization. + * The idea is that swig generates all the structures that are needed. + * The runtime then collects these partially filled structures. + * The SWIG_InitializeModule function takes these initial arrays out of + * swig_module, and does all the lookup, filling in the swig_module.types + * array with the correct data and linking the correct swig_cast_info + * structures together. + * + * The generated swig_type_info structures are assigned staticly to an initial + * array. We just loop through that array, and handle each type individually. + * First we lookup if this type has been already loaded, and if so, use the + * loaded structure instead of the generated one. Then we have to fill in the + * cast linked list. The cast data is initially stored in something like a + * two-dimensional array. Each row corresponds to a type (there are the same + * number of rows as there are in the swig_type_initial array). Each entry in + * a column is one of the swig_cast_info structures for that type. + * The cast_initial array is actually an array of arrays, because each row has + * a variable number of columns. So to actually build the cast linked list, + * we find the array of casts associated with the type, and loop through it + * adding the casts to the list. The one last trick we need to do is making + * sure the type pointer in the swig_cast_info struct is correct. + * + * First off, we lookup the cast->type name to see if it is already loaded. + * There are three cases to handle: + * 1) If the cast->type has already been loaded AND the type we are adding + * casting info to has not been loaded (it is in this module), THEN we + * replace the cast->type pointer with the type pointer that has already + * been loaded. + * 2) If BOTH types (the one we are adding casting info to, and the + * cast->type) are loaded, THEN the cast info has already been loaded by + * the previous module so we just ignore it. + * 3) Finally, if cast->type has not already been loaded, then we add that + * swig_cast_info to the linked list (because the cast->type) pointer will + * be correct. + * ----------------------------------------------------------------------------- */ + +#ifdef __cplusplus +extern "C" { +#if 0 +} /* c-mode */ +#endif +#endif + +#if 0 +#define SWIGRUNTIME_DEBUG +#endif + + +SWIGRUNTIME void +SWIG_InitializeModule(void *clientdata) { + size_t i; + swig_module_info *module_head, *iter; + int found; + + clientdata = clientdata; + + /* check to see if the circular list has been setup, if not, set it up */ + if (swig_module.next==0) { + /* Initialize the swig_module */ + swig_module.type_initial = swig_type_initial; + swig_module.cast_initial = swig_cast_initial; + swig_module.next = &swig_module; + } + + /* Try and load any already created modules */ + module_head = SWIG_GetModule(clientdata); + if (!module_head) { + /* This is the first module loaded for this interpreter */ + /* so set the swig module into the interpreter */ + SWIG_SetModule(clientdata, &swig_module); + module_head = &swig_module; + } else { + /* the interpreter has loaded a SWIG module, but has it loaded this one? */ + found=0; + iter=module_head; + do { + if (iter==&swig_module) { + found=1; + break; + } + iter=iter->next; + } while (iter!= module_head); + + /* if the is found in the list, then all is done and we may leave */ + if (found) return; + /* otherwise we must add out module into the list */ + swig_module.next = module_head->next; + module_head->next = &swig_module; + } + + /* Now work on filling in swig_module.types */ +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: size %d\n", swig_module.size); +#endif + for (i = 0; i < swig_module.size; ++i) { + swig_type_info *type = 0; + swig_type_info *ret; + swig_cast_info *cast; + +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name); +#endif + + /* if there is another module already loaded */ + if (swig_module.next != &swig_module) { + type = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, swig_module.type_initial[i]->name); + } + if (type) { + /* Overwrite clientdata field */ +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: found type %s\n", type->name); +#endif + if (swig_module.type_initial[i]->clientdata) { + type->clientdata = swig_module.type_initial[i]->clientdata; +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: found and overwrite type %s \n", type->name); +#endif + } + } else { + type = swig_module.type_initial[i]; + } + + /* Insert casting types */ + cast = swig_module.cast_initial[i]; + while (cast->type) { + /* Don't need to add information already in the list */ + ret = 0; +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: look cast %s\n", cast->type->name); +#endif + if (swig_module.next != &swig_module) { + ret = SWIG_MangledTypeQueryModule(swig_module.next, &swig_module, cast->type->name); +#ifdef SWIGRUNTIME_DEBUG + if (ret) printf("SWIG_InitializeModule: found cast %s\n", ret->name); +#endif + } + if (ret) { + if (type == swig_module.type_initial[i]) { +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: skip old type %s\n", ret->name); +#endif + cast->type = ret; + ret = 0; + } else { + /* Check for casting already in the list */ + swig_cast_info *ocast = SWIG_TypeCheck(ret->name, type); +#ifdef SWIGRUNTIME_DEBUG + if (ocast) printf("SWIG_InitializeModule: skip old cast %s\n", ret->name); +#endif + if (!ocast) ret = 0; + } + } + + if (!ret) { +#ifdef SWIGRUNTIME_DEBUG + printf("SWIG_InitializeModule: adding cast %s\n", cast->type->name); +#endif + if (type->cast) { + type->cast->prev = cast; + cast->next = type->cast; + } + type->cast = cast; + } + cast++; + } + /* Set entry in modules->types array equal to the type */ + swig_module.types[i] = type; + } + swig_module.types[i] = 0; + +#ifdef SWIGRUNTIME_DEBUG + printf("**** SWIG_InitializeModule: Cast List ******\n"); + for (i = 0; i < swig_module.size; ++i) { + int j = 0; + swig_cast_info *cast = swig_module.cast_initial[i]; + printf("SWIG_InitializeModule: type %d %s\n", i, swig_module.type_initial[i]->name); + while (cast->type) { + printf("SWIG_InitializeModule: cast type %s\n", cast->type->name); + cast++; + ++j; + } + printf("---- Total casts: %d\n",j); + } + printf("**** SWIG_InitializeModule: Cast List ******\n"); +#endif +} + +/* This function will propagate the clientdata field of type to +* any new swig_type_info structures that have been added into the list +* of equivalent types. It is like calling +* SWIG_TypeClientData(type, clientdata) a second time. +*/ +SWIGRUNTIME void +SWIG_PropagateClientData(void) { + size_t i; + swig_cast_info *equiv; + static int init_run = 0; + + if (init_run) return; + init_run = 1; + + for (i = 0; i < swig_module.size; i++) { + if (swig_module.types[i]->clientdata) { + equiv = swig_module.types[i]->cast; + while (equiv) { + if (!equiv->converter) { + if (equiv->type && !equiv->type->clientdata) + SWIG_TypeClientData(equiv->type, swig_module.types[i]->clientdata); + } + equiv = equiv->next; + } + } + } +} + +#ifdef __cplusplus +#if 0 +{ + /* c-mode */ +#endif +} +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + + /* ----------------------------------------------------------------------------- + * constants/methods manipulation + * ----------------------------------------------------------------------------- */ + + /* Install Constants */ + + SWIGINTERN void + SWIG_Tcl_InstallConstants(Tcl_Interp *interp, swig_const_info constants[]) { + int i; + Tcl_Obj *obj; + + if (!swigconstTableinit) { + Tcl_InitHashTable(&swigconstTable, TCL_STRING_KEYS); + swigconstTableinit = 1; + } + for (i = 0; constants[i].type; i++) { + switch(constants[i].type) { + case SWIG_TCL_POINTER: + obj = SWIG_NewPointerObj(constants[i].pvalue, *(constants[i]).ptype,0); + break; + case SWIG_TCL_BINARY: + obj = SWIG_NewPackedObj(constants[i].pvalue, constants[i].lvalue, *(constants[i].ptype)); + break; + default: + obj = 0; + break; + } + if (obj) { + SWIG_Tcl_SetConstantObj(interp, constants[i].name, obj); + } + } + } + +#ifdef __cplusplus +} +#endif + +/* -----------------------------------------------------------------------------* + * Partial Init method + * -----------------------------------------------------------------------------*/ + +SWIGEXPORT int SWIG_init(Tcl_Interp *interp) { + int i; + if (interp == 0) return TCL_ERROR; +#ifdef USE_TCL_STUBS + if (Tcl_InitStubs(interp, (char*)"8.1", 0) == NULL) { + return TCL_ERROR; + } +#endif + Tcl_PkgProvide(interp, (char*)SWIG_name, (char*)SWIG_version); + +#ifdef SWIG_namespace + Tcl_Eval(interp, "namespace eval " SWIG_namespace " { }"); +#endif + + SWIG_InitializeModule((void *) interp); + SWIG_PropagateClientData(); + + for (i = 0; swig_commands[i].name; i++) { + Tcl_CreateObjCommand(interp, (char *) swig_commands[i].name, (swig_wrapper_func) swig_commands[i].wrapper, + swig_commands[i].clientdata, NULL); + } + for (i = 0; swig_variables[i].name; i++) { + Tcl_SetVar(interp, (char *) swig_variables[i].name, (char *) "", TCL_GLOBAL_ONLY); + Tcl_TraceVar(interp, (char *) swig_variables[i].name, TCL_TRACE_READS | TCL_GLOBAL_ONLY, + (Tcl_VarTraceProc *) swig_variables[i].get, (ClientData) swig_variables[i].addr); + Tcl_TraceVar(interp, (char *) swig_variables[i].name, TCL_TRACE_WRITES | TCL_GLOBAL_ONLY, + (Tcl_VarTraceProc *) swig_variables[i].set, (ClientData) swig_variables[i].addr); + } + + SWIG_Tcl_InstallConstants(interp, swig_constants); + + + SWIG_Tcl_SetConstantObj(interp, "NXACC_READ", SWIG_From_int((int)(1))); + SWIG_Tcl_SetConstantObj(interp, "NXACC_RDWR", SWIG_From_int((int)(2))); + SWIG_Tcl_SetConstantObj(interp, "NXACC_CREATE", SWIG_From_int((int)(3))); + SWIG_Tcl_SetConstantObj(interp, "NXACC_CREATE4", SWIG_From_int((int)(4))); + SWIG_Tcl_SetConstantObj(interp, "NXACC_CREATE5", SWIG_From_int((int)(5))); + SWIG_Tcl_SetConstantObj(interp, "NX_FLOAT32", SWIG_From_int((int)(5))); + SWIG_Tcl_SetConstantObj(interp, "NX_FLOAT64", SWIG_From_int((int)(6))); + SWIG_Tcl_SetConstantObj(interp, "NX_INT8", SWIG_From_int((int)(20))); + SWIG_Tcl_SetConstantObj(interp, "NX_UINT8", SWIG_From_int((int)(21))); + SWIG_Tcl_SetConstantObj(interp, "NX_BOOLEAN", SWIG_From_int((int)(21))); + SWIG_Tcl_SetConstantObj(interp, "NX_INT16", SWIG_From_int((int)(22))); + SWIG_Tcl_SetConstantObj(interp, "NX_UINT16", SWIG_From_int((int)(23))); + SWIG_Tcl_SetConstantObj(interp, "NX_INT32", SWIG_From_int((int)(24))); + SWIG_Tcl_SetConstantObj(interp, "NX_UINT32", SWIG_From_int((int)(25))); + SWIG_Tcl_SetConstantObj(interp, "NX_CHAR", SWIG_From_int((int)(4))); + return TCL_OK; +} +SWIGEXPORT int Nxinter_SafeInit(Tcl_Interp *interp) { + return SWIG_init(interp); +} + diff --git a/nxinterhelper.c b/nxinterhelper.c new file mode 100644 index 00000000..723e0adf --- /dev/null +++ b/nxinterhelper.c @@ -0,0 +1,561 @@ +/* + This is a library of support functions and data structures which can be + used in order to create interfaces between the NeXus-API and scripting + languages or data analysis systems with a native code interface. + + copyright: GPL + + Mark Koennecke, October 2002 + Mark Koennecke, November 2002 +*/ +#include +#include +#include +#include +#include "nxinterhelper.h" +#include "nxdataset.h" +/*----------------------------------------------------------------- + An own error handler. nx_getlasterror will return the test of + the last NeXus error. + --------------------------------------------------------------------*/ +static char errorText[256]= ""; + +static void nxinterError(void *pData, char *error){ + strncpy(errorText,error,255); +} +/*-----------------------------------------------------------------------*/ +char *nx_getlasterror(void){ + return strdup(errorText); +} +/*-------------------- opening and closing -------------------------------*/ +void *nx_open(char *filename, int accessMethod){ + NXhandle handle = NULL; + int status; + + NXMSetError(NULL,nxinterError); + status = NXopen(filename,(NXaccess)accessMethod, &handle); + if(status == NX_OK){ + return handle; + }else{ + return NULL; + } +} +/*------------------------------------------------------------------------*/ +void *nx_flush(void *hundle){ + NXhandle handle; + int status; + + handle = (NXhandle)hundle; + status = NXflush(&handle); + if(status == NX_OK){ + return handle; + } else { + return NULL; + } +} +/*-----------------------------------------------------------------------*/ +void nx_close(void *hundle){ + NXhandle handle; + + handle = (NXhandle)hundle; + NXclose(&handle); +} +/*=================== group handling functions ========================*/ +int nx_makegroup(void *handle, char *name, char *nxclass){ + int status; + NXhandle hfil; + + hfil = (NXhandle)handle; + status = NXmakegroup(hfil,name, nxclass); + if(status == NX_OK){ + return 1; + } else { + return 0; + } +} +/*---------------------------------------------------------------------*/ +int nx_opengroup(void *handle, char *name, char *nxclass){ + int status; + NXhandle hfil; + + hfil = (NXhandle)handle; + status = NXopengroup(hfil,name, nxclass); + if(status == NX_OK){ + return 1; + } else { + return 0; + } +} +/*---------------------------------------------------------------------*/ +int nx_openpath(void *handle, char *path){ + int status; + NXhandle hfil; + + hfil = (NXhandle)handle; + status = NXopenpath(hfil,path); + if(status == NX_OK){ + return 1; + } else { + return 0; + } +} +/*---------------------------------------------------------------------*/ +int nx_opengrouppath(void *handle, char *path){ + int status; + NXhandle hfil; + + hfil = (NXhandle)handle; + status = NXopengrouppath(hfil,path); + if(status == NX_OK){ + return 1; + } else { + return 0; + } +} +/*--------------------------------------------------------------------*/ +int nx_closegroup(void *handle){ + int status; + NXhandle hfil; + + hfil = (NXhandle)handle; + status = NXclosegroup(hfil); + if(status == NX_OK){ + return 1; + } else { + return 0; + } +} +/*-------------------------------------------------------------------*/ +char *nx_getnextentry(void *handle, char separator){ + int status, length, type; + NXhandle hfil; + char *resultBuffer = NULL; + NXname group,nxclass; + + hfil = (NXhandle)handle; + status = NXgetnextentry(hfil,group, nxclass,&type); + if(status == NX_OK){ + length = 30 + strlen(group) + strlen(nxclass); + /* + This introduces a memory leak. I had hoped, that swig would + kill it for me after use, but I'am afraid, this is not the + case. Unfortately I do not know how to fix the issue. + */ + resultBuffer = (char *)malloc(length*sizeof(char)); + if(resultBuffer == NULL){ + return NULL; + } + sprintf(resultBuffer,"%s%c%s%c%d",group,separator,nxclass, + separator,type); + return resultBuffer; + } else { + return NULL; + } +} +/*-------------------------------------------------------------------*/ +void *nx_getgroupID(void *handle){ + int status; + NXhandle hfil; + NXlink *linki; + + linki = (NXlink *)malloc(sizeof(NXlink)); + if(linki == NULL){ + return NULL; + } + hfil = (NXhandle)handle; + status = NXgetgroupID(hfil,linki); + if(status == NX_OK){ + return linki; + } else { + return NULL; + } +} +/*------------------------------------------------------------------*/ +int nx_initgroupdir(void *handle){ + int status; + NXhandle hfil; + + hfil = (NXhandle)handle; + status = NXinitgroupdir(hfil); + if(status == NX_OK){ + return 1; + } else { + return 0; + } +} +/*========================== dataset handling =======================*/ +int nx_makedata(void *ptr, char *name, int rank, int type, + void *dimPtr){ + int status; + NXhandle hfil; + pNXDS dimData; + + hfil = (NXhandle)ptr; + dimData = (pNXDS)dimPtr; + if(dimData->type != NX_INT32){ + NXIReportError(NULL,"ERROR: dimension data not integer"); + return 0; + } + status = NXmakedata(hfil, name, type, rank, + dimData->u.iPtr); + if(status == NX_OK){ + return 1; + } else { + return 0; + } +} +/*--------------------------------------------------------------------*/ +int nx_compmakedata(void *ptr, char *name, int rank, int type, + void *dimPtr, void *bufPtr){ + int status; + NXhandle hfil; + pNXDS dimData, bufData; + + hfil = (NXhandle)ptr; + dimData = (pNXDS)dimPtr; + if(dimData->type != NX_INT32){ + NXIReportError(NULL,"ERROR: dimension data not integer"); + return 0; + } + bufData = (pNXDS)bufPtr; + status = NXcompmakedata(hfil, name, type, rank, + dimData->u.iPtr, NX_COMP_LZW,bufData->u.iPtr); + if(status == NX_OK){ + return 1; + } else { + return 0; + } +} +/*----------------------------------------------------------------------*/ +int nx_opendata(void *handle, char *name){ + int status; + NXhandle hfil; + + hfil = (NXhandle)handle; + status = NXopendata(hfil,name); + if(status == NX_OK){ + return 1; + } else { + return 0; + } +} +/*----------------------------------------------------------------------*/ +int nx_closedata(void *handle){ + int status; + NXhandle hfil; + + hfil = (NXhandle)handle; + status = NXclosedata(hfil); + if(status == NX_OK){ + return 1; + } else { + return 0; + } +} +/*------------------------------------------------------------------------*/ +int nx_putslab(void *handle, void *dataset, void *startDim){ + int status; + NXhandle hfil; + pNXDS data; + pNXDS start; + int end[NX_MAXRANK], i; + + hfil = (NXhandle)handle; + data = (pNXDS)dataset; + start = (pNXDS)startDim; + for(i = 0; i < data->rank;i++){ + end[i] = data->dim[i]; + } + + status = NXputslab(hfil,data->u.ptr,start->u.iPtr,end); + if(status == NX_OK){ + return 1; + } else { + return 0; + } +} +/*-----------------------------------------------------------------------*/ +void *nx_getslab(void *handle, void *startdim, void *sizedim){ + pNXDS resultdata; + pNXDS start, size; + int status, rank, type, dim[NX_MAXRANK]; + NXhandle hfil; + + hfil = (NXhandle)handle; + start = (pNXDS)startdim; + size = (pNXDS)sizedim; + + /* + get info first, then allocate data + */ + status = NXgetinfo(hfil, &rank,dim,&type); + if(status != NX_OK){ + return NULL; + } + + resultdata = createNXDataset(rank,type,size->u.iPtr); + if(resultdata == NULL){ + return NULL; + } + + status = NXgetslab(hfil,resultdata->u.ptr,start->u.iPtr, + size->u.iPtr); + if(status == NX_OK){ + return resultdata; + }else{ + dropNXDataset(resultdata); + return NULL; + } +} +/*------------------------------------------------------------------------*/ +void *nx_getds(void *handle, char *name){ + pNXDS result = NULL; + int rank, type,dim[NX_MAXRANK],status; + NXhandle hfil; + + hfil = (NXhandle)handle; + status = NXopendata(hfil,name); + if(status != NX_OK){ + return NULL; + } + + status = NXgetinfo(hfil,&rank,dim,&type); + if(status != NX_OK){ + return NULL; + } + + result = createNXDataset(rank,type,dim); + if(result == NULL){ + NXclosedata(hfil); + return NULL; + } + + status = NXgetdata(hfil,result->u.ptr); + if(result == NULL){ + NXclosedata(hfil); + dropNXDataset(result); + return NULL; + } + NXclosedata(hfil); + return result; +} +/*----------------------------------------------------------------------*/ +int nx_putds(void *handle, char *name, void *dataset){ + NXhandle hfil; + int status; + pNXDS data; + + hfil = (NXhandle)handle; + data = (pNXDS)dataset; + + status = NXopendata(hfil,name); + if(status != NX_OK){ + status = NXmakedata(hfil,name,data->type,data->rank,data->dim); + if(status != NX_OK){ + return 0; + } + NXopendata(hfil,name); + } + + status = NXputdata(hfil,data->u.ptr); + NXclosedata(hfil); + if(status != NX_OK){ + return 0; + }else{ + return 1; + } +} +/*------------------------------------------------------------------------*/ +void *nx_getdata(void *handle){ + pNXDS result = NULL; + int rank, type,dim[NX_MAXRANK],status; + NXhandle hfil; + + + hfil = (NXhandle)handle; + status = NXgetinfo(hfil,&rank,dim,&type); + if(status != NX_OK){ + return NULL; + } + + result = createNXDataset(rank,type,dim); + if(result == NULL){ + NXclosedata(hfil); + return NULL; + } + + status = NXgetdata(hfil,result->u.ptr); + if(result == NULL){ + dropNXDataset(result); + return NULL; + } + return result; +} +/*----------------------------------------------------------------------*/ +int nx_putdata(void *handle, void *dataset){ + NXhandle hfil; + int status; + pNXDS data; + + hfil = (NXhandle)handle; + data = (pNXDS)dataset; + + if(data == NULL){ + NXIReportError(NULL,"ERROR: NULL data pointer in nx_putdata"); + return 0; + } + + status = NXputdata(hfil,data->u.ptr); + if(status != NX_OK){ + return 0; + }else{ + return 1; + } +} +/*----------------------------------------------------------------------*/ +void *nx_getinfo(void *handle){ + NXhandle hfil; + int status, type, rank, dim[NX_MAXRANK], rdim[1], i; + pNXDS data = NULL; + + hfil = (NXhandle)handle; + + status = NXgetinfo(handle,&rank,dim,&type); + if(status != NX_OK){ + return NULL; + } + rdim[0] = 2 + rank; + data = createNXDataset(1,NX_INT32,rdim); + data->u.iPtr[0] = type; + data->u.iPtr[1] = rank; + for(i = 0; i < rank; i++){ + data->u.iPtr[2+i] = dim[i]; + } + return data; +} +/*----------------------------------------------------------------------*/ +void *nx_getdataID(void *handle){ + NXhandle hfil; + int status; + NXlink *linki; + + linki = (NXlink *)malloc(sizeof(NXlink)); + if(linki == NULL){ + return NULL; + } + + hfil = (NXhandle)handle; + status = NXgetdataID(hfil,linki); + if(status == NX_OK){ + return linki; + } else { + free(linki); + return NULL; + } +} +/*-------------------------------------------------------------------*/ +char *nx_getnextattr(void *handle, char separator){ + int status, length, type; + char *result; + NXhandle hfil; + NXname aName; + + hfil = (NXhandle)handle; + status = NXgetnextattr(hfil,aName, &length, &type); + if(status == NX_OK){ + /* + This introduces a memory leak. I had hoped, that swig would + kill it for me after use, but I'am afraid, this is not the + case. Unfortately I do not know how to fix the issue. + */ + result = (char *)malloc((20+strlen(aName))*sizeof(char)); + if(result == NULL){ + return NULL; + } + memset(result,0,(20+strlen(aName))*sizeof(char)); + sprintf(result,"%s%c%d%c%d", aName,separator, + length,separator,type); + return result; + } else { + return NULL; + } +} +/*-------------------------------------------------------------------*/ +int nx_putattr(void *handle, char *name, void *ds){ + int status; + NXhandle hfil; + pNXDS data; + + hfil = (NXhandle)handle; + data = (pNXDS)ds; + status = NXputattr(hfil,name,data->u.ptr,data->dim[0],data->type); + if(status == NX_OK){ + return 1; + }else{ + return 0; + } +} +/*-------------------------------------------------------------------*/ +void *nx_getattr(void *handle, char *name, int type, int length){ + NXhandle hfil; + int status, tp, ll, dim[1]; + pNXDS data = NULL; + + + hfil = (NXhandle)handle; + + /* + prepare dataset + */ + dim[0] = length+1; + data = createNXDataset(1,type,dim); + if(data == NULL){ + return NULL; + } + + /* + finally read the real data + */ + ll = length; + tp = type; + status = NXgetattr(hfil,name,data->u.ptr,&ll,&tp); + if(status != NX_OK){ + dropNXDataset(data); + return NULL; + } + + return data; +} +/*-----------------------------------------------------------------------*/ +int nx_makelink(void *handle, void *link){ + NXhandle hfil; + NXlink* lk; + int status; + + hfil = (NXhandle)handle; + lk = (NXlink *)link; + + status = NXmakelink(hfil,lk); + if(status == NX_OK){ + return 1; + }else{ + return 0; + } +} +/*-----------------------------------------------------------------------*/ +int nx_opensourcegroup(void *handle){ + NXhandle hfil; + int status; + + hfil = (NXhandle)handle; + + status = NXopensourcegroup(hfil); + if(status == NX_OK){ + return 1; + }else{ + return 0; + } +} + + + diff --git a/nxinterhelper.h b/nxinterhelper.h new file mode 100644 index 00000000..7c2e280c --- /dev/null +++ b/nxinterhelper.h @@ -0,0 +1,68 @@ +/* + This is a library of support functions and data structures which can be + used in order to create interfaces between the NeXus-API and scripting + languages or data analysis systems with a native code interface. + + copyright: GPL + + Mark Koennecke, October 2002 +*/ + +#ifndef NXINTERHELPER +#define NXINTERHELPER + +#include + +/*------------- opening and closing section ---------------------*/ +void *nx_open(char *filename, int accessMethod); +void *nx_flush(void *handle); +void nx_close(void *handle); +/*--------------- group handling section ------------------------*/ +int nx_makegroup(void *handle, char *name, char *nxclass); +int nx_opengroup(void *handle, char *name, char *nxclass); +int nx_opengrouppath(void *handle, char *path); +int nx_openpath(void *handle, char *path); +int nx_closegroup(void *handle); +char *nx_getnextentry(void *handle, char separator); +void *nx_getgroupID(void *handle); +int nx_initgroupdir(void *handle); + +/*---------------- dataset handling -----------------------------*/ +int nx_makedata(void *ptr, char *name, int rank, int type, void *dimPtr); +int nx_compmakedata(void *ptr, char *name, int rank, int type, + void *dimPtr, void *bufPtr); + +int nx_opendata(void *handle, char *name); +int nx_closedata(void *handle); + +int nx_putslab(void *handle, void *dataset, void *startDim); +void *nx_getslab(void *handle, void *startdim, void *size); + +void *nx_getds(void *handle, char *name); +int nx_putds(void *handle, char *name, void *dataset); + +void *nx_getdata(void *handle); +int nx_putdata(void *handle,void *dataset); + +void *nx_getinfo(void *handle); +void *nx_getdataID(void *handle); + +/*-------------------- attributes --------------------------------*/ +char *nx_getnextattr(void *handle, char separator); +int nx_putattr(void *handle, char *name, void *ds); +void *nx_getattr(void *handle, char *name, int type, int length); + + +/*---------------------- link -----------------------------------*/ +int nx_makelink(void *handle, void *link); +int nx_opensourcegroup(void *handle); +/*----------------- error handling -----------------------------*/ +char *nx_getlasterror(void); + +#endif + + + + + + diff --git a/nxio.c b/nxio.c index ca5be754..a74145e9 100644 --- a/nxio.c +++ b/nxio.c @@ -25,8 +25,15 @@ */ #include #include +#include "napi.h" #include "nxio.h" #include "nxdataset.h" +#include "napiconfig.h" + +/* fix for mxml-2.3 */ +#ifndef MXML_WRAP +#define MXML_WRAP 79 +#endif /* #define TESTMAIN 1 */ /*=================== type code handling ================================= */ @@ -36,7 +43,8 @@ typedef struct { int nx_type; }type_code; -static type_code typecode[9]; +#define NTYPECODE 11 +static type_code typecode[NTYPECODE]; /*-----------------------------------------------------------------------*/ void initializeNumberFormats(){ type_code myCode; @@ -76,21 +84,31 @@ void initializeNumberFormats(){ myCode.nx_type = NX_INT32; typecode[6] = myCode; - strcpy(myCode.name,"NX_UNIT32"); + strcpy(myCode.name,"NX_UINT32"); strcpy(myCode.format,"%12d"); myCode.nx_type = NX_UINT32; typecode[7] = myCode; + strcpy(myCode.name,"NX_INT64"); + strcpy(myCode.format,"%24" PRINTF_INT64 ); + myCode.nx_type = NX_INT64; + typecode[8] = myCode; + + strcpy(myCode.name,"NX_UINT64"); + strcpy(myCode.format,"%24" PRINTF_UINT64); + myCode.nx_type = NX_UINT64; + typecode[9] = myCode; + strcpy(myCode.name,"NX_CHAR"); strcpy(myCode.format,"%c"); myCode.nx_type = NX_CHAR; - typecode[8] = myCode; + typecode[10] = myCode; } /*----------------------------------------------------------------------*/ void setNumberFormat(int nx_type, char *format){ int i; - for(i = 0; i < 9; i++){ + for(i = 0; i < NTYPECODE; i++){ if(typecode[i].nx_type == nx_type){ strncpy(typecode[i].format,format,29); } @@ -100,7 +118,7 @@ void setNumberFormat(int nx_type, char *format){ static void getNumberFormat(int nx_type, char format[30]){ int i; - for(i = 0; i < 9; i++){ + for(i = 0; i < NTYPECODE; i++){ if(typecode[i].nx_type == nx_type){ strncpy(format,typecode[i].format,29); } @@ -110,7 +128,7 @@ static void getNumberFormat(int nx_type, char format[30]){ void getNumberText(int nx_type, char *typestring, int typeLen){ int i; - for(i = 0; i < 9; i++){ + for(i = 0; i < NTYPECODE; i++){ if(typecode[i].nx_type == nx_type){ strncpy(typestring,typecode[i].name,typeLen); } @@ -138,9 +156,14 @@ myxml_add_char(int ch, /* I - Character to add */ */ if (*bufsize < 1024) + { (*bufsize) *= 2; + } else - (*bufsize) += 1024; + { + (*bufsize) *= 3; + (*bufsize) /= 2; + } newbuffer = (char *)malloc(*bufsize*sizeof(char)); if(!newbuffer){ @@ -204,7 +227,7 @@ extern char *stptok(char *s, char *tok, size_t toklen, char *brk); /*===================================================================== actual stuff for implementing the callback functions =====================================================================*/ -static void analyzeDim(const char *typeString, int *rank, +void analyzeDim(const char *typeString, int *rank, int *iDim, int *type){ char dimString[132]; char dim[20]; @@ -219,6 +242,8 @@ static void analyzeDim(const char *typeString, int *rank, case NX_UINT16: case NX_INT32: case NX_UINT32: + case NX_INT64: + case NX_UINT64: case NX_FLOAT32: case NX_FLOAT64: iDim[0] = 1; @@ -258,7 +283,7 @@ static void analyzeDim(const char *typeString, int *rank, int translateTypeCode(char *code){ int i, result = -1; - for(i = 0; i < 9; i++){ + for(i = 0; i < NTYPECODE; i++){ if(strstr(code,typecode[i].name) != NULL){ result = typecode[i].nx_type; break; @@ -346,7 +371,11 @@ mxml_type_t nexusTypeCallback(mxml_node_t *parent){ */ return MXML_OPAQUE; } else{ - return MXML_CUSTOM; + if(strstr(typeString,"NX_CHAR") != NULL){ + return MXML_OPAQUE; + } else { + return MXML_CUSTOM; + } } } } @@ -411,12 +440,19 @@ static void formatNumber(double value, char *txt, int txtLen, case NX_UINT32: snprintf(txt,txtLen,format,(int)value); break; + case NX_INT64: + snprintf(txt,txtLen,format,(int64_t)value); + break; + case NX_UINT64: + snprintf(txt,txtLen,format,(uint64_t)value); + break; case NX_FLOAT32: case NX_FLOAT64: snprintf(txt,txtLen,format,value); break; default: - assert(0); /* something is very wrong here */ + /*assert(0); something is very wrong here */ + printf("Problem\n"); break; } } @@ -534,8 +570,12 @@ static int isTextData(mxml_node_t *node){ } } /*---------------------------------------------------------------------*/ + +/* + * note: not reentrant or thead safe; returns pointer to static storage + */ const char *NXwhitespaceCallback(mxml_node_t *node, int where){ - char *indent; + static char *indent = NULL; int len; if(strstr(node->value.element.name,"?xml") != NULL){ @@ -545,6 +585,11 @@ const char *NXwhitespaceCallback(mxml_node_t *node, int where){ if(isTextData(node)){ if(where == MXML_WS_BEFORE_OPEN){ len = countDepth(node)*2 + 2; + if (indent != NULL) + { + free(indent); + indent = NULL; + } indent = (char *)malloc(len*sizeof(char)); if(indent != NULL){ memset(indent,' ',len); @@ -558,6 +603,11 @@ const char *NXwhitespaceCallback(mxml_node_t *node, int where){ if(where == MXML_WS_BEFORE_OPEN || where == MXML_WS_BEFORE_CLOSE){ len = countDepth(node)*2 + 2; + if (indent != NULL) + { + free(indent); + indent = NULL; + } indent = (char *)malloc(len*sizeof(char)); if(indent != NULL){ memset(indent,' ',len); diff --git a/nxio.h b/nxio.h index 98283779..0de80474 100644 --- a/nxio.h +++ b/nxio.h @@ -40,6 +40,8 @@ void getNumberText(int nx_type, char *typestring, int typeLen); void destroyDataset(void *data); int translateTypeCode(char *code); int isDataNode(mxml_node_t *node); +void analyzeDim(const char *typeString, int *rank, + int *iDim, int *type); #endif diff --git a/nxscript.c b/nxscript.c index 29d36937..a97178d2 100644 --- a/nxscript.c +++ b/nxscript.c @@ -373,6 +373,32 @@ static void putSicsData(SConnection *pCon, SicsInterp *pSics, } } /*----------------------------------------------------------------------*/ +static void putAttribute(SConnection *pCon, SicsInterp *pSics, + pNXScript self, int argc, char *argv[]){ + int status, type = NX_CHAR; + char buffer[256]; + + if(argc < 5){ + SCWrite(pCon,"ERROR: insufficient number of arguments to putAttribute", + eError); + return; + } + + status = NXDopenalias(self->fileHandle,self->dictHandle,argv[2]); + if(status != NX_OK){ + sprintf(buffer,"ERROR: failed to open alias %s", argv[2]); + SCWrite(pCon,buffer,eError); + return; + } + status = NXputattr(self->fileHandle,argv[3],(void *)argv[4], + strlen(argv[4])+1, type); + if(status != NX_OK){ + sprintf(buffer,"ERROR: failed to write attribute %s", argv[3]); + SCWrite(pCon,buffer,eError); + } + NXopenpath(self->fileHandle,"/"); +} +/*----------------------------------------------------------------------*/ static void updateHMDim(NXScript *self, pHistMem mem){ int iDim[MAXDIM]; int i, rank, timeLength, status; @@ -394,10 +420,12 @@ static void updateHMDim(NXScript *self, pHistMem mem){ timeBin = GetHistTimeBin(mem,&timeLength); if(timeLength > 2){ sprintf(dummy,"%d",timeLength); - status = NXDupdate(self->dictHandle,"timedim",dummy); - if(status == 0) { - NXDadd(self->dictHandle,"timedim",dummy); - } + } else { + sprintf(dummy,"%d",1); + } + status = NXDupdate(self->dictHandle,"timedim",dummy); + if(status == 0) { + NXDadd(self->dictHandle,"timedim",dummy); } } /*---------------------------------------------------------------------- @@ -410,7 +438,7 @@ static void putHistogramMemory(SConnection *pCon, SicsInterp *pSics, pNXScript self, int argc, char *argv[]){ pHistMem mem = NULL; - int status, start, length, i, subset = 0; + int status, start, length, i, subset = 0, bank = 0; HistInt *iData = NULL; char buffer[256]; @@ -458,6 +486,19 @@ static void putHistogramMemory(SConnection *pCon, SicsInterp *pSics, return; } } + + /* + * check for additional bank number + */ + if(argc > 6){ + status = Tcl_GetInt(InterpGetTcl(pSics),argv[6],&bank); + if(status != TCL_OK){ + sprintf(buffer,"ERROR: failed to convert %s to integer", + argv[6]); + SCWrite(pCon,buffer,eError); + return; + } + } /* read HM @@ -470,7 +511,7 @@ static void putHistogramMemory(SConnection *pCon, SicsInterp *pSics, return; } memset(iData,0,length*sizeof(HistInt)); - status = GetHistogramDirect(mem,pCon,0,start,start+length,iData, + status = GetHistogramDirect(mem,pCon,bank,start,start+length,iData, length*sizeof(HistInt)); }else{ /* @@ -554,10 +595,7 @@ static void putHistogramMemoryChunked(SConnection *pCon, SicsInterp *pSics, SCWrite(pCon,buffer,eError); return; } - - - - + /* read HM */ @@ -607,6 +645,95 @@ static void putHistogramMemoryChunked(SConnection *pCon, SicsInterp *pSics, return; } +/*----------------------------------------------------------------------*/ +static int listToArray(SicsInterp *pSics, char *list, + int intar[NX_MAXRANK]){ + int argc, status, i, val; + CONST char **argv; + Tcl_Interp *pTcl = InterpGetTcl(pSics); + + status = Tcl_SplitList(pTcl, list, &argc, &argv); + if(status != TCL_OK){ + return status; + } + + for(i = 0; i < argc; i++){ + status = Tcl_GetInt(pTcl,argv[i],&val); + if(status != TCL_OK){ + return status; + } + intar[i] = val; + } + Tcl_Free((char *)argv); + return TCL_OK; +} +/*----------------------------------------------------------------------*/ +static void putSlab(SConnection *pCon, SicsInterp *pSics, pNXScript self, + int argc, char *argv[]){ + int start[NX_MAXRANK], size[NX_MAXRANK]; + int status, written = 0; + pHistMem mem = NULL; + HistInt *histData = NULL; + pSICSData data = NULL; + + if(argc < 6){ + SCWrite(pCon,"ERROR: insufficient number of arguments to putslab", + eError); + return; + } + + status = NXDopenalias(self->fileHandle, self->dictHandle,argv[2]); + if(status != NX_OK){ + SCPrintf(pCon,eError,"ERROR: failed to open alias %s", argv[2]); + return; + } + + status = listToArray(pSics,argv[3],start); + if(status != TCL_OK){ + SCWrite(pCon,"ERROR: failed to convert start value list", eError); + return; + } + + status = listToArray(pSics,argv[4],size); + if(status != TCL_OK){ + SCWrite(pCon,"ERROR: failed to convert size value list", eError); + return; + } + + /* + * try to write HM data + */ + mem = (pHistMem)FindCommandData(pSics,argv[5],"HistMem"); + if(mem != NULL){ + histData = GetHistogramPointer(mem,pCon); + if(histData){ + status = NXputslab(self->fileHandle, histData, start, size); + if(status == NX_OK){ + written = 1; + } + } + } + + /* + * try to look for sicsdata + */ + data = (pSICSData)FindCommandData(pSics,argv[5],"SICSData"); + if(data != NULL){ + status = NXputslab(self->fileHandle,data->data,start,size); + if(status == NX_OK){ + written = 1; + } + } + /* + * drop out of hierarchy + */ + NXopenpath(self->fileHandle,"/"); + + if(written == 0){ + SCWrite(pCon,"ERROR: failed to write data, data not recognised", + eError); + } +} /*-------------------------------------------------------------------*/ static void putTimeBinning(SConnection *pCon, SicsInterp *pSics, pNXScript self, @@ -619,7 +746,7 @@ static void putTimeBinning(SConnection *pCon, SicsInterp *pSics, if(argc < 4){ SCWrite(pCon,"ERROR: insufficient number of arguments to puttimebinning", - eError); + eError); return; } @@ -710,7 +837,10 @@ static void putArray(SConnection *pCon, SicsInterp *pSics, data = (float *)malloc(length*sizeof(float)); } if(data == NULL){ - SCWrite(pCon,"ERROR: out of memory or invalid length",eError); + snprintf(buffer,255, + "ERROR: out of memory or invalid length at %s, length = %s", + argv[2],argv[4]); + SCWrite(pCon,buffer,eError); return; } memset(data,0,length*sizeof(float)); @@ -935,7 +1065,7 @@ static int handlePut(SConnection *pCon, SicsInterp *pSics, pNXScript self, } if(strlen(defString) < 900){ strcat(defString," -dim {"); - sprintf(numBuf,"%d",strlen(buffer)+1); + sprintf(numBuf,"%d",(int)strlen(buffer)+1); strcat(defString,numBuf); strcat(defString," }"); } else { @@ -978,6 +1108,12 @@ static int handlePut(SConnection *pCon, SicsInterp *pSics, pNXScript self, }else if(strcmp(argv[1],"putsicsdata") == 0){ /*===============*/ putSicsData(pCon,pSics,self,argc,argv); + }else if(strcmp(argv[1],"putattribute") == 0){ + /*===============*/ + putAttribute(pCon,pSics,self,argc,argv); + }else if(strcmp(argv[1],"putslab") == 0){ + /*===============*/ + putSlab(pCon,pSics,self,argc,argv); } else { SCWrite(pCon,"ERROR: put command not recognised",eError); } @@ -1030,6 +1166,7 @@ int NXScriptAction(SConnection *pCon, SicsInterp *pSics, void *pData, pNXScript self = (pNXScript)pData; char *pFile = NULL; int status; + char buffer[132]; /* preliminary checks @@ -1093,6 +1230,20 @@ int NXScriptAction(SConnection *pCon, SicsInterp *pSics, void *pData, return 1; } + if(strcmp(argv[1],"isalias") == 0) { + if(argc < 3) { + SCWrite(pCon,"ERROR: need alias to test",eError); + return 1; + } + if(NXDget(self->dictHandle,argv[2],buffer,131) == NX_OK){ + snprintf(buffer,131,"%s = 1", argv[2]); + } else { + snprintf(buffer,131,"%s = 0", argv[2]); + } + SCWrite(pCon,buffer,eValue); + return 1; + } + if(strcmp(argv[1],"makelink") == 0){ makeLink(pCon,pSics,self,argc,argv); return 1; diff --git a/nxstack.c b/nxstack.c new file mode 100644 index 00000000..bd2f492c --- /dev/null +++ b/nxstack.c @@ -0,0 +1,100 @@ +/* + This is some code to handle a stack of NeXus files. This is used to implement + external linking within the NeXus-API + + Copyright (C) 1997-2006 Mark Koennecke + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information, see +*/ +#include +#include +#include +#include "nxstack.h" + +/*----------------------------------------------------------------------- + Data definitions +---------------------------------------------------------------------*/ + +typedef struct { + pNexusFunction pDriver; + NXlink closeID; + char filename[1024]; +}fileStackEntry; + + +typedef struct __fileStack { + int fileStackPointer; + fileStackEntry fileStack[MAXEXTERNALDEPTH]; +}fileStack; +/*---------------------------------------------------------------------*/ +pFileStack makeFileStack(){ + pFileStack pNew = NULL; + + pNew = malloc(sizeof(fileStack)); + if(pNew == NULL){ + return NULL; + } + memset(pNew,0,sizeof(fileStack)); + pNew->fileStackPointer = -1; + return pNew; +} +/*---------------------------------------------------------------------*/ +void killFileStack(pFileStack self){ + if(self != NULL){ + free(self); + } +} +/*----------------------------------------------------------------------*/ +void pushFileStack(pFileStack self, pNexusFunction pDriv, char *file){ + int length; + + self->fileStackPointer++; + self->fileStack[self->fileStackPointer].pDriver = pDriv; + memset(&self->fileStack[self->fileStackPointer].closeID,0,sizeof(NXlink)); + length = strlen(file); + if(length >= 1024){ + length = 1023; + } + memcpy(&self->fileStack[self->fileStackPointer].filename,file,length); +} +/*----------------------------------------------------------------------*/ +void popFileStack(pFileStack self){ + self->fileStackPointer--; + if(self->fileStackPointer < -1){ + self->fileStackPointer = -1; + } +} +/*----------------------------------------------------------------------*/ +pNexusFunction peekFileOnStack(pFileStack self){ + return self->fileStack[self->fileStackPointer].pDriver; +} +/*---------------------------------------------------------------------*/ +char *peekFilenameOnStack(pFileStack self){ + return self->fileStack[self->fileStackPointer].filename; +} +/*----------------------------------------------------------------------*/ +void peekIDOnStack(pFileStack self, NXlink *id){ + memcpy(id, &self->fileStack[self->fileStackPointer].closeID, sizeof(NXlink)); +} +/*---------------------------------------------------------------------*/ +void setCloseID(pFileStack self, NXlink id){ + memcpy(&self->fileStack[self->fileStackPointer].closeID, &id, sizeof(NXlink)); +} +/*----------------------------------------------------------------------*/ +int fileStackDepth(pFileStack self){ + return self->fileStackPointer; +} diff --git a/nxstack.h b/nxstack.h new file mode 100755 index 00000000..e7a9568b --- /dev/null +++ b/nxstack.h @@ -0,0 +1,43 @@ +/* + This is some code to handle a stack of NeXus files. This is used to implement + external linking within the NeXus-API + + Copyright (C) 1997-2006 Mark Koennecke + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + For further information, see +*/ +#ifndef NEXUSFILESTACK +#define NEXUSFILESTACK + +typedef struct __fileStack *pFileStack; +#define MAXEXTERNALDEPTH 16 + +pFileStack makeFileStack(); +void killFileStack(pFileStack self); + +void pushFileStack(pFileStack self, pNexusFunction pDriv, char *filename); +void popFileStack(pFileStack self); + +pNexusFunction peekFileOnStack(pFileStack self); +char *peekFilenameOnStack(pFileStack self); +void peekIDOnStack(pFileStack self, NXlink *id); +void setCloseID(pFileStack self, NXlink id); + +int fileStackDepth(pFileStack self); + +#endif + diff --git a/nxutil.c b/nxutil.c index 6ad7596f..07c74429 100644 --- a/nxutil.c +++ b/nxutil.c @@ -28,8 +28,9 @@ int iRet; /* Find the motor */ - strtolower(pName); - pMot = FindMotor(pSics,pName); + strncpy(pBueffel,pName,511); + strtolower(pBueffel); + pMot = FindMotor(pSics,pBueffel); if(!pMot) { sprintf(pBueffel,"WARNING: cannot find motor %s",pName); @@ -58,8 +59,9 @@ int iRet; /* Find the motor */ - strtolower(pName); - pMot = FindMotor(pSics,pName); + strncpy(pBueffel,pName,511); + strtolower(pBueffel); + pMot = FindMotor(pSics,pBueffel); if(!pMot) { sprintf(pBueffel,"WARNING: cannot find motor %s",pName); @@ -95,8 +97,9 @@ char *pText = NULL; /* find it */ - strtolower(pName); - pVar = FindVariable(pSics,pName); + strncpy(pBueffel,pName,511); + strtolower(pBueffel); + pVar = FindVariable(pSics,pBueffel); if(!pVar) { sprintf(pBueffel,"WARNING: cannot find variable %s",pName); @@ -112,7 +115,9 @@ VarGetText(pVar,&pText); if(pText != NULL) { - iRet = NXDputalias(hFil,pDict,pAlias,pText); + memset(pBueffel,0,511); + strncpy(pBueffel,pText,511); + iRet = NXDputalias(hFil,pDict,pAlias,pBueffel); free(pText); return iRet; } diff --git a/nxxml.c b/nxxml.c index bec44eef..e1ab96aa 100644 --- a/nxxml.c +++ b/nxxml.c @@ -2,7 +2,7 @@ * This is the implementation file for the XML file driver * for NeXus * - * Copyright (C) 2004 Mark Koennecke + * Copyright (C) 2006 Mark Koennecke * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,7 +18,7 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * For further information, see + * For further information, see */ #include #include @@ -31,6 +31,7 @@ extern void *NXpData; +char *nxitrim(char *str); /* from napi.c */ /*----------------------- our data structures -------------------------- One might wonder why a node stack is still needed even if this API @@ -243,6 +244,7 @@ static mxml_node_t *searchGroupLinks(pXMLNexus xmlHandle, CONSTCHAR *name, mxml_node_t *current; mxml_node_t *test = NULL; const char *linkTarget; + const char *linkName = NULL; current = xmlHandle->stack[xmlHandle->stackPointer].current; linkNode = current; @@ -257,6 +259,17 @@ static mxml_node_t *searchGroupLinks(pXMLNexus xmlHandle, CONSTCHAR *name, } } } + /* + test for named links + */ + linkName = mxmlElementGetAttr(linkNode,"name"); + if(test != NULL && linkName != NULL){ + if(strcmp(test->value.element.name,nxclass) == 0){ + if(strcmp(linkName, name) == 0){ + return test; + } + } + } } return NULL; } @@ -374,28 +387,26 @@ NXstatus NXXmakedata (NXhandle fid, return NX_ERROR; } if(dimensions[0] < 0){ - NXIReportError(NXpData, - "NeXus XML-API does not support unlimited dimensions"); - return NX_ERROR; + dimensions[0] = 1; } current = xmlHandle->stack[xmlHandle->stackPointer].current; + dataNode = mxmlNewElement(current,name); + typestring = buildTypeString(datatype,rank,dimensions); + if(typestring != NULL){ + mxmlElementSetAttr(dataNode,TYPENAME,typestring); + free(typestring); + } else { + NXIReportError(NXpData,"Failed to allocate typestring"); + return NX_ERROR; + } /* NX_CHAR maps to MXML_OPAQUE datasets */ - dataNode = mxmlNewElement(current,name); if(datatype == NX_CHAR){ newData = mxmlNewOpaque(dataNode,""); return NX_OK; } else { - typestring = buildTypeString(datatype,rank,dimensions); - if(typestring != NULL){ - mxmlElementSetAttr(dataNode,TYPENAME,typestring); - free(typestring); - } else { - NXIReportError(NXpData,"Failed to allocate typestring"); - return NX_ERROR; - } newData = (mxml_node_t *)malloc(sizeof(mxml_node_t)); if(!newData){ NXIReportError(NXpData,"Failed to allocate space for dataset"); @@ -419,6 +430,7 @@ static mxml_node_t *searchSDSLinks(pXMLNexus xmlHandle, CONSTCHAR *name){ mxml_node_t *current; mxml_node_t *test = NULL; const char *linkTarget; + const char *linkName = NULL; current = xmlHandle->stack[xmlHandle->stackPointer].current; linkNode = current; @@ -431,27 +443,18 @@ static mxml_node_t *searchSDSLinks(pXMLNexus xmlHandle, CONSTCHAR *name){ return test; } } + /* + test for named links + */ + linkName = mxmlElementGetAttr(linkNode,"name"); + if(test != NULL && linkName != NULL){ + if(strcmp(linkName,name) == 0){ + return test; + } + } } return NULL; } -/*-------------------------------------------------------------------*/ -static int strtrimcr(char *szStr, char *szSet) -{ - int i, j; /* Locale counters */ - - /*-------------------------------------------------*/ - - j = i = strlen(szStr) - 1; /* Find length of string */ - - while (strrchr(szSet, szStr[ i ]) - && (0 <= i)) - { - /* While string is terminated by one of the specified characters */ - szStr[ i-- ] = '\0'; /*- Replace character with '\0' */ - } - - return(j - i); /* Return the difference between old and new length */ -} /*-----------------------------------------------------------------------*/ NXstatus NXXopendata (NXhandle fid, CONSTCHAR *name){ pXMLNexus xmlHandle = NULL; @@ -523,7 +526,8 @@ NXstatus NXXputdata (NXhandle fid, void *data){ mxml_node_t *userData = NULL; mxml_node_t *current = NULL; pNXDS dataset; - int length; + int i, length, type, rank, dim[NX_MAXRANK]; + char *pPtr = NULL; xmlHandle = (pXMLNexus)fid; assert(xmlHandle); @@ -538,9 +542,28 @@ NXstatus NXXputdata (NXhandle fid, void *data){ assert(userData != NULL); if(userData->type == MXML_OPAQUE){ /* - text data + Text data. We have to make sure that the text is \0 terminated. + Some language bindings do not ensure that this is the case. */ - mxmlSetOpaque(userData,(const char *)data); + if(NXXgetinfo(fid,&rank, dim, &type) == NX_OK){ + length = 1; + for(i=0; ivalue.custom.data; assert(dataset); @@ -555,7 +578,7 @@ NXstatus NXXgetdata (NXhandle fid, void *data){ mxml_node_t *userData = NULL; mxml_node_t *current = NULL; pNXDS dataset; - int length; + int i, length, type, rank, dim[NX_MAXRANK]; xmlHandle = (pXMLNexus)fid; assert(xmlHandle); @@ -572,7 +595,17 @@ NXstatus NXXgetdata (NXhandle fid, void *data){ /* text data */ - strcpy((char *)data,userData->value.opaque); + if(NXXgetinfo(fid,&rank, dim, &type) == NX_OK){ + length = 1; + for(i=0; ivalue.opaque,length); + } else { + strcpy((char *)data,nxitrim(userData->value.opaque)); + } + } else { dataset = (pNXDS)userData->value.custom.data; assert(dataset); @@ -589,6 +622,7 @@ NXstatus NXXgetinfo (NXhandle fid, int *rank, mxml_node_t *current = NULL; pNXDS dataset; int myRank, i; + const char *attr = NULL; xmlHandle = (pXMLNexus)fid; assert(xmlHandle); @@ -605,9 +639,15 @@ NXstatus NXXgetinfo (NXhandle fid, int *rank, /* text data */ - *rank = 1; - *iType = NX_CHAR; - dimension[0]= strlen(userData->value.opaque); + attr = mxmlElementGetAttr(current, TYPENAME); + if(attr == NULL){ + *rank = 1; + *iType = NX_CHAR; + dimension[0]= strlen(userData->value.opaque); + } else { + analyzeDim(attr,rank,dimension,iType); + *iType = NX_CHAR; + } } else { dataset = (pNXDS)userData->value.custom.data; assert(dataset); @@ -670,6 +710,38 @@ static void putSlabData(pNXDS dataset, pNXDS slabData, int dim, } } } +/*---------------------------------------------------------------------- + This is in order to support unlimited dimensions along the first axis + -----------------------------------------------------------------------*/ +static int checkAndExtendDataset(mxml_node_t *node, pNXDS dataset, + int start[], int size[]){ + int dim0, byteLength; + void *oldData = NULL; + char *typestring = NULL; + + dim0 = start[0] + size[0]; + if(dim0 > dataset->dim[0]){ + byteLength = getNXDatasetByteLength(dataset); + oldData = dataset->u.ptr; + dataset->dim[0] = dim0; + dataset->u.ptr = malloc(getNXDatasetByteLength(dataset)); + if(dataset->u.ptr == NULL){ + return 0; + } + memset(dataset->u.ptr,0,getNXDatasetByteLength(dataset)); + memcpy(dataset->u.ptr,oldData,byteLength); + free(oldData); + typestring = buildTypeString(dataset->type,dataset->rank,dataset->dim); + if(typestring != NULL){ + mxmlElementSetAttr(node,TYPENAME,typestring); + free(typestring); + } else { + NXIReportError(NXpData,"Failed to allocate typestring"); + return 0; + } + } + return 1; +} /*----------------------------------------------------------------------*/ NXstatus NXXputslab (NXhandle fid, void *data, int iStart[], int iSize[]){ @@ -678,7 +750,7 @@ NXstatus NXXputslab (NXhandle fid, void *data, mxml_node_t *userData = NULL; mxml_node_t *current = NULL; pNXDS dataset, slabData; - int sourcePos[NX_MAXRANK], targetPos[NX_MAXRANK]; + int sourcePos[NX_MAXRANK], targetPos[NX_MAXRANK], status; xmlHandle = (pXMLNexus)fid; assert(xmlHandle); @@ -697,11 +769,20 @@ NXstatus NXXputslab (NXhandle fid, void *data, } dataset = (pNXDS)userData->value.custom.data; assert(dataset); + + status = checkAndExtendDataset(current,dataset,iStart,iSize); + if(status == 0){ + NXIReportError(NXpData,"Out of memory extending dataset"); + return NX_ERROR; + } + slabData = makeSlabData(dataset, data, iSize); if(slabData == NULL){ NXIReportError(NXpData,"Failed to allocate slab data"); return NX_ERROR; } + + putSlabData(dataset,slabData,0,iStart,sourcePos,targetPos); free(slabData->dim); free(slabData); @@ -803,14 +884,18 @@ static NXstatus NXXsetnumberformat(NXhandle fid, /*============================ Attributes ============================*/ static char *formatAttributeData(void *data, int datalen, int iType){ int intData = 0; - long iValue; - double dValue; + long iValue = -99999; + double dValue = -1e38; char type[20]; char *number; if(iType == NX_CHAR){ - return strdup((char *)data); +/* data may not be NULL terminated */ + number = (char*)malloc((datalen+1) * sizeof(char)); + memcpy(number, data, datalen * sizeof(char)); + number[datalen] = '\0'; + return number; } number = (char *)malloc(132*sizeof(char)); @@ -882,16 +967,8 @@ NXstatus NXXputattr (NXhandle fid, CONSTCHAR *name, void *data, xmlHandle = (pXMLNexus)fid; assert(xmlHandle); - if(!isDataNode(xmlHandle->stack[xmlHandle->stackPointer].current)){ - /* - global attribute - */ - current = xmlHandle->stack[0].current; - } else { - /* - dataset attribute - */ - current = xmlHandle->stack[xmlHandle->stackPointer].current; + current = xmlHandle->stack[xmlHandle->stackPointer].current; + if(isDataNode(xmlHandle->stack[xmlHandle->stackPointer].current)){ if(strcmp(name,TYPENAME) == 0){ NXIReportError(NXpData,"type is a reserved attribute name, rejected"); return NX_ERROR; @@ -922,17 +999,7 @@ NXstatus NXXgetattr (NXhandle fid, char *name, xmlHandle = (pXMLNexus)fid; assert(xmlHandle); - if(!isDataNode(xmlHandle->stack[xmlHandle->stackPointer].current)){ - /* - global attribute - */ - current = xmlHandle->stack[0].current; - } else { - /* - dataset attribute - */ - current = xmlHandle->stack[xmlHandle->stackPointer].current; - } + current = xmlHandle->stack[xmlHandle->stackPointer].current; attribute = mxmlElementGetAttr(current,name); if(!attribute){ @@ -1014,6 +1081,7 @@ NXstatus NXXgetnextentry (NXhandle fid,NXname name, const char *target = NULL, *attname = NULL; pNXDS dataset; char pBueffel[256]; + const char *linkName = NULL; xmlHandle = (pXMLNexus)fid; assert(xmlHandle); @@ -1045,6 +1113,7 @@ NXstatus NXXgetnextentry (NXhandle fid,NXname name, } if(strcmp(next->value.element.name,"NAPIlink") == 0){ target = mxmlElementGetAttr(next,"target"); + linkName = mxmlElementGetAttr(next,"name"); if(target == NULL){ NXIReportError(NXpData,"Corrupted file, NAPIlink without target"); return NX_ERROR; @@ -1078,6 +1147,12 @@ NXstatus NXXgetnextentry (NXhandle fid,NXname name, attname = mxmlElementGetAttr(next,"name"); strcpy(name,attname); } + /* + this is for named links + */ + if(linkName != NULL){ + strcpy(name,linkName); + } return NX_OK; } /*----------------------------------------------------------------------*/ @@ -1108,17 +1183,7 @@ NXstatus NXXgetnextattr (NXhandle fid, NXname pName, xmlHandle = (pXMLNexus)fid; assert(xmlHandle); - if(isDataNode(xmlHandle->stack[xmlHandle->stackPointer].current)){ - /* - dataset attribute - */ - stackPtr = xmlHandle->stackPointer; - } else { - /* - global attribute - */ - stackPtr = 0; - } + stackPtr = xmlHandle->stackPointer; current = xmlHandle->stack[stackPtr].current; currentAtt = xmlHandle->stack[stackPtr].currentAttribute; @@ -1128,10 +1193,20 @@ NXstatus NXXgetnextattr (NXhandle fid, NXname pName, return NX_EOD; } + /* + hide group name attribute + */ + if(strcmp(current->value.element.attrs[currentAtt].name,"name") == 0 + && !isDataNode(current) ){ + xmlHandle->stack[stackPtr].currentAttribute++; + return NXXgetnextattr(fid,pName,iLength,iType); + } + /* hide type attribute */ - if(strcmp(current->value.element.attrs[currentAtt].name,TYPENAME) == 0){ + if(strcmp(current->value.element.attrs[currentAtt].name,TYPENAME) == 0 + && isDataNode(current)){ xmlHandle->stack[stackPtr].currentAttribute++; return NXXgetnextattr(fid,pName,iLength,iType); } @@ -1161,18 +1236,7 @@ extern NXstatus NXXinitattrdir(NXhandle fid){ xmlHandle = (pXMLNexus)fid; assert(xmlHandle); - if(isDataNode(xmlHandle->stack[xmlHandle->stackPointer].current)){ - /* - dataset attribute - */ - stackPtr = xmlHandle->stackPointer; - } else { - /* - global attribute - */ - stackPtr = 0; - } - + stackPtr = xmlHandle->stackPointer; xmlHandle->stack[stackPtr].currentAttribute = 0; return NX_OK; } @@ -1218,22 +1282,17 @@ NXstatus NXXgetattrinfo (NXhandle fid, int *iN){ xmlHandle = (pXMLNexus)fid; assert(xmlHandle); - if(isDataNode(xmlHandle->stack[xmlHandle->stackPointer].current)){ - /* - dataset attribute - */ - stackPtr = xmlHandle->stackPointer; - } else { - /* - global attribute - */ - stackPtr = 0; - } + stackPtr = xmlHandle->stackPointer; current = xmlHandle->stack[stackPtr].current; + /* - hide type attribute + hide type and group name attributes */ + if(!isDataNode(current)) { + *iN = current->value.element.num_attrs -1; + return NX_OK; + } if(mxmlElementGetAttr(current,TYPENAME) != NULL){ *iN = current->value.element.num_attrs -1; } else { @@ -1413,6 +1472,34 @@ NXstatus NXXmakelink (NXhandle fid, NXlink* sLink){ } return NX_OK; } + +/*-----------------------------------------------------------------------*/ +NXstatus NXXmakenamedlink (NXhandle fid, CONSTCHAR *name, NXlink* sLink){ + pXMLNexus xmlHandle = NULL; + mxml_node_t *current = NULL, *linkNode = NULL; + mxml_node_t *linkedNode = NULL; + + xmlHandle = (pXMLNexus)fid; + assert(xmlHandle); + + if(isDataNode(xmlHandle->stack[xmlHandle->stackPointer].current)){ + NXIReportError(NXpData,"No group to link to open"); + return NX_ERROR; + } + current = xmlHandle->stack[xmlHandle->stackPointer].current; + linkNode = mxmlNewElement(current,"NAPIlink"); + if(!linkNode){ + NXIReportError(NXpData,"Failed to allocate new link element"); + return NX_ERROR; + } + mxmlElementSetAttr(linkNode,"target",sLink->targetPath); + mxmlElementSetAttr(linkNode,"name",name); + linkedNode = getLinkTarget(xmlHandle,sLink->targetPath); + if(linkedNode != NULL){ + mxmlElementSetAttr(linkedNode,"target",sLink->targetPath); + } + return NX_OK; +} /*----------------------------------------------------------------------*/ NXstatus NXXsameID (NXhandle fileid, NXlink* pFirstID, NXlink* pSecondID){ @@ -1444,6 +1531,7 @@ void NXXassignFunctions(pNexusFunction fHandle){ fHandle->nxputslab=NXXputslab; fHandle->nxgetdataID=NXXgetdataID; fHandle->nxmakelink=NXXmakelink; + fHandle->nxmakenamedlink=NXXmakenamedlink; fHandle->nxgetdata=NXXgetdata; fHandle->nxgetinfo=NXXgetinfo; fHandle->nxgetnextentry=NXXgetnextentry; diff --git a/obdes.c b/obdes.c index 64c16800..127e56bc 100644 --- a/obdes.c +++ b/obdes.c @@ -44,6 +44,7 @@ #include "obdes.h" #include "conman.h" #include "servlog.h" +#include "hipadaba.h" /*-------------------------------------------------------------------------*/ static void *DefaultGetInterface(void *pData,int iID) @@ -67,6 +68,7 @@ } pRes->name = strdup(name); pRes->pKeys = NULL; + pRes->parNode = NULL; pRes->SaveStatus = DefaultSave; pRes->GetInterface = DefaultGetInterface; return pRes; @@ -77,6 +79,14 @@ assert(self); if(self->name) free(self->name); if(self->pKeys) IFDeleteOptions(self->pKeys); + /* + * delate a parameter node only when not linked elsewhere + */ + if(self->parNode != NULL){ + if(self->parNode->mama == NULL){ + DeleteHipadabaNode(self->parNode,NULL); + } + } free(self); } @@ -146,7 +156,7 @@ { if(NULL!=self) { - IFSetOption(self->pKeys,keyName,eltValue); + self->pKeys = IFSetOption(self->pKeys,keyName,eltValue); } } /*--------------------------------------------------------------------------*/ @@ -156,7 +166,7 @@ { return; } - IFSetOption(self->pKeys,"group",group); + self->pKeys = IFSetOption(self->pKeys,"group",group); } /*--------------------------------------------------------------------------*/ void SetDescriptorDescription(pObjectDescriptor self, char *description) @@ -165,7 +175,7 @@ { return; } - IFSetOption(self->pKeys,"description", description); + self->pKeys = IFSetOption(self->pKeys,"description", description); } /*--------------------------------------------------------------------------*/ char * GetDescriptorKey(pObjectDescriptor self, char *keyName) diff --git a/obdes.h b/obdes.h index d82dc49d..f842a2e9 100644 --- a/obdes.h +++ b/obdes.h @@ -23,12 +23,14 @@ #define SICSDESCRIPTOR #include #include +#include typedef struct { char *name; int (*SaveStatus)(void *self, char *name,FILE *fd); void *(*GetInterface)(void *self, int iInterfaceID); IPair *pKeys; + pHdb parNode; } ObjectDescriptor, *pObjectDescriptor; /*---------------------------------------------------------------------------*/ diff --git a/ofac.c b/ofac.c index 4f653561..1bc2c26a 100644 --- a/ofac.c +++ b/ofac.c @@ -6,7 +6,7 @@ - Mark Koennecke, November 1996 -- ???? + Mark Koennecke, November 1996 -- ???? heavy modifications to separate PSI specific commands into a separate library. Mark Koennecke, June 2003 @@ -56,6 +56,7 @@ #include "servlog.h" #include "nserver.h" #include "status.h" +#include "statusfile.h" #include "configfu.h" #include "devexec.h" #include "mumo.h" @@ -118,6 +119,16 @@ #include "sinfox.h" #include "sicslist.h" #include "cone.h" +#include "sicshipadaba.h" +#include "multicounter.h" +#include "sicspoll.h" +#include "statemon.h" +#include "asyncqueue.h" +#include "asyncprotocol.h" +#include "sicsobj.h" +#include "hdbqueue.h" +#include "genericcontroller.h" +#include "proxy.h" /*----------------------- Server options creation -------------------------*/ static int IFServerOption(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) @@ -199,6 +210,7 @@ assert(pInter); pExe = CreateExeList(pTask); + pServ->pExecutor = pExe; pEnv = CreateEnvMon(); assert(pExe); @@ -207,9 +219,10 @@ /* permanent commands */ AddCommand(pInter,"Sics_Exitus",SicsExit,NULL,NULL); AddCommand(pInter,"FileEval",MacroFileEval,NULL,NULL); + AddCommand(pInter,"InternEval",InternalFileEval,NULL,NULL); - AddCommand(pInter,"FileWhere",MacroWhere,WhereKill,NULL); AddCommand(pInter,"ClientPut",ClientPut,NULL,NULL); + AddCommand(pInter,"GumPut",GumPut,NULL,NULL); AddCommand(pInter,"broadcast",Broadcast,NULL,NULL); AddCommand(pInter,"transact",TransactAction,NULL,NULL); AddCommand(pInter,"fulltransact",TransactAction,NULL,NULL); @@ -221,8 +234,6 @@ AddCommand(pInter,"status", UserStatus,NULL,NULL); AddCommand(pInter,"ResetServer",ResetStatus,NULL,NULL); AddCommand(pInter,"Dir",ListObjects,NULL,NULL); - AddCommand(pInter,"Backup",BackupStatus,NULL,NULL); - AddCommand(pInter,"Restore",RestoreStatus,NULL,NULL); AddCommand(pInter,"SetInt", SetSICSInterrupt,NULL,NULL); AddCommand(pInter,"GetInt",GetSICSInterrupt,NULL,NULL); AddCommand(pInter,"SICSType",SICSType,NULL,NULL); @@ -237,11 +248,15 @@ AddCommand(pInter,"commandlog",CommandLog,CommandLogClose,NULL); AddCommand(pInter,"udpquieck",QuieckAction,KillQuieck,NULL); AddCommand(pInter,"alias",MakeAlias,NULL,NULL); + AddCommand(pInter,"findalias",LocateAliasAction,NULL,NULL); AddCommand(pInter,"sicscron",MakeCron,NULL,NULL); + AddCommand(pInter,"dolater",MakeCron,NULL,NULL); AddCommand(pInter,"sicsdatafactory",SICSDataFactory,NULL,NULL); AddCommand(pInter,"scriptcallback",CallbackScript,NULL,NULL); AddCommand(pInter,"help",SicsHelp,KillHelp,NULL); AddCommand(pInter,"list",SicsList,NULL,NULL); + AddCommand(pInter,"InstallHdb",InstallSICSHipadaba,NULL,NULL); + MakeProtocol(pInter); /* commands to do with the executor. Only StopExe carries the DeleteFunction in order to avoid double deletion. All the @@ -268,7 +283,7 @@ AddCommand(pInter,"MakeEnergy",MakeEnergyVar,NULL,NULL); AddCommand(pInter,"MakeCounter",MakeCounter,NULL,NULL); AddCommand(pInter,"MakeO2T",CreateO2T,NULL,NULL); - AddCommand(pInter,"SicsAlias",SicsAlias,NULL,NULL); + AddCommand(pInter,"SicsAlias",SicsAlias,NULL,NULL); AddCommand(pInter,"DefineAlias",DefineAlias,NULL,NULL); /* M.Z. */ AddCommand(pInter,"MakeHM",MakeHistMemory,NULL,NULL); AddCommand(pInter,"VelocitySelector",VelSelFactory,NULL,NULL); @@ -317,12 +332,24 @@ McStasReaderFactory,NULL,NULL); AddCommand(pInter,"MakeMcStasController", McStasControllerFactory,NULL,NULL); - AddCommand(pInter,"InstallProtocolHandler", - InstallProtocol,NULL,NULL); AddCommand(pInter,"InstallSinfox", InstallSinfox,NULL,NULL); AddCommand(pInter,"MakeCone", MakeCone,NULL,NULL); + AddCommand(pInter,"MakeMultiCounter", + MakeMultiCounter,NULL,NULL); + AddCommand(pInter,"MakeSicsPoll", + InstallSICSPoll,NULL,NULL); + AddCommand(pInter,"MakeStateMon", + StateMonFactory,NULL,NULL); + AddCommand(pInter,"MakeAsyncProtocol",AsyncProtocolFactory,NULL,NULL); + AddCommand(pInter,"MakeAsyncQueue",AsyncQueueFactory,NULL,NULL); + AddCommand(pInter,"MakeSicsObj",InstallSICSOBJ,NULL,NULL); + AddCommand(pInter,"DynSicsObj",InstallSICSOBJ,NULL,NULL); +/* AddCommand(pInter,"MakeHdbQueue",MakeHDBQueue,NULL,NULL); */ + AddCommand(pInter,"MakeGenController",GenControllerFactory,NULL,NULL); + AddCommand(pInter,"genconfigure",GenControllerConfigure,NULL,NULL); + AddCommand(pInter,"MakeProxy",ProxyFactory,NULL,NULL); /* install site specific commands @@ -371,7 +398,7 @@ RemoveCommand(pSics,"MakeLin2Ang"); RemoveCommand(pSics,"MakeSync"); RemoveCommand(pSics,"MakeHMControl"); - /* RemoveCommand(pSics,"MakeRS232Controller"); */ + RemoveCommand(pSics,"MakeRS232Controller"); RemoveCommand(pSics,"MakeMaxDetector"); RemoveCommand(pSics,"AntiColliderInstall"); RemoveCommand(pSics,"MakeGPIB"); @@ -388,9 +415,16 @@ RemoveCommand(pSics,"MakeTasUB"); RemoveCommand(pSics,"MakeTasScan"); RemoveCommand(pSics,"MakemcStasReader"); - RemoveCommand(pSics,"InstallProtocolHandler"); RemoveCommand(pSics,"InstallSinfox"); RemoveCommand(pSics,"MakeCone"); + RemoveCommand(pSics,"MakeMultiCounter"); + RemoveCommand(pSics,"MakeStateMon"); + RemoveCommand(pSics,"MakeAsyncQueue"); + RemoveCommand(pSics,"MakeAsyncProtocol"); + RemoveCommand(pSics,"MakeSicsObject"); + RemoveCommand(pSics,"MakeGenController"); + RemoveCommand(pSics,"genconfigure"); + RemoveCommand(pSics,"MakeProxy"); /* remove site specific installation commands */ @@ -406,9 +440,17 @@ /* insert here initialization routines ... */ - INIT(SiteInit); /* site specific initializations */ + INIT(StatisticsInit); + INIT(InitializerInit); + INIT(SaveHdbInit); /* must be after InitializerInit */ + INIT(SctInit); + INIT(SctDriveInit); + INIT(LogReaderInit); + INIT(LogSetupInit); + INIT(StatusFileInit); - } + INIT(SiteInit); /* site specific initializations */ + } /*--------------------------------------------------------------------------*/ int InitObjectCommands(pServer pServ, char *file) { @@ -423,18 +465,16 @@ InitGeneral(); /* general initialization */ - InitIniCommands(pSics,pServ->pTasker); - /* create a connection */ pCon = SCCreateDummyConnection(pSics); if(!pCon) { return 0; } - /* removed (gives double output to stdout) - pCon->iFiles = 1; - pCon->pFiles[0] = stdout; - */ + MakeExeManager(pCon,pSics,NULL,1, NULL); + InitIniCommands(pSics,pServ->pTasker); + InstallBckRestore(pCon,pSics); + pCon->iFiles = 0; /* evaluate the file */ diff --git a/optimise.c b/optimise.c index d74cae09..64afe2cb 100644 --- a/optimise.c +++ b/optimise.c @@ -22,6 +22,7 @@ #include "scan.i" #include "fitcenter.h" #include "optimise.h" +#include "stdscan.h" #ifdef CYGNUS #define MAXFLOAT 9999999.99 @@ -83,7 +84,6 @@ { return 1; } - /*------------------------------------------------------------------------*/ pOptimise CreateOptimiser(pCounter pCount) @@ -308,22 +308,7 @@ { DynarGet(self->pVariables,i,&pData); pOVar = (pOVarEntry)pData; - if(FindMotor(pServ->pSics,pOVar->pName) != NULL) - { - iRet = MotorGetSoftPosition(pOVar->pData,pCon,&fVal); - if(iRet == 1) - { - pOVar->fCenter = fVal; - } - else - { - pOVar->fCenter = -1000.; - } - } - else - { - pOVar->fCenter = pOVar->pDriv->GetValue(pOVar->pData,pCon); - } + pOVar->fCenter = pOVar->pDriv->GetValue(pOVar->pData,pCon); if(pOVar->fCenter < -900.) { return SCANERROR; @@ -466,6 +451,209 @@ return iReturn; } +/*------------------------------------------------------------------------ + * We use the scan object here for counting. The reason is that this + * handles well in the common case of a single counter but also has + * provisions for other counting methods through the scan modules + * scripting mechanism + * ------------------------------------------------------------------------*/ +static long ClimbCount(pOptimise self, SConnection *pCon) +{ + int status; + long data[1]; + int (*CollectFunc)(pScanData self, int iPoint) = NULL; + + SilentPrepare(self->pScanner); + + status = self->pScanner->ScanCount(self->pScanner,0); + if(status != 1) + { + return status; + } + if(self->pScanner->CollectScanData == CollectScanData) + { + CollectFunc = self->pScanner->CollectScanData; + self->pScanner->CollectScanData = CollectSilent; + } + status = self->pScanner->CollectScanData(self->pScanner,0); + if(CollectFunc != NULL) + { + self->pScanner->CollectScanData = CollectFunc; + } + if(status != 1) + { + return status; + } + GetScanCounts(self->pScanner,data,1); + return data[0]; +} +/*------------------------------------------------------------------------*/ +static int ClimbDrive(SConnection *pCon,char *name, float value) +{ + int status; + + status = Start2Run(pCon,pServ->pSics,name,value); + if(status != 1) + { + return DRIVEERROR; + } + status = Wait4Success(GetExecutor()); + if(status != DEVDONE) + { + return DRIVEERROR; + } + return 1; +} +/*------------------------------------------------------------------------*/ +static int findDirection(pOptimise self, pOVarEntry pOvar, SConnection *pCon) +{ + int status, direction; + float varValue; + long oneCount, twoCount; + + varValue = pOvar->fCenter + pOvar->fStep; + status = ClimbDrive(pCon,pOvar->pName,varValue); + if(!status) + { + return DRIVEERROR; + } + oneCount = ClimbCount(self,pCon); + if(oneCount < 0) + { + return SCANERROR; + } + if(SCGetInterrupt(pCon) != eContinue) + { + return SCANABORT; + } + varValue = pOvar->fCenter - pOvar->fStep; + status = ClimbDrive(pCon,pOvar->pName,varValue); + if(!status) + { + return DRIVEERROR; + } + twoCount = ClimbCount(self,pCon); + if(SCGetInterrupt(pCon) != eContinue) + { + return SCANABORT; + } + if(twoCount < 0) + { + return SCANERROR; + } + if(oneCount > twoCount) + { + direction = 1; + } + else + { + direction = -1; + } + return direction; +} +/*-------------------------------------------------------------------------*/ + static int ClimbVariable(pOptimise self, SConnection *pCon, int i) + { + pOVarEntry pOvar; + void *pData; + int status, direction = 1; + long oneCount, twoCount, lastCount, currentCount; + float varValue, startValue; + char buffer[256]; + int (*CollectFunc)(pScanData self, int iPoint) = NULL; + + assert(self); + assert( (i >= 0) && (i < self->iVar)); + assert(pCon); + + /* get variable data */ + DynarGet(self->pVariables,i,&pData); + pOvar = (pOVarEntry)pData; + startValue = pOvar->fCenter; + + /* + * prepare scan object + */ + self->pScanner->pCon = pCon; + self->pScanner->pSics = pServ->pSics; + self->pScanner->iNP = 1; + self->pScanner->iMode = self->eCount; + self->pScanner->fPreset = self->fPreset; + + direction = findDirection(self,pOvar, pCon); + if(direction < -1){ + return direction; + } + /* + * drive to the last best position + */ + varValue = pOvar->fCenter + direction*pOvar->fStep; + status = ClimbDrive(pCon,pOvar->pName,varValue); + if(!status) + { + return DRIVEERROR; + } + lastCount = ClimbCount(self,pCon); + if(lastCount < 0) + { + return SCANERROR; + } + currentCount = lastCount; + + /* + * climb upwards as long as possible + */ + while(1) + { + pOvar->fCenter = varValue; + varValue = pOvar->fCenter + direction * pOvar->fStep; + status = ClimbDrive(pCon,pOvar->pName,varValue); + if(!status) + { + return DRIVEERROR; + } + if(SCGetInterrupt(pCon) != eContinue) + { + return SCANABORT; + } + currentCount = ClimbCount(self,pCon); + if(currentCount < 0) + { + return SCANERROR; + } + if(SCGetInterrupt(pCon) != eContinue) + { + return SCANABORT; + } + snprintf(buffer,255,"Climbing %s, value = %f, count = %ld", + pOvar->pName, varValue, currentCount); + SCWrite(pCon,buffer,eWarning); + + if(currentCount <= lastCount) + { + /* + * we are finished. Drive to previous position and + * break + */ + status = ClimbDrive(pCon,pOvar->pName,pOvar->fCenter); + if(!status) + { + return DRIVEERROR; + } + break; + } + else + { + /* + * go on, we are not over the top yet + */ + lastCount = currentCount; + } + } + pOvar->fShift = ABS(startValue - pOvar->fCenter); + + return 1; + } /*---------------------------------------------------------------------------*/ static int CheckSuccess(pOptimise self) { @@ -530,6 +718,45 @@ } return MAXCYCLE; } +/*---------------------------------------------------------------------------*/ + int OptimiserClimb(pOptimise self, SConnection *pCon) + { + int i, iRet, iCycle, iRedoVar = 0; + char pBueffel[256]; + + assert(self); + + if(self->iVar < 1) + { + SCWrite(pCon,"ERROR: Nothing to optimise",eError); + return 0; + } + + iRet = OptimiserInit(self,pCon); + if(!iRet) + { + return iRet; + } + for(iCycle = 0; iCycle < self->iMaxCycles; iCycle++) + { + sprintf(pBueffel,"Optimiser cycle %d of %d started",iCycle, self->iMaxCycles); + SCWrite(pCon,pBueffel,eStatus); + for(i = iRedoVar; i < self->iVar; i++) + { + iRet = ClimbVariable(self,pCon,i); + if(iRet <= 0) + { + return iRet; + } + } + iRet = CheckSuccess(self); + if(iRet) + { + return 1; + } + } + return MAXCYCLE; + } /*--------------------------------------------------------------------------*/ int MakeOptimiser(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) @@ -717,6 +944,52 @@ break; } } +/*-------- climb */ + else if(strcmp(argv[1],"climb") == 0) + { + /* check rights */ + if(!SCMatchRights(pCon,usUser)) + { + SCWrite(pCon,"ERROR: You are not aurhorised to do this!",eError); + return 0; + } + iRet = OptimiserClimb(self,pCon); + switch(iRet) + { + case PEAKLOST: + SCWrite(pCon,"ERROR: lost the peak, sorry!",eError); + return 0; + break; + case MAXCYCLE: + sprintf(pBueffel,"ERROR: could not optimise peak in %d cycles", + self->iMaxCycles); + SCWrite(pCon,pBueffel,eError); + return 0; + break; + case SCANERROR: + SCWrite(pCon,"ERROR: failed to scan the peak",eError); + return 0; + break; + case SCANABORT: + SCWrite(pCon,"ERROR: Scan was aborted, Optimiser follows",eError); + return 0; + break; + case DRIVEERROR: + SCWrite(pCon,"ERROR: Failure to drive variable to new position",eError); + return 0; + break; + case 1: + SCWrite(pCon,"At long last, I finished optimising the peak",eValue); + return 1; + break; + default: + SCWrite(pCon,"ERROR: Unidentified error krept into Optimiser",eError); + return 0; + break; + } + SCWrite(pCon,"Optimiser climbed successfully",eValue); + return 1; + } /* ------ count mode */ if(strcmp(argv[1],"countmode") == 0) { diff --git a/oscillate.c b/oscillate.c index ecd844d5..09be9e24 100644 --- a/oscillate.c +++ b/oscillate.c @@ -170,7 +170,6 @@ static int StartOscillation(pOscillator self, SConnection *pCon){ static void KillOscillator(void *data){ pOscillator self = (pOscillator)data; if(self != NULL){ - StopOscillation(self); if(self->pDes != NULL){ DeleteDescriptor(self->pDes); } diff --git a/outcode.c b/outcode.c index e52f6f58..ad7fa0ee 100644 --- a/outcode.c +++ b/outcode.c @@ -20,6 +20,8 @@ "event", "warning", "error", + "hdbvalue", + "hdbevent", NULL }; - static int iNoCodes = 10; + static int iNoCodes = 13; #endif diff --git a/polldriv.c b/polldriv.c new file mode 100644 index 00000000..da2f5119 --- /dev/null +++ b/polldriv.c @@ -0,0 +1,153 @@ +/** + * This is the sister module to sicspoll which defines the drivers for the + * various modes of polling SICS objects. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, November-December 2006 + */ + +#include +#include +#include +#include + +#include "polldriv.h" +#include "macro.h" +#include "sicshipadaba.h" +/*================ actual driver implementation =========================*/ +static int timeDue(struct __POLLDRIV *self, time_t now, SConnection *pCon){ + if(now > self->nextPoll){ + return 1; + } else { + return 0; + } +} +/*------------------ HDB Driver -----------------------------------------*/ +static int pollHdb(struct __POLLDRIV *self, SConnection *pCon){ + hdbValue old, newVal; + pHdb node = NULL; + + memset(&old,0,sizeof(hdbValue)); + memset(&newVal,0,sizeof(hdbValue)); + node = (pHdb)self->objPointer; + assert(node != NULL); + old = node->value; + self->nextPoll = time(NULL) + self->pollIntervall; + if(GetHipadabaPar(node, &newVal, pCon) == 1){ + if(!compareHdbValue(old,newVal)){ + UpdateHipadabaPar(node,newVal,pCon); + } + ReleaseHdbValue(&newVal); + return 1; + } else { + return 0; + } +} +/*-----------------------------------------------------------------------*/ +static pPollDriv makeHdbDriver(SConnection *pCon, char *objectIdentifier, + int argc, char *argv[]){ + pHdb node = NULL; + pPollDriv pNew = NULL; + + node = FindHdbNode(NULL,objectIdentifier,pCon); + if(node == NULL){ + SCWrite(pCon,"ERROR: object to poll not found",eError); + return 0; + } + pNew = malloc(sizeof(PollDriv)); + if(pNew == NULL){ + return NULL; + } + memset(pNew,0,sizeof(PollDriv)); + + + pNew->objectIdentifier = strdup(objectIdentifier); + pNew->objPointer = node; + pNew->isDue = timeDue; + pNew->poll = pollHdb; + + if(argc > 0){ + pNew->pollIntervall = atoi(argv[0]); + } else { + pNew->pollIntervall = 10; + } + + return pNew; +} +/*==================== script poll driver ========================*/ +static int pollScript(struct __POLLDRIV *self, SConnection *pCon){ + int status; + Tcl_Interp *pTcl = InterpGetTcl(pServ->pSics); + + self->nextPoll = time(NULL) + self->pollIntervall; + + MacroPush(pCon); + status = Tcl_Eval(pTcl,(char *)self->objPointer); + MacroPop(); + if(status == 0){ + return 1; + } else { + return 0; + } +} +/*-----------------------------------------------------------------------*/ +static void killScriptObj(void *data){ + if(data != NULL){ + free(data); + } +} +/*-----------------------------------------------------------------------*/ +static pPollDriv makeScriptDriver(SConnection *pCon, char *objectIdentifier, + int argc, char *argv[]){ + pPollDriv pNew = NULL; + char scriptBuffer[512]; + + if(argc < 2){ + SCWrite(pCon, + "ERROR: need intervall and script parameter for script polling driver", eError); + return NULL; + } + + pNew = malloc(sizeof(PollDriv)); + if(pNew == NULL){ + return NULL; + } + memset(pNew,0,sizeof(PollDriv)); + + pNew->pollIntervall = atoi(argv[0]); + memset(scriptBuffer,0,512); + Arg2Text(argc-1, &argv[1],scriptBuffer,511); + + pNew->objectIdentifier = strdup(objectIdentifier); + pNew->objPointer = strdup(scriptBuffer); + pNew->isDue = timeDue; + pNew->poll = pollScript; + pNew->killObjPointer = killScriptObj; + + return pNew; +} +/*================ external interface ====================================*/ +pPollDriv makePollDriver(SConnection *pCon, char *driver, + char *objectIdentifier, int argc, char *argv[]){ + + strtolower(driver); + if(strcmp(driver,"hdb") == 0) { + return makeHdbDriver(pCon,objectIdentifier, argc, argv); + } else if(strcmp(driver,"script") == 0){ + return makeScriptDriver(pCon,objectIdentifier, argc, argv); + } else { + SCWrite(pCon,"ERROR: polling driver type unknown",eError); + return NULL; + } +} +/*------------------------------------------------------------------------*/ +void deletePollDriv(pPollDriv self){ + if(self->objectIdentifier != NULL){ + free(self->objectIdentifier); + } + if(self->objPointer != NULL && self->killObjPointer != NULL){ + self->killObjPointer(self->objPointer); + } + free(self); +} diff --git a/polldriv.h b/polldriv.h new file mode 100644 index 00000000..409afb2d --- /dev/null +++ b/polldriv.h @@ -0,0 +1,44 @@ +/** + * This is the sister module to sicspoll which defines the drivers for the + * various modes of polling SICS objects. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, November-December 2006 + */ +#ifndef POLLDRIV_H_ +#define POLLDRIV_H_ +#include +#include +/*==================== a data structure ===================================*/ +typedef struct __POLLDRIV{ + char *objectIdentifier; /* the object identifier */ + void *objPointer; /* a pointer to the object */ + time_t nextPoll; /* next polling time */ + int pollIntervall; /* poll intervall */ + int (*isDue)(struct __POLLDRIV *self, time_t now, SConnection *pCon); + /* function called to determine if this object must be polled */ + int (*poll)(struct __POLLDRIV *self, SConnection *pCon); + /* the actual polling function */ + void (*killObjPointer)(void *data); + /* a function to possibly kill the objPointer. Can be NULL */ +}PollDriv, *pPollDriv; +/*==================== the interface =====================================*/ +/* + * make a poll driver + * @param pCon A connection to report errors too + * @param driver the driver type to generate + * @param objectIdentifier An identifer for the object to poll + * @param argc number of additional parameter + * @param *argv[] Additional parameters. + * @return NULL on failure or a PollDriv strucure else. + */ +pPollDriv makePollDriver(SConnection *pCon, char *driver, + char *objectIdentifier, int argc, char *argv[]); +/** + * free all memory associated with this poll driver + * @param self The structure to delete + */ +void deletePollDriv(pPollDriv self); + +#endif /*POLLDRIV_H_*/ diff --git a/polldriv.tc b/polldriv.tc new file mode 100644 index 00000000..42b82fcb --- /dev/null +++ b/polldriv.tc @@ -0,0 +1,87 @@ +/** + * This is the sister module to sicspoll which defines the drivers for the + * various modes of polling SICS objects. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, November-December 2006 + */ +<%! source sicstemplates.tcl %> +<% stdIncludes %> +#include "polldriv.h" +#include "splitter.h" +#include "sicshipadaba.h" +/*================ actual driver implementation =========================*/ +static int timeDue(struct __POLLDRIV *self, time_t now, SConnection *pCon){ + if(now > self->nextPoll){ + return 1; + } else { + return 0; + } +} +/*------------------ HDB Driver -----------------------------------------*/ +static int pollHdb(struct __POLLDRIV *self, SConnection *pCon){ + hdbValue old, newVal; + pHdb node = NULL; + + memset(&old,0,sizeof(hdbValue)); + memset(&newVal,0,sizeof(hdbValue)); + node = (pHdb)self->objPointer; + assert(node != NULL); + old = node->value; + self->nextPoll = time(NULL) + self->pollIntervall; + if(GetHipadabaPar(node, &newVal, pCon) == 1){ + if(!compareHdbValue(old,newVal)){ + UpdateHipadabaPar(node,newVal,pCon); + } + ReleaseHdbValue(&newVal); + return 1; + } else { + return 0; + } +} +/*-----------------------------------------------------------------------*/ +static pPollDriv makeHdbDriver(SConnection *pCon, char *objectIdentifier, + int argc, char *argv[]){ + pHdb node = NULL; + pPollDriv pNew = NULL; + + node = locateSICSNode(pServ->pSics,pCon,objectIdentifier); + if(node == NULL){ + SCWrite(pCon,"ERROR: object to poll not found",eError); + return 0; + } + <%newStruc PollDriv 5 %> + + pNew->objectIdentifier = strdup(objectIdentifier); + pNew->objPointer = node; + pNew->isDue = timeDue; + pNew->poll = pollHdb; + + if(argc > 0){ + pNew->pollIntervall = atoi(argv[0]); + } else { + pNew->pollIntervall = 10; + } + + return pNew; +} +/*================ external interface ====================================*/ +pPollDriv makePollDriver(SConnection *pCon, char *driver, + char *objectIdentifier, int argc, char *argv[]){ + + strtolower(driver); + if(strcmp(driver,"hdb") == 0) { + return makeHdbDriver(pCon,objectIdentifier, argc, argv); + } else { + SCWrite(pCon,"ERROR: polling driver type unknown",eError); + return NULL; + } +} +/*------------------------------------------------------------------------*/ +void deletePollDriv(pPollDriv self){ + if(self->objectIdentifier != NULL){ + free(self->objectIdentifier); + } + free(self); +} diff --git a/protocol.c b/protocol.c index 22b2e97e..909ac1ca 100644 --- a/protocol.c +++ b/protocol.c @@ -16,11 +16,13 @@ #include #include "commandlog.h" #include "protocol.h" +#include #define MAXMSG 1024 #define INIT_STR_SIZE 256 #define STR_RESIZE_LENGTH 256 - +#define NUMPROS 6 +#define PROLISTLEN 7 typedef struct __Protocol { pObjectDescriptor pDes; /* required as first field */ char *name; /* protocol handler name */ @@ -28,7 +30,7 @@ typedef struct __Protocol { int iNumPros; /* number of valid protocols? */ writeFunc defaultWriter; /* default write function */ int isDefaultSet; - char *pProList[5]; /* list of valid protocols? */ + char *pProList[PROLISTLEN]; /* list of valid protocols? */ } Protocol; char *pEventType[]={ @@ -85,8 +87,6 @@ pProtocol CreateProtocol(void); static int ProtocolOptions(SConnection* pCon, pProtocol pPro); static int ProtocolHelp(SConnection* pCon, Protocol* pPro); static int ProtocolSet(SConnection* pCon, Protocol* pPro, char *pProName); -static int ProtocolGet(SConnection* pCon, Protocol* pPro, char *pProName, - int *pIndex); static int ProtocolList(SConnection* pCon, Protocol* pPro); int ProtocolAction(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); @@ -95,15 +95,18 @@ static int InitDefaultProtocol(SConnection* pCon, Protocol *pPro); /* Signatures for protocol writers implemented in this file */ int SCWriteSycamore(SConnection *pCon, char *pBuffer, int iOut); +int SCWriteJSON_String(SConnection *pCon, char *pBuffer, int iOut); /*--------------------------------------------------------------------------*/ pProtocol CreateProtocol(void) { - int i, iNumPros = 4; - char *pPros[5] = {"default", + int i, iNumPros = NUMPROS; + char *pPros[] = {"default", "normal", "withcode", "sycamore", + "json", + "act", NULL }; pProtocol pNew = NULL; @@ -182,8 +185,8 @@ static int ContextDo(SConnection *pCon, SicsInterp *pSics, void *pData, status = Tcl_GetInt(pSics->pTcl,argv[1],&comCon.transID); if(status != TCL_OK){ - snprintf(command,1023,"ERROR: failed to convert %s to transaction ID", argv[1]); - SCWrite(pCon,command,eError); + snprintf(buffer,1023,"ERROR: failed to convert %s to transaction ID", argv[1]); + SCWrite(pCon,buffer,eError); return 0; } strncpy(comCon.deviceID,argv[2],SCDEVIDLEN); @@ -215,7 +218,16 @@ int InstallProtocol(SConnection *pCon, SicsInterp *pSics, void *pData, SCSendOK(pCon); return 1; } - +/*------------------------------------------------------------------------*/ +void MakeProtocol(SicsInterp *pSics){ + pProtocol pNew = NULL; + pNew = CreateProtocol(); + if(NULL!= pNew) + { + AddCommand(pSics,"Protocol",ProtocolAction,DeleteProtocol,pNew); + AddCommand(pSics,"contextdo",ContextDo,NULL,NULL); + } +} /*------------------------------------------------------------------------*/ static int ProtocolOptions(SConnection* pCon, pProtocol pPro) { @@ -276,7 +288,12 @@ static int ProtocolSet(SConnection* pCon, Protocol* pPro, char *pProName) case 3: /* sycamore */ SCSetWriteFunc(pCon,SCWriteSycamore); break; - + case 4: /* json */ + SCSetWriteFunc(pCon,SCWriteJSON_String); + break; + case 5: + SCSetWriteFunc(pCon,SCACTWrite); + break; case 0: /* default = psi_sics */ default: SCSetWriteFunc(pCon,pPro->defaultWriter); @@ -288,14 +305,21 @@ static int ProtocolSet(SConnection* pCon, Protocol* pPro, char *pProName) } /*------------------------------------------------------------------------*/ -static int ProtocolGet(SConnection* pCon, Protocol* pPro, char *pProName, - int *pIndex) +int ProtocolGet(SConnection* pCon, void* pData, char *pProName, int len) { + int Index; + Protocol *pPro = (Protocol *)pData; if(!SCVerifyConnection(pCon)) { return 0; } + if(pData == NULL) + { + pCon->iProtocolID = 0; + return 1; + } + /* lazy initialisation of defaultWriter since connection is verified */ if(0==pPro->isDefaultSet) { @@ -303,24 +327,28 @@ static int ProtocolGet(SConnection* pCon, Protocol* pPro, char *pProName, pPro->isDefaultSet = 1; pCon->iProtocolID = 0; } - - *pIndex = (int)malloc(sizeof(int)); - *pIndex = pCon->iProtocolID; + strncpy(pProName, pPro->pProList[pCon->iProtocolID], len); + return 1; +#if 0 + Index = pCon->iProtocolID; /* check list of protocols for valid name */ - switch(*pIndex) + switch(Index) { case 0: /* default = psi_sics */ case 1: /* normal (connection start default) */ case 2: /* outcodes */ case 3: /* sycamore */ - pProName = strdup(pPro->pProList[*pIndex]); + case 4: /* json */ + case 5: /* act */ + pProName = pPro->pProList[Index]; return 1; break; default: return 0; break; } +#endif } /*------------------------------------------------------------------------*/ @@ -351,10 +379,10 @@ int ProtocolAction(SConnection *pCon, SicsInterp *pSics, void *pData, {NULL} }; - assert(pCon); - assert(pSics); + assert(pCon != NULL); + assert(pSics != NULL); pPro = (pProtocol)pData; - assert(pPro); + assert(pPro != NULL); /* You need to have User level access rights to use this facility */ if(!SCMatchRights(pCon,usSpy)) @@ -427,8 +455,6 @@ static int InitDefaultProtocol(SConnection* pCon, Protocol *pPro) } /*---------------------------------------------------------------------*/ void sycformat(char *tag, OutCode msgFlag, pDynString msgString, pDynString msgOut) { - char typePrefix[] = "type."; - char statusPrefix[] = "status."; DynStringConcat(msgOut," "); switch (msgFlag) { eEvent: @@ -445,19 +471,13 @@ void sycformat(char *tag, OutCode msgFlag, pDynString msgString, pDynString msgO } int SCWriteSycamore(SConnection *pCon, char *pBuffer, int iOut) { - int i, iPtr, iRet; - int bDevIDdone = 0; - int bFlagDone = 0; + int iRet; char pBueffel[MAXMSG], *pBufferFrom, *pBufferTo; long taskID = 0; /* char pPrefix[40];*/ pDynString pMsg = NULL; pDynString pMsgString = NULL; - SicsInterp *pSics; - TokenList *pList = NULL; - TokenList *pCurrent; commandContext comCon; - char *savedTclResult = NULL; if (strlen(pBuffer) == 0) { @@ -494,8 +514,7 @@ int SCWriteSycamore(SConnection *pCon, char *pBuffer, int iOut) /* write to commandlog if user or manager privilege */ if(SCGetRights(pCon) <= usUser) { - sprintf(pBueffel,"To sock %d :",iRet); - WriteToCommandLog(pBueffel,pBuffer); + WriteToCommandLogId(NULL,iRet,pBuffer); } /* put it into the interpreter if present */ @@ -517,11 +536,6 @@ int SCWriteSycamore(SConnection *pCon, char *pBuffer, int iOut) return 0; } - /* - build the Tcl-command to execute for formatting the - data into a sycamore string - */ - pSics = GetInterpreter(); taskID = comCon.transID; pMsg = CreateDynString(INIT_STR_SIZE, STR_RESIZE_LENGTH); @@ -552,9 +566,14 @@ int SCWriteSycamore(SConnection *pCon, char *pBuffer, int iOut) if (iOut == eEvent) { DynStringConcat(pMsgString, " type="); - DynStringConcat(pMsgString, pEventType[pCon->conEventType]); + /* Default type to VALUECHANGE if conEventType not set */ + if (-1 == pCon->conEventType) + DynStringConcat(pMsgString, pEventType[0]); + else + DynStringConcat(pMsgString, pEventType[pCon->conEventType]); /* DynStringConcat(pMsgString, " status="); DynStringConcat(pMsgString, pStatus[pCon->conStatus]);*/ + DynStringConcat(pMsgString,","); } DynStringConcat(pMsgString," "); DynStringConcat(pMsgString,pBuffer); @@ -577,6 +596,161 @@ int SCWriteSycamore(SConnection *pCon, char *pBuffer, int iOut) return 1; } +/* Only work for hipadaba commands, hlist, hset, hget, hnotify + * A multiline string (ie have crnl) will be converted to an array. + * Strings with '=' will be converted to name value pairs + */ +struct json_object *mkJSON_Object(SConnection *pCon, char *pBuffer, int iOut) +{ + int linenum = __LINE__; + char pBueffel[MAXMSG], *pBufferFrom, *pBufferTo; + long taskID = 0; + struct json_object *msg_json=NULL, *tmp_json=NULL; + + commandContext comCon; + char pError[256]; + pError[0]='\0'; + + + if (strlen(pBuffer) == 0) { + return 0; + } + + if(!SCVerifyConnection(pCon)) + { + return 0; + } + comCon = SCGetContext(pCon); + + /* Return 0 without dying if no message data */ + if(pBuffer == NULL) + { + return 0; + } + + /* + build the Tcl-command to execute for formatting the + data into a sycamore string + */ + taskID = comCon.transID; + + pBueffel[0] = '\0'; + msg_json = json_object_new_object(); + if (is_error(msg_json)) { linenum = __LINE__; goto reporterr; } +/* field 1: connID */ + json_object_object_add(msg_json, "con", json_object_new_int(pCon->ident)); +/* field 2: taskID */ + json_object_object_add(msg_json, "trans", json_object_new_int(taskID)); + /* deviceID */ + json_object_object_add(msg_json, "object", json_object_new_string(comCon.deviceID)); + + /* msgFlag */ + switch(iOut) { + case 5: /* eValue */ + json_object_object_add(msg_json, "flag", json_object_new_string("out")); + break; + default: + json_object_object_add(msg_json, "flag", json_object_new_string(pCode[iOut])); + break; + } + if (iOut == eHdbValue || iOut == eHdbEvent) { + tmp_json = json_tokener_parse(pBuffer); + if (is_error(tmp_json)) { linenum = __LINE__; goto reporterr; } + } else { + /* Strip \r and \n */ + for (pBufferFrom=pBufferTo=pBuffer; ; pBufferFrom++) { + if (*pBufferFrom == '\r' || *pBufferFrom == '\n') + continue; + pBufferTo = pBufferFrom; + if (*pBufferTo == '\0') + break; + pBufferTo++; + } + tmp_json = json_object_new_string(pBuffer); + if (is_error(tmp_json)) { linenum = __LINE__; goto reporterr; } + } + json_object_object_add(msg_json, "data", tmp_json); + return msg_json; + +reporterr: + SCSetWriteFunc(pCon,SCNormalWrite); + snprintf(pError, 256,"{\"ERROR\": \"%s:%d Error making json object\"}", __FILE__, linenum); + SCWrite(pCon,pError,eError); + SCSetWriteFunc(pCon,SCWriteJSON_String); +cleanup: + if (tmp_json != NULL && !is_error(tmp_json)) + json_object_put(tmp_json); + if (msg_json != NULL && !is_error(msg_json)) + json_object_put(msg_json); + return NULL; +} + +int SCWriteJSON_String(SConnection *pCon, char *pBuffer, int iOut) +{ + struct json_object *my_object=NULL, *tmp_json=NULL; + char pBueffel[MAXMSG], errBuff[MAXMSG]; + int iRet, errLen = MAXMSG; + + if (strlen(pBuffer) == 0) + return 1; + + /* log it for any case */ + if(pCon->pSock) + { + iRet = pCon->pSock->sockid; + } + else + { + iRet = 0; + } + sprintf(pBueffel,"Next line intended for socket: %d",iRet); + SICSLogWrite(pBueffel,eInternal); + SICSLogWrite(pBuffer,iOut); + + /* write to commandlog if user or manager privilege */ + if(SCGetRights(pCon) <= usUser) + { + if(pCon->iMacro != 1) + { + WriteToCommandLogId(NULL,iRet,pBuffer); + } + else + { + if(iOut == eError || iOut == eWarning) + { + WriteToCommandLogId(NULL,iRet,pBuffer); + } + } + } + + if(SCinMacro(pCon)) + { + InterpWrite(pServ->pSics,pBuffer); + /* print it to client if error message */ + if((iOut== eError) || (iOut == eWarning) ) + { + tmp_json = json_object_new_string(pBuffer); + iRet = SCDoSockWrite(pCon,json_object_to_json_string(tmp_json)); + } + } else { + if ((my_object = mkJSON_Object(pCon, pBuffer, iOut)) == NULL) { + snprintf(errBuff, errLen, "failed to make JSON object from, %s", pBuffer); + tmp_json = json_object_new_string(errBuff); + my_object = json_object_new_object(); + json_object_object_add(my_object, "ERROR", tmp_json); + SCDoSockWrite(pCon,json_object_to_json_string(my_object)); + iRet = 0; + } else { + iRet = SCDoSockWrite(pCon,json_object_to_json_string(my_object)); + SCWriteToLogFiles(pCon,pBuffer); + } + } + if (tmp_json != NULL && !is_error(tmp_json)) + json_object_put(tmp_json); + if (my_object != NULL && !is_error(my_object)) + json_object_put(my_object); + return iRet; +} /*------------------------------------------------------------------------*/ /* Protocol API */ char * GetProtocolName(SConnection* pCon) @@ -604,6 +778,7 @@ char * GetProtocolName(SConnection* pCon) case 1: /* normal (connection start default) */ case 2: /* outcodes */ case 3: /* sycamore */ + case 4: /* json */ return strdup(pPro->pProList[pCon->iProtocolID]); break; default: @@ -621,3 +796,26 @@ int GetProtocolID(SConnection* pCon) } return -1; } +/*---------------------------------------------------------------------------*/ +writeFunc GetProtocolWriteFunc(SConnection *pCon){ + if(pCon != NULL){ + switch(pCon->iProtocolID){ + case 2: /* outcodes */ + return SCWriteWithOutcode; + break; + case 3: /* sycamore */ + return SCWriteSycamore; + break; + case 4: /* json */ + return SCWriteJSON_String; + break; + case 5: + return SCACTWrite; + break; + default: + return SCNormalWrite; + break; + } + } + return SCNormalWrite; +} diff --git a/protocol.h b/protocol.h index c3b8c585..24d51d72 100644 --- a/protocol.h +++ b/protocol.h @@ -19,16 +19,18 @@ static char *pProTags[3] = { int InstallProtocol(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); void DeleteProtocol(void *pSelf); +void MakeProtocol(SicsInterp *pSics); /*--------------------- operations --------------------------------------*/ int ProtocolAction(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); - /*--------------------- implement protocol sycamore ---------------------*/ int SCWriteSycamore(SConnection *pCon, char *pBuffer, int iOut); /*--------------------- implement protocol API -----------------------*/ char * GetProtocolName(SConnection *pCon); int GetProtocolID(SConnection *pCon); +int ProtocolGet(SConnection* pCon, void* pData, char *pProName, int len); +writeFunc GetProtocolWriteFunc(SConnection *pCon); /*-----------------------------------------------------------------------*/ #endif diff --git a/proxy.c b/proxy.c new file mode 100644 index 00000000..a30a5dd8 --- /dev/null +++ b/proxy.c @@ -0,0 +1,476 @@ +/** + * This is the implementation of a SICS object which really is a placeholder + * for another one. It shall be used in Hipadaba for sample enviornment + * devices. This is also the reason why the objectNode is supposed to be + * double. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, March 2008 + * + */ +#include +#include +#include +/*-----------------------------------------------------------------------*/ +typedef struct { + pIDrivable pDriv; + pEVInterface pEnv; + pIDrivable pSlaveDriv; + void *slaveData; + pEVInterface pEnvSlave; +} ProxyInt, *pProxyInt; +/*------------------------------------------------------------------------*/ +static void KillProxyInt(void *data){ + pProxyInt proxy = (pProxyInt)data; + + if(proxy == NULL){ + return; + } + if(proxy->pDriv != NULL){ + free(proxy->pDriv); + } + if(proxy->pEnv != NULL){ + free(proxy->pEnv); + } + free(proxy); +} +/*===================== Drivable Interfaces ================================*/ +static int testDrivProxy(pSICSOBJ self){ + pProxyInt proxy = self->pPrivate; + + if(proxy->pSlaveDriv != NULL && proxy->slaveData != NULL){ + return 1; + } + return 0; +} +/*-----------------------------------------------------------------------*/ +static int ProxyHalt(void *data){ + pSICSOBJ self = (pSICSOBJ)data; + pProxyInt proxy = NULL; + + assert(self != NULL); + proxy = self->pPrivate; + assert(self != NULL); + + if(testDrivProxy(self)){ + return proxy->pSlaveDriv->Halt(proxy->slaveData); + } else { + return 0; + } +} +/*------------------------------------------------------------------------*/ +static int ProxyLimits(void *data, float fval, + char *error, int iErrLen){ + pSICSOBJ self = (pSICSOBJ)data; + pProxyInt proxy = NULL; + + assert(self != NULL); + proxy = self->pPrivate; + assert(self != NULL); + + if(testDrivProxy(self)){ + return proxy->pSlaveDriv->CheckLimits(proxy->slaveData, fval, + error, iErrLen); + } else { + strncpy(error,"ERROR: device not configured",iErrLen); + return 0; + } +} +/*-----------------------------------------------------------------------*/ +static long ProxySet(void *data, SConnection *pCon, float fVal){ + pSICSOBJ self = (pSICSOBJ)data; + pProxyInt proxy = NULL; + + assert(self != NULL); + proxy = self->pPrivate; + assert(self != NULL); + + if(testDrivProxy(self)){ + return proxy->pSlaveDriv->SetValue(proxy->slaveData, + pCon, fVal); + } else { + SCWrite(pCon,"ERROR: device not configured", eError); + return 0; + } +} +/*-----------------------------------------------------------------------*/ +static int ProxyStatus(void *data, SConnection *pCon){ + pSICSOBJ self = (pSICSOBJ)data; + pProxyInt proxy = NULL; + + assert(self != NULL); + proxy = self->pPrivate; + assert(self != NULL); + + if(testDrivProxy(self)){ + return proxy->pSlaveDriv->CheckStatus(proxy->slaveData, + pCon); + } else { + SCWrite(pCon,"ERROR: device not configured", eError); + return HWFault; + } +} +/*-----------------------------------------------------------------------*/ +static float ProxyGet(void *data, SConnection *pCon){ + pSICSOBJ self = (pSICSOBJ)data; + pProxyInt proxy = NULL; + + assert(self != NULL); + proxy = self->pPrivate; + assert(self != NULL); + + if(testDrivProxy(self)){ + return proxy->pSlaveDriv->GetValue(proxy->slaveData, pCon); + } else { + SCWrite(pCon,"ERROR: device not configured", eError); + return HWFault; + } +} +/*===================== environment interface ==========================*/ +static int testEnvProxy(pSICSOBJ self){ + pProxyInt proxy = self->pPrivate; + + if(proxy->pEnvSlave != NULL && proxy->slaveData != NULL){ + return 1; + } + return 0; +} +/*-----------------------------------------------------------------------*/ +static EVMode ProxyMode(void *data){ + pSICSOBJ self = (pSICSOBJ)data; + pProxyInt proxy = NULL; + + assert(self != NULL); + proxy = self->pPrivate; + assert(self != NULL); + + if(testEnvProxy(self)){ + return proxy->pEnvSlave->GetMode(proxy->slaveData); + } else { + return EVError; + } +} +/*------------------------------------------------------------------------*/ +static int ProxyTolerance(void *data){ + pSICSOBJ self = (pSICSOBJ)data; + pProxyInt proxy = NULL; + + assert(self != NULL); + proxy = self->pPrivate; + assert(self != NULL); + + if(testEnvProxy(self)){ + return proxy->pEnvSlave->IsInTolerance(proxy->slaveData); + } else { + return 0; + } +} +/*-------------------------------------------------------------------------*/ +static int ProxyError(void *data){ + pSICSOBJ self = (pSICSOBJ)data; + pProxyInt proxy = NULL; + + assert(self != NULL); + proxy = self->pPrivate; + assert(self != NULL); + + if(testEnvProxy(self)){ + return proxy->pEnvSlave->HandleError(proxy->slaveData); + } else { + return 0; + } +} +/*-----------------------------------------------------------------------*/ +static void *findRealDev(pHdb node){ + char realDevice[80]; + CommandList *pCom = NULL; + + GetHdbProperty(node,"proxy",realDevice,80); + pCom = FindCommand(pServ->pSics,realDevice); + if(pCom != NULL){ + return pCom->pData; + } else { + return NULL; + } +} +/*-----------------------------------------------------------------------*/ +static void *ProxyGetInterface(void *pData, int iID){ + pSICSOBJ self = (pSICSOBJ)pData; + pDummy other = NULL; + void *inter = NULL; + pProxyInt proxy = NULL; + + assert(self != NULL); + proxy = self->pPrivate; + + if(self != NULL){ + other = (pDummy)findRealDev(self->objectNode); + if(other != NULL){ + inter = other->pDescriptor->GetInterface(other, iID); + if(inter == NULL && + (iID == DRIVEID || iID == ENVIRINTERFACE) ){ + proxy->pEnvSlave = NULL; + proxy->pSlaveDriv = NULL; + proxy->slaveData = NULL; + return NULL; + } else { + if(iID == DRIVEID){ + proxy->pSlaveDriv = inter; + proxy->slaveData = other; + return proxy->pDriv; + } else if(iID == ENVIRINTERFACE){ + proxy->pEnvSlave = inter; + proxy->slaveData = other; + return proxy->pEnv; + } + } + return inter; + } + } + return NULL; +} +/*------------------------------------------------------------------------*/ +static hdbCallbackReturn ProxyCallback(pHdb node, void *userData, + pHdbMessage message){ + pHdbDataMessage get = NULL, set = NULL; + pDummy other = NULL; + pIDrivable pDriv = NULL; + float fval; + int status; + SConnection *pCon = NULL; + pSICSOBJ self = (pSICSOBJ)userData; + char proxyDev[80]; + + get = GetHdbGetMessage(message); + if(get != NULL){ + pCon = (SConnection *)get->callData; + other = (pDummy)findRealDev(node); + if(other != NULL){ + pDriv = other->pDescriptor->GetInterface(other,DRIVEID); + if(pDriv != NULL && pCon != NULL){ + fval = pDriv->GetValue(other, pCon); + get->v->v.doubleValue = (double)fval; + node->value.v.doubleValue = (double)fval; + return hdbContinue; + } + } + get->v->v.doubleValue = .0; + } + + set = GetHdbSetMessage(message); + if(set != NULL){ + pCon = (SConnection *)set->callData; + other = (pDummy)findRealDev(node); + if(other == NULL){ + if(pCon != NULL){ + SCWrite(pCon,"ERROR: device not configured", eError); + return hdbAbort; + } + } + GetHdbProperty(node,"proxy", proxyDev,80); + status = StartDevice(pServ->pExecutor, proxyDev, + self->pDes, self, pCon, (float)set->v->v.doubleValue); + if(status == 1){ + return hdbContinue; + } else { + return hdbAbort; + } + } + + return hdbContinue; +} +/*----------------------------------------------------------------------------*/ +static hdbCallbackReturn MapParCallback(pHdb node, void *userData, + pHdbMessage message){ + pHdbDataMessage get = NULL, set = NULL; + CommandList *pCom = NULL; + SConnection *pCon = NULL; + char mapPar[80], proxyDev[80], *pData = NULL; + char command[1024]; + pDynString data = NULL; + + if(GetHdbKillNodeMessage(message) != NULL ){ + return hdbContinue; + } + + GetHdbProperty(node->mama, "proxy", proxyDev,80); + pCom = FindCommand(pServ->pSics, proxyDev); + if(pCom == NULL){ + if(pCon != NULL){ + SCWrite(pCon,"ERROR: device not configured", eError); + } + return hdbContinue; + } + GetHdbProperty(node,"mappar", mapPar, 80); + + get = GetHdbGetMessage(message); + if(get != NULL){ + pCon = (SConnection *)get->callData; snprintf(command,1024,"%s %s", proxyDev, mapPar); + if(pCon != NULL){ + SCStartBuffering(pCon); + InterpExecute(pServ->pSics, pCon,command); + data = SCEndBuffering(pCon); + if(data != NULL){ + pData = GetCharArray(data); + if(strstr(pData,"ERROR") != NULL){ + SCWrite(pCon,pData,eError); + } else { + pData = strchr(pData,(int)'='); + if(pData != NULL){ + pData++; + if(!readHdbValue(get->v, pData, command, 1024)){ + SCWrite(pCon, command, eError); + } + copyHdbValue(get->v, &node->value); + return hdbContinue; + } + } + } + } + } + + set = GetHdbSetMessage(message); + if(set != NULL){ + pCon = (SConnection *)set->callData; + data = formatValue(*(set->v), node); + if(data != NULL){ + snprintf(command,1024,"%s %s %s", proxyDev, mapPar, + GetCharArray(data)); + DeleteDynString(data); + InterpExecute(pServ->pSics, pCon, command); + } + } + + return hdbContinue; +} +/*-------------------------------------------------------------------------*/ +static int MapFunc(pSICSOBJ self, SConnection *pCon, pHdb commandNode, + pHdb par[], int nPar){ + pHdb node = NULL; + int type; + + if(nPar < 4){ + SCWrite(pCon,"ERROR: not enough arguments to MapFunc", eError); + return 0; + } + + type = convertHdbType(par[2]->value.v.text); + node = MakeHipadabaNode(par[0]->value.v.text, type, 1); + if(node == NULL){ + SCWrite(pCon,"ERROR: out of memory in MapFunc", eError); + return 0; + } + SetHdbProperty(node,"mappar", par[1]->value.v.text); + SetHdbProperty(node,"priv", par[3]->value.v.text); + AppendHipadabaCallback(node, + MakeHipadabaCallback(MapParCallback, NULL, NULL)); + AddHipadabaChild(self->objectNode, node, pCon); + + return 1; +} +/*--------------------------------------------------------------------------*/ +static int ProxyAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + int status; + pSICSOBJ self = (pSICSOBJ)pData; + CommandList *pCom = NULL; + char proxyObj[80]; + + assert(self != NULL); + + /* + * do parameters and object functions + */ + if(argc > 1){ + status = InvokeSICSOBJ(pCon, pSics, pData, argc, argv); + if(status != -1 ){ + return status; + } + } + + /* + * try the interpreter function of the proxy object + */ + GetHdbProperty(self->objectNode,"proxy", proxyObj, 80); + pCom = FindCommand(pSics, proxyObj); + if(pCom != NULL){ + return pCom->OFunc(pCon,pSics,pCom->pData, argc, argv); + } else { + SCWrite(pCon,"ERROR: device not configured", eError); + return 0; + } +} +/*--------------------------------------------------------------------------*/ +int ProxyFactory(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + int type; + pSICSOBJ pNew = NULL; + pHdb mapFunc = NULL; + hdbValue v; + pProxyInt proxy = NULL; + + if(argc < 4){ + SCWrite(pCon,"ERROR: not enough arguments for ProxyFactory", + eError); + return 0; + } + + type = convertHdbType(argv[3]); + pNew = MakeSICSOBJv(argv[1], "ProxyObject", type, usSpy); + if(pNew == NULL){ + SCWrite(pCon,"ERROR: out of memory in ProxyFactory", + eError); + return 0; + } + + proxy = malloc(sizeof(ProxyInt)); + if(proxy == NULL){ + SCWrite(pCon,"ERROR: out of memory in ProxyFactory", + eError); + return 0; + } + memset(proxy,0,sizeof(ProxyInt)); + proxy->pDriv = CreateDrivableInterface(); + proxy->pEnv = CreateEVInterface(); + if(proxy->pDriv == NULL && proxy->pEnv == NULL){ + SCWrite(pCon,"ERROR: out of memory in ProxyFactory", + eError); + return 0; + } + proxy->pDriv->CheckLimits = ProxyLimits; + proxy->pDriv->CheckStatus = ProxyStatus; + proxy->pDriv->GetValue = ProxyGet; + proxy->pDriv->Halt = ProxyHalt; + proxy->pDriv->SetValue = ProxySet; + + proxy->pEnv->GetMode = ProxyMode; + proxy->pEnv->HandleError = ProxyError; + proxy->pEnv->IsInTolerance = ProxyTolerance; + + pNew->KillPrivate = KillProxyInt; + pNew->pPrivate = proxy; + + pNew->pDes->GetInterface = ProxyGetInterface; + SetHdbProperty(pNew->objectNode, "proxy", argv[2]); + AppendHipadabaCallback(pNew->objectNode, + MakeHipadabaCallback(ProxyCallback, pNew,NULL)); + + v = MakeSICSFunc(MapFunc); + mapFunc = MakeSICSHdbPar("map", usMugger, v); + SetHdbProperty(mapFunc,"visible","false"); + v = MakeHdbText("Undefined"); + AddSICSHdbPar(mapFunc,"name",usMugger,v); + AddSICSHdbPar(mapFunc,"target",usMugger,v); + AddSICSHdbPar(mapFunc,"type",usMugger,v); + AddSICSHdbPar(mapFunc,"priv",usMugger,v); + AddHipadabaChild(pNew->objectNode, mapFunc, pCon); + + AddCommand(pSics,argv[1], + ProxyAction, + KillSICSOBJ, + pNew); + + return 1; +} + diff --git a/proxy.h b/proxy.h new file mode 100644 index 00000000..4afc22cc --- /dev/null +++ b/proxy.h @@ -0,0 +1,17 @@ +/** + * This is the implementation of a SICS object which really is a placeholder + * for another one. It shall be used in Hipadaba for sample enviornment + * devices. This is also the reason why the objectNode is supposed to be + * double. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, March 2008 + * + */ +#ifndef PROXY_H_ +#define PROXY_H_ +int ProxyFactory(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + +#endif /*PROXY_H_*/ diff --git a/regresscter.c b/regresscter.c new file mode 100644 index 00000000..a0701b50 --- /dev/null +++ b/regresscter.c @@ -0,0 +1,262 @@ +/*-------------------------------------------------------------------------- + This is a counter for use in automated regression tests. + + copyright: see file COPYRIGHT + + Mark Koennecke, September 2006 +----------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include "fortify.h" +#include +#include "sics.h" +#include "countdriv.h" +/*---------------------------------- possible error types ------------------*/ +#define NONE 0 +#define STARTFAIL 1 +#define STATUSFAIL 2 +#define PAUSEFAIL 3 +#define CONTFAIL 4 +#define READFAIL 5 + +#define STATEIDLE 0 +#define STATERUN 1 +#define STATEPAU 2 +/*--------------------------------------------------------------------------*/ +typedef struct { + int errType; + int recover; + int state; + time_t endTime; +} RegressSt; +/*---------------------------------------------------------------------------*/ +static int RegressGetStatus(struct __COUNTER *self, float *fControl){ + RegressSt *pSim = NULL; + time_t tD, tDe; + int iRun; + + assert(self); + pSim = (RegressSt *) self->pData; + assert(pSim); + + if(pSim->errType == STATUSFAIL){ + return HWFault; + } + if(time(NULL) > pSim->endTime){ + pSim->state = STATEIDLE; + } + switch(pSim->state){ + case STATEIDLE: + return HWIdle; + break; + case STATERUN: + return HWBusy; + break; + case STATEPAU: + return HWPause; + break; + } + assert(0); + return HWFault; +} +/*---------------------------------------------------------------------------*/ +static int RegressStart(struct __COUNTER *self){ + RegressSt *pSim = NULL; + time_t tD; + int iRun; + + assert(self); + pSim = (RegressSt *) self->pData; + assert(pSim); + + if(pSim->errType == STARTFAIL){ + return HWFault; + } + pSim->state = STATERUN; + if(self->eMode == eTimer){ + pSim->endTime = time(NULL) + (int)self->fPreset; + } else { + pSim->endTime = time(NULL) + 7; + } + + return OKOK; +} +/*---------------------------------------------------------------------------*/ +static int RegressPause(struct __COUNTER *self){ + RegressSt *pSim = NULL; + time_t tD; + int iRun; + + assert(self); + pSim = (RegressSt *) self->pData; + assert(pSim); + + if(pSim->errType == PAUSEFAIL){ + return HWFault; + } + + pSim->state = STATEPAU; + + return OKOK; +} +/*---------------------------------------------------------------------------*/ +static int RegressContinue(struct __COUNTER *self){ + RegressSt *pSim = NULL; + + assert(self); + pSim = (RegressSt *) self->pData; + assert(pSim); + + if(pSim->errType == CONTFAIL){ + return HWFault; + } + + pSim->state = STATERUN; + + return OKOK; +} +/*--------------------------------------------------------------------------*/ +static int RegressHalt(struct __COUNTER *self){ + RegressSt *pSim = NULL; + + assert(self); + pSim = (RegressSt *) self->pData; + assert(pSim); + + pSim->state = STATEIDLE; + + return OKOK; +} + +/*-------------------------------------------------------------------------*/ +static int RegressReadValues(struct __COUNTER *self){ + RegressSt *pSim = NULL; + int i; + + assert(self); + pSim = (RegressSt *) self->pData; + assert(pSim); + + if(pSim->errType == READFAIL){ + return HWFault; + } + + for (i = 0; i < MAXCOUNT; i++) { + self->lCounts[i] = i*10+5; + } + self->lCounts[1] = self->fPreset; + self->fTime = self->fPreset; + return OKOK; +} +/*-------------------------------------------------------------------------*/ +static int RegressGetError(struct __COUNTER *self, int *iCode, char *error, + int iErrLen){ + strncpy(error, "Regression counter error", iErrLen); + *iCode = 1; + return 1; +} +/*--------------------------------------------------------------------------*/ +static int RegressTryAndFixIt(struct __COUNTER *self, int iCode){ + RegressSt *pSim = NULL; + + assert(self); + pSim = (RegressSt *) self->pData; + assert(pSim); + + if(pSim->recover == 1){ + pSim->errType = NONE; + return COREDO; + } else { + return COTERM; + } +} +/*--------------------------------------------------------------------------*/ +static int RegressSet(struct __COUNTER *self, char *name, int iCter, float FVal){ + RegressSt *pSim = NULL; + + assert(self); + pSim = (RegressSt *) self->pData; + assert(pSim); + + if(strcmp(name,"errortype") == 0){ + pSim->errType = (int)FVal; + return 1; + } + if(strcmp(name,"recover") == 0){ + pSim->recover = (int)FVal; + return 1; + } + if(strcmp(name,"finish") == 0){ + pSim->state = STATEIDLE; + return 1; + } + return 0; +} +/*--------------------------------------------------------------------------*/ +static int RegressGet(struct __COUNTER *self, char *name, + int iCter, float *fVal){ + RegressSt *pSim = NULL; + + assert(self); + pSim = (RegressSt *) self->pData; + assert(pSim); + + if(strcmp(name,"errortype") == 0){ + *fVal = pSim->errType; + return 1; + } + if(strcmp(name,"recover") == 0){ + *fVal = pSim->recover; + return 1; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static int RegressSend(struct __COUNTER *self, char *pText, + char *pReply, int iReplyLen){ + strncpy(pReply, "Simulated response", iReplyLen); + return 1; +} +/*---------------------------------------------------------------------------*/ +pCounterDriver NewRegressCounter(char *name){ + pCounterDriver pRes = NULL; + RegressSt *pData = NULL; + int iRet; + int iC1, iC2, iC3; + char *pErr; + char pBueffel[132]; + + pRes = CreateCounterDriver(name, "Regress"); + if (!pRes) { + return NULL; + } + + pData = (RegressSt *) malloc(sizeof(RegressSt)); + if (!pData) { + DeleteCounterDriver(pRes); + return NULL; + } + memset(pData,0,sizeof(RegressSt)); + pRes->pData = pData; + + /* + * assign functions + */ + pRes->GetStatus = RegressGetStatus; + pRes->Start = RegressStart; + pRes->Halt = RegressHalt; + pRes->ReadValues = RegressReadValues; + pRes->GetError = RegressGetError; + pRes->TryAndFixIt = RegressTryAndFixIt; + pRes->Pause = RegressPause; + pRes->Continue = RegressContinue; + pRes->Set = RegressSet; + pRes->Get = RegressGet; + pRes->Send = RegressSend; + pRes->KillPrivate = NULL; + pRes->iNoOfMonitors = 8; + + return pRes; +} diff --git a/remob.c b/remob.c index fca5591e..e0308028 100644 --- a/remob.c +++ b/remob.c @@ -19,6 +19,7 @@ M. Zolliker July 04 #include "status.h" #include "servlog.h" #include "site.h" +#include "commandlog.h" /*-------------------------------------------------------------------------*/ #define INTERRUPTMODE 0 #define ACCESSCODE 1 @@ -43,7 +44,8 @@ typedef struct RemServer { int matchMap; int taskActive; int interestActive; - SCStore conn; + int forwardMessages; + SCStore *conn; } RemServer; struct Remob { @@ -191,37 +193,42 @@ static int RemSetInterest(RemChannel *rc) { return 0; } /*-------------------------------------------------------------------------*/ -static void RemConnect(RemServer *remserver, RemChannel *rc) { - /* open channel rc, if not yet opened */ +static void RemConnect(RemServer *remserver, int both) { + /* open channel 0 or both channels, if not yet opened */ int iRet; char buf[256]; mkChannel *chan; + int i; + RemChannel *rc; - if (!rc->chan) { - rc->timeout = 0; - chan = NETConnect(remserver->host, remserver->port); - if (!chan) { - return; - } - rc->chan = chan; - if (rc != &remserver->rc[0]) { /* open the user channel */ - iRet = RemWrite(rc, "remuser sesam\n"); - } else { /* open spy channel */ - iRet = RemWrite(rc, "Spy 007\n"); - } - if (iRet < 0) goto close; - iRet = RemRead(rc, 1000); - while (iRet > 0) { /* eat login response */ - if (StartsWith(rc->line, "Login OK")) { - /* printf("connected\n"); */ - if (remserver->interestActive && rc == &remserver->rc[0]) { /* open the user channel */ - if (!RemSetInterest(rc)) goto close; - } + for (i = 0; i <= both; i++) { + rc = &remserver->rc[i]; + if (!rc->chan) { + rc->timeout = 0; + chan = NETConnect(remserver->host, remserver->port); + if (!chan) { return; } + rc->chan = chan; + if (i != 0) { /* open the user channel */ + iRet = RemWrite(rc, "remuser sesam\n"); + } else { /* open spy channel */ + iRet = RemWrite(rc, "Spy 007\n"); + } + if (iRet < 0) goto close; iRet = RemRead(rc, 1000); + while (iRet > 0) { /* eat login response */ + if (StartsWith(rc->line, "Login OK")) { + /* printf("connected\n"); */ + if (remserver->interestActive && rc == &remserver->rc[0]) { /* open the user channel */ + if (!RemSetInterest(rc)) goto close; + } + break; + } + iRet = RemRead(rc, 1000); + } + if (iRet <= 0) goto close; } - goto close; } return; close: @@ -242,24 +249,29 @@ static int RemServerTask(void *data) { rc = &remserver->rc[isUser]; if (RemRead(rc, 0) <= 0) continue; - /* printf("< %s\n", buf); */ - + if (strstr(rc->line, " ") == rc->line) { + WriteToCommandLog("REMOB>", "infinite echo loop detected"); + continue; + } if (isUser == 0) { if (RemHandle(remserver)) { /* handle drivstat messages */ continue; } } - /* forward all other messages */ - pCon = SCLoad(&remserver->conn); - if (pCon) { - RemCopy(rc, pCon); + if (remserver->forwardMessages) { + /* forward all other messages */ + if (SCStoreConnected(remserver->conn)) { + pCon = SCStorePush(remserver->conn); + RemCopy(rc, pCon); + SCStorePop(remserver->conn); + } } } return 1; } /*-------------------------------------------------------------------------*/ -static int RemTransact(RemServer *remserver, RemChannel *rc, SConnection *pCon, +static int RemTransact(RemServer *remserver, int nChan, SConnection *pCon, char *cmd, ...) { /* the variable arguments are for filtering: @@ -275,7 +287,8 @@ static int RemTransact(RemServer *remserver, RemChannel *rc, SConnection *pCon, va_list ap; int try; int argMask; - + RemChannel *rc = &remserver->rc[nChan]; + try=2; if (rc->timeout) { /* eat old responses */ while (RemRead(rc, 0) > 0) { @@ -283,10 +296,8 @@ static int RemTransact(RemServer *remserver, RemChannel *rc, SConnection *pCon, } } tryagain: - strcpy(buf, "transact "); - strcat(buf, cmd); - strcat(buf,"\n"); - RemConnect(remserver, rc); + snprintf(buf, sizeof buf, "transact %s\n", cmd); + RemConnect(remserver, nChan); iRet = RemWrite(rc, buf); if (iRet < 0) goto close; @@ -362,8 +373,9 @@ static int RemobHalt(void *self) { char buf[64]; assert(remob); - RemConnect(remserver, rc); + RemConnect(remserver, 1); snprintf(buf, sizeof(buf), "stopexe %s\n", remob->name); + remob->status = HWFault; return RemWrite(rc, buf); } @@ -386,12 +398,12 @@ static float RemobGetValue(void *pData, SConnection *pCon) { assert(remob); - SCSave(&remserver->conn, pCon); + remserver->conn = SCSave(pCon, remserver->conn); none = -1.25e6; value= none; snprintf(buf, sizeof(buf), "<%s", remob->name); /* get value needs only spy priviledge */ - iRet = RemTransact(remserver, &remserver->rc[0], pCon, remob->name, buf, &value, ">", NULL); + iRet = RemTransact(remserver, 0, pCon, remob->name, buf, &value, ">", NULL); if (iRet <= 0) { return 0.0; } @@ -437,7 +449,7 @@ static int RemobStatus(void *pData, SConnection *pCon) { assert(remob); - SCSave(&remob->server->conn, pCon); + remob->server->conn = SCSave(pCon, remob->server->conn); return remob->status; } /*---------------------------------------------------------------------------*/ @@ -453,25 +465,27 @@ static long RemobRun(void *self, SConnection *pCon, float fNew) { float fDelta; int rights; RemChannel *rc; - + int nChan; + remserver = remob->server; - SCSave(&remserver->conn, pCon); + remserver->conn = SCSave(pCon, remserver->conn); assert(remob); assert(pCon); rights = SCGetRights(pCon); - rc = &remserver->rc[rights <= usUser]; - RemConnect(remserver, &remserver->rc[0]); /* connect spy for listexe interest */ + nChan = rights <= usUser; + rc = &remserver->rc[nChan]; + RemConnect(remserver, 0); /* connect spy for listexe interest */ remob->status = HWIdle; snprintf(buf, sizeof(buf), "run %s %f", remob->name, fNew); - iRet = RemTransact(remserver, rc, pCon, buf, "!ERROR: somebody else", "!ERROR: cannot", ">", NULL); + iRet = RemTransact(remserver, nChan, pCon, buf, "!ERROR: somebody else", "!ERROR: cannot", ">", NULL); if (iRet <= 0) return 0; if (remserver->matchMap & 1) { /* already running, stop first */ remob->status = HWBusy; snprintf(sBuf, sizeof(sBuf), "stopexe %s", remob->name); - iRet = RemTransact(remserver, rc, pCon, sBuf, ">", NULL); + iRet = RemTransact(remserver, nChan, pCon, sBuf, ">", NULL); if (iRet <= 0) return 0; while (remob->status == HWBusy) { iRet = RemRead(rc, 1000); @@ -480,7 +494,7 @@ static long RemobRun(void *self, SConnection *pCon, float fNew) { RemCopy(rc, pCon); } } - iRet = RemTransact(remserver, rc, pCon, buf, ">", NULL); + iRet = RemTransact(remserver, nChan, pCon, buf, ">", NULL); if (iRet <= 0) return 0; } /* wait for "started" message */ @@ -534,12 +548,12 @@ int RemobAction(SConnection *pCon, SicsInterp *pSics, void *pData, float fValue; long lID; char *endp; - char *argv0; char *cmd; /* char acce[128], inte[128]; */ int rights; + int nChan; RemChannel *rc; assert(pCon); @@ -547,24 +561,25 @@ int RemobAction(SConnection *pCon, SicsInterp *pSics, void *pData, assert(remob); rights = SCGetRights(pCon); - rc = &remserver->rc[rights <= usUser]; + nChan = (rights <= usUser); + rc = &remserver->rc[nChan]; if (rights >= usUser) { - SCSave(&remserver->conn, pCon); + remserver->conn = SCSave(pCon, remserver->conn); } if (argc == 1) { - iRet = RemTransact(remserver, rc, pCon, argv[0], ">", NULL); + iRet = RemTransact(remserver, nChan, pCon, remob->name, ">", NULL); + } else if (strcasecmp(argv[1],"interest") == 0) { + /* ignore interest commands, as they would not work properly */ + iRet=1; } else if (strcasecmp(argv[1],"list") == 0) { /* snprintf(acce, sizeof(acce), "!%s.accesscode", remob->name); snprintf(inte, sizeof(inte), "!%s.interruptmode", remob->name); */ - argv0 = argv[0]; - argv[0] = remob->name; - cmd = Arg2Tcl(argc, argv, buf, sizeof buf); - argv[0] = argv0; + cmd = Arg2Tcl0(argc-1, argv+1, buf, sizeof buf, remob->name); if (cmd) { - RemTransact(remserver, rc, pCon, cmd, ">", NULL); + RemTransact(remserver, nChan, pCon, cmd, ">", NULL); if (cmd != buf) free(cmd); } iRet=1; @@ -573,7 +588,7 @@ int RemobAction(SConnection *pCon, SicsInterp *pSics, void *pData, for (i=1; i", NULL); + iRet = RemTransact(remserver, nChan, pCon, buf, ">", NULL); } return iRet; } @@ -622,7 +637,7 @@ int RemServerAction(SConnection *pCon, SicsInterp *pSics, void *pData, TokenList *pCurrent; TokenList *pName; int iRet; - int i; + socklen_t i; int pos; float fValue; long lID; @@ -630,7 +645,7 @@ int RemServerAction(SConnection *pCon, SicsInterp *pSics, void *pData, struct sockaddr_in adr; struct hostent *thishost; Remob *p, *next; - int rights; + int rights, nChan; RemChannel *rc; assert(pCon); @@ -638,9 +653,10 @@ int RemServerAction(SConnection *pCon, SicsInterp *pSics, void *pData, assert(remserver); rights = SCGetRights(pCon); - rc = &remserver->rc[rights <= usUser]; - if (rights >= usUser) { - SCSave(&remserver->conn, pCon); + nChan = (rights <= usUser); + rc = &remserver->rc[nChan]; + if (nChan) { + remserver->conn = SCSave(pCon, remserver->conn); } if (argc == 1) { serverport = IFindOption(pSICSOptions,"ServerPort"); @@ -659,7 +675,7 @@ int RemServerAction(SConnection *pCon, SicsInterp *pSics, void *pData, SCPrintf(pCon, eStatus, "%s = %s:%d %s:%s", argv[0], remserver->host, remserver->port, thishostname, serverport); } else if (argc>2 && strcasecmp(argv[1],"nowait") == 0) { - RemConnect(remserver, rc); + RemConnect(remserver, nChan); for (i=2; i2) { RemWrite(rc, " "); @@ -691,7 +707,7 @@ int RemServerAction(SConnection *pCon, SicsInterp *pSics, void *pData, for (i=1; i", NULL); + iRet = RemTransact(remserver, nChan, pCon, buf, ">", NULL); return iRet; } return 1; @@ -718,6 +734,7 @@ static void RemServerKill(void *self) { DeleteDescriptor(remserver->desc); if (remserver->name) free(remserver->name); if (remserver->host) free(remserver->host); + SCStoreFree(remserver->conn); free(remserver); } /*-----------------------------------------------------------------------*/ @@ -818,6 +835,7 @@ static RemServer *RemServerInit(char *name, char *host, int port) { rc->timeout = 0; } remserver->objList = NULL; + remserver->conn = NULL; if (!remserver->name || !remserver->host || !remserver->port) { @@ -827,6 +845,7 @@ static RemServer *RemServerInit(char *name, char *host, int port) { } remserver->taskActive = 1; remserver->interestActive = 0; + remserver->forwardMessages = 1; TaskRegister(pServ->pTasker, RemServerTask, NULL, RemServerKill, remserver, 1); return remserver; } @@ -855,8 +874,11 @@ int RemobCreate(SConnection *pCon, SicsInterp *pSics, void *pData, argtolower(argc,argv); if (argc >= 4 && strcmp(argv[1], "server") == 0) { - if (argc == 5) { + if (argc >= 5) { remserver = RemServerInit(argv[2], argv[3], atoi(argv[4])); + if (remserver && argc > 5 && argv[5][0] == '0') { + remserver->forwardMessages = 0; + } } else { p = strchr(argv[3], ':'); if (!p) { diff --git a/rs232controller.c b/rs232controller.c index 17fe6509..9d6c22ca 100644 --- a/rs232controller.c +++ b/rs232controller.c @@ -434,6 +434,42 @@ void getRS232Error(int iCode, char *errorBuffer, } } /*--------------------------------------------------------------------*/ +int fixRS232Error(prs232 self, int iCode){ + int i, status, read; + char buffer[8192]; + + switch(iCode){ + case BADMEMORY: + case FAILEDCONNECT: + return 0; + break; + case INCOMPLETE: + case TIMEOUT: + /* + * try to clear possibly pending stuff + */ + for(i = 0; i < 3; i++){ + if(availableRS232(self)){ + read = 8192; + readRS232(self,buffer,&read); + } + } + return 1; + break; + case NOTCONNECTED: + case BADSEND: + closeRS232(self); + status = initRS232(self); + if(status > 0){ + return 1; + } else { + return 0; + } + break; + } + return 0; +} +/*--------------------------------------------------------------------*/ int getRS232Timeout(prs232 self){ return self->timeout; } diff --git a/rs232controller.h b/rs232controller.h index beddb2d2..1628930c 100644 --- a/rs232controller.h +++ b/rs232controller.h @@ -65,6 +65,7 @@ void getRS232Error(int iCode, char *errorBuffer, int errorBufferLen); + int fixRS232Error(prs232 self, int iCode); int getRS232Timeout(prs232 self); int initRS232(prs232 self); int initRS232WithFlags(prs232 self, int flags); diff --git a/sans.tcl b/sans.tcl deleted file mode 100644 index 41bf129a..00000000 --- a/sans.tcl +++ /dev/null @@ -1,209 +0,0 @@ -# -------------------------------------------------------------------------- -# Initialization script the instrument SANS at SINQ -# -# Dr. Mark Koennecke, June 1997 -#--------------------------------------------------------------------------- -# O P T I O N S -set root "/data/koenneck/src/sics" -# first all the server options are set - -ServerOption SaveFile $root/bin/sansstat.tcl -# File to save the status of the instrument too - -ServerOption ReadTimeOut 10 -# timeout when checking for commands. In the main loop SICS checks for -# pending commands on each connection with the above timeout, has -# PERFORMANCE impact! - -ServerOption AcceptTimeOut 10 -# timeout when checking for connection req. -# Similar to above, but for connections - -ServerOption ReadUserPasswdTimeout 500000 -# time to wiat for a user/passwd to be sent from a client. Increase this -# if there is a problem connecting to a server due to network overload\ - -ServerOption LogFileBaseName $root/log/sanslog -# the path and base name of the internal server logfile to which all -# activity will be logged. - -ServerOption ServerPort 2915 -# the port number the server is going to listen at. The client MUST know -# this number in order to connect. It is in client.ini - -ServerOption InterruptPort 2917 -# The UDP port where the server will wait for Interrupts from clients. -# Obviously, clients wishing to interrupt need to know this number. - -# Telnet Options -ServerOption TelnetPort 1301 -ServerOption TelWord sicslogin - -# The token system -TokenInit connan - -#--------------------------------------------------------------------------- -# U S E R S - -# than the SICS users are specified -# Syntax: SicsUser name password userRightsCode -SicsUser Manager Joachim 1 -SicsUser User Kohl 2 -SicsUser Spy 007 3 - -#-------------------------------------------------------------------------- -# S I M P L E V A R I A B L E S - -# now a few general variables are created -# Syntax: VarMake name type access -# type can be one of: Text, Int, Float -#access can be one of: Internal, Mugger, user, Spy - -VarMake Instrument Text Internal -Instrument "SANS at SINQ,PSI" -#initialisation -Instrument lock - -VarMake title Text User -VarMake User Text User -User "Albert von Villigen" -VarMake SubTitle Text User -VarMake environment Text User -VarMake comment Text User -VarMake SampleName Text User -SampleName KohlSulfid -#----------- Initialize data storage stuff -VarMake SicsDataPath Text Mugger -SicsDataPath $root/data/ -SicsDataPath lock -VarMake SicsDataPrefix Text Mugger -SicsDataPrefix sans -SicsDataPrefix lock -VarMake SicsDataPostFix Text Mugger -SicsDataPostFix ".hdf" -SicsDataPostFix lock -MakeDataNumber SicsDataNumber $root/data/2001/DataNumber -InitSANS $root/sansdict.dic - - -#-------------------------------------------------------------------------- -# D E V I C E S : M O T O R S - -#Motor a4 EL734 LNSP22 4000 5 6 -# EL734 motor with parameters: hostname PortNumber Channel MotorID - -#Motor a4 EL734DC LNSP22 4000 5 6 -# EL734DC motor with parameters: hostname PortNumber Channel MotorID - -# Motor nam SIM -20. 20. 5. 1.0 -# Simulated motor with name nam, lower limit -20, upper limit +20, -# error ratio 5% and speed 1.0. Speed may be omitted - -# Motors for sample movement -Motor schi SIM -22.0 +22. 10. -Motor sphi SIM -22.0 +22.0 10. -Motor som EL734 lnsp25.psi.ch 4000 2 8 -Motor sax EL734 lnsp25.psi.ch 4000 2 6 -Motor say EL734 lnsp25.psi.ch 4000 2 7 -Motor saz EL734 lnsp25.psi.ch 4000 2 1 -Motor spos EL734 lnsp25.psi.ch 4000 2 5 - -#Motors for detector movement -Motor DetectorX EL734DC lnsp25.psi.ch 4000 3 1 -DetectorX Precision 0.5 -Motor DetectorY EL734DC lnsp25.psi.ch 4000 3 2 -DetectorY Precision 0.2 -Motor DetectorRotation EL734DC lnsp25.psi.ch 4000 3 3 -DetectorRotation Precision 0.1 - -#Motors for beamstop -Motor BeamStopX EL734 lnsp25.psi.ch 4000 2 3 -BeamStopX Precision 0.2 -Motor BeamStopY EL734 lnsp25.psi.ch 4000 2 4 -BeamStopY Precision 0.2 - - -#------------------------------------------------------------------------ -# Velocity selector -Motor tilt EL734 lnsp25.psi.ch 4000 2 2 -set dornen(Host) lnsp25.psi.ch -set dornen(Port) 4000 -set dornen(Channel) 6 -set dornen(Timeout) 3000 -VelocitySelector nvs tilt DORNIER dornen -nvs add -20 28800 -nvs add 3800 4500 -nvs add 5900 6700 -nvs add 8100 9600 -unset dornen - -#-------------------------------------------------------------------------- -# P R O C E D U R E S - -# create the drive command -MakeDrive -#-------------------------------------------------------------------------- -# MultiMotor is an object which groups several motors together. - -#--------------------------------- sample position -MakeMulti sample -# creates a MultiMotor with name sample -sample alias schi chi -# alias creates an internal name (chi) for motor schi -sample alias sphi phi -sample alias som omega -sample alias sax x -sample alias say y -sample alias saz z -sample pos Jo schi 0. sphi 0. som 45. sax 2. say 3. saz 10. -# define Jo as a named position. This means with sample Jo you'll reach -# the positions specified -sample pos Mo schi 0. sphi 0. som 180. sax 2. say 3. saz 10. -sample endconfig -# ends configuration of sample and install the command. This is REQUIRED - -#---------------------------------- detector position -MakeMulti detector -detector alias DetectorX X -detector alias DetectorY Y -detector alias DetectorRotation phi -detector endconfig - -#----------------------------------- beamstop -MakeMulti BeamStop -BeamStop alias BeamStopX X -BeamStop alias BeamStopY Y -BeamStop pos out BeamStopX 817. -BeamStop endconfig -#------------------------------------ Shortcuts -SicsAlias BeamStop bs -SicsAlias detector dt -#------------------------------------ Histogram Memory -MakeCounter counter EL737 lnsp25.psi.ch 4000 4 -counter SetExponent 6 - -MakeHM banana SINQHM -banana configure HistMode Normal -banana configure OverFlowMode Ceil -banana configure Rank 1 -banana configure Length 16384 -banana configure BinWidth 4 -banana preset 100. -banana CountMode Timer -banana configure HMComputer lnse02.psi.ch -banana configure HMPort 2400 -banana configure Counter counter -banana init -banana exponent 6 -#----------------------------------------------------------------------------- -# R U E N B U F F E R -MakeRuenBuffer - -#---------------------- some special Tcl commands -source $root/bin/log.tcl -Publish LogBook User - -source $root/bin/count.tcl -Publish count User -Publish Repeat User - diff --git a/savehdb.c b/savehdb.c new file mode 100644 index 00000000..b4b29523 --- /dev/null +++ b/savehdb.c @@ -0,0 +1,80 @@ +#include +#include "dynstring.h" +#include "statusfile.h" +#include "sicshipadaba.h" + +static void SaveHdbBranch(pHdb node, FILE *fil) { + pHdb child; + char prop[16]; + pDynString dyn; + char path[1024]; + + if (GetHdbProperty(node, "save", prop, sizeof prop)) { + if (strcmp(prop, "me") == 0) { + dyn = formatValue(node->value, node); + GetHdbPath(node, path, sizeof path); + fprintf(fil, "hupdate %s %s\n", path, GetCharArray(dyn)); + DeleteDynString(dyn); + } + for (child = node->child; child != NULL; child = child->next) { + SaveHdbBranch(child, fil); + } + } +} + +static int SaveHdbTree(void *object, char *name, FILE *fil) { + pHdb node; + + SaveHdbBranch(GetHipadabaRoot(), fil); + return 1; +} + +static hdbCallbackReturn SaveHdbCallback(pHdb node, void *userData, + pHdbMessage message) { + pHdbDataMessage mm = NULL; + + if((mm = GetHdbUpdateMessage(message)) == NULL){ + return hdbContinue; + } + + StatusFileDirty(); + return hdbContinue;; +} + +static int SaveHdbEnable(SConnection *con, SicsInterp *sics, + void *data, int argc, char *argv[]) { + pHdb node; + char prop[16]; + pHdbCallback cb; + + if (argc < 2) { + SCPrintf(con, eError, "ERROR: should be: %s ", argv[0]); + return 0; + } + + node = FindHdbNode(NULL, argv[1], con); + if (!node) { + SCPrintf(con, eError, "ERROR: %s not found", argv[1]); + return 0; + } + cb = MakeHipadabaCallback(SaveHdbCallback, NULL, NULL); + assert(cb); + AppendHipadabaCallback(node,cb); + + SetHdbProperty(node, "save", "me"); + for (node = node->mama; node != NULL; node = node->mama) { + if (!GetHdbProperty(node, "save", prop, sizeof prop)) { + SetHdbProperty(node, "save", "kids"); + } + } + StatusFileDirty(); + return 1; +} + +void SaveHdbInit(void) { + pDummy hs = NULL; + + hs = CreateDummy("hdb saver"); + hs->pDescriptor->SaveStatus = SaveHdbTree; + AddCommandWithFlag(pServ->pSics, "hsave", SaveHdbEnable, KillDummy, hs, 0); +} diff --git a/scan.c b/scan.c index 4911cef9..e61a3d48 100644 --- a/scan.c +++ b/scan.c @@ -209,8 +209,6 @@ static void ConfigureScanDict(pStringDict dict) self->ScanDrive = ScanDrive; self->ScanCount = ScanCount; self->CollectScanData = CollectScanData; - self->posSoft = 0; - return 1; } /*-------------------------------------------------------------------------*/ @@ -385,6 +383,7 @@ int AppendScanLine(pScanData self, char *line) } /*-------------------------------------------------------------------------*/ extern char *stptok(const char *s, char *t, int len, char *brk); +extern char *trim(char *txt); int StoreScanCounts(pScanData self, char *data) { @@ -402,7 +401,7 @@ int StoreScanCounts(pScanData self, char *data) InitCountEntry(&sCount); /* parse the data */ - pPtr = data; + pPtr = trim(data); pPtr = stptok(pPtr,pNumber,29," \t"); if(pPtr != NULL) { @@ -548,10 +547,6 @@ CountEntry CollectCounterData(pScanData self) { /*--------- drive */ iRet = self->ScanDrive(self,i); - if(!iRet) - { - return 0; - } /* finished, check for interrupts. Whatever happened, user interrupt or HW interrupt, it will be on our connection */ @@ -580,13 +575,15 @@ CountEntry CollectCounterData(pScanData self) return 0; break; } + if(!iRet) + { + SCWrite(self->pCon,"WARNING: skipped scan point after drive failure", + eWarning); + continue; + } + /*-------------- count */ iRet = self->ScanCount(self, i); - if(!iRet) - { - return 0; - } - /* finished, check for interrupts. Whatever happened, user interrupt or HW interrupt, it will be on our connection */ @@ -611,6 +608,13 @@ CountEntry CollectCounterData(pScanData self) return 0; break; } + if(!iRet) + { + SCWrite(self->pCon,"WARNING: skipped scan point after count failure", + eWarning); + continue; + } + /*-------- scan post processing */ self->CollectScanData(self,i); InvokeCallBack(self->pCall,SCANPOINT,self); @@ -1067,10 +1071,6 @@ CountEntry CollectCounterData(pScanData self) } } /*---------------------------------------------------------------------*/ -int isScanVarSoft(pScanData self){ - return self->posSoft; -} -/*-------------------------------------------------------------------------*/ int GetScanVarStep(pScanData self, int iWhich, float *fStep) { pVarEntry pVar = NULL; @@ -1540,7 +1540,7 @@ static int PrintCountsOrMonitors(pScanData self, SConnection *pCon, if(iWhich < 0) { GetScanCounts(self,lData,self->iNP); - snprintf(pBueffel,59,"%s.counts = {", name); + snprintf(pBueffel,59,"%s.Counts = {", name); } else { @@ -1584,7 +1584,7 @@ static int PrintTimes(pScanData self, SConnection *pCon, snprintf(pBueffel,59,"%s.scantimes = { ",name); DynStringCopy(data,pBueffel); - for(i = 0; i < self->iNP; i++) + for(i = 0; i < self->iCounts; i++) { DynarGet(self->pCounts,i,&pPtr); pData = (pCountEntry)pPtr; @@ -2100,14 +2100,6 @@ static int DumpScan(pScanData self, SConnection *pCon) SCSendOK(pCon); return 1; } - else if(strcmp(argv[2],"soft") == 0) - { - ResetScanFunctions(self); - self->CollectScanData = CollectScanDataJochen; - self->posSoft = 1; - SCSendOK(pCon); - return 1; - } else { site = getSite(); @@ -2127,12 +2119,17 @@ static int DumpScan(pScanData self, SConnection *pCon) } /*------------ functions */ else if(strcmp(argv[1],"function") == 0) - { - return InterpretScanFunctions(self, pCon, argc, argv); + { + return InterpretScanFunctions(self, pCon, argc, argv); } /*---------- scan */ else if(strcmp(argv[1],"run") == 0) { + if (argc < 5) { + sprintf(pBueffel,"ERROR: not enough arguments for %s run",argv[0]); + SCWrite(pCon,pBueffel,eError); + return 0; + } /* get NP */ iRet = Tcl_GetInt(InterpGetTcl(pSics),argv[2],&lNP); if(iRet != TCL_OK) @@ -2181,6 +2178,11 @@ static int DumpScan(pScanData self, SConnection *pCon) /*---------- silent */ else if(strcmp(argv[1],"silent") == 0) { + if (argc < 5) { + sprintf(pBueffel,"ERROR: not enough arguments for %s silent",argv[0]); + SCWrite(pCon,pBueffel,eError); + return 0; + } /* get NP */ iRet = Tcl_GetInt(InterpGetTcl(pSics),argv[2],&lNP); if(iRet != TCL_OK) @@ -2221,6 +2223,11 @@ static int DumpScan(pScanData self, SConnection *pCon) } return iRet; } +/*----------- getnumchan */ + else if(strcmp(argv[1],"getnumchan") == 0) { + snprintf(pBueffel,511,"%s.getnumchan = %d", argv[0], GetNMonitor((pCounter)self->pCounterData)); + SCWrite(pCon,pBueffel,eValue); + } /*----------- setchannel */ else if(strcmp(argv[1],"setchannel") == 0) { diff --git a/scan.h b/scan.h index aeea5377..09ad15b7 100644 --- a/scan.h +++ b/scan.h @@ -39,7 +39,6 @@ char *pName, int iLength); int GetScanVarStep(pScanData self, int iWhich, float *fStep); - int isScanVarSoft(pScanData self); int GetScanMonitor(pScanData self, int iWhich, long *lData, int iDataLen); int GetScanNP(pScanData self); diff --git a/scan.i b/scan.i index 5bf55307..52dae69f 100644 --- a/scan.i +++ b/scan.i @@ -46,7 +46,6 @@ int iP); pStringDict scanFunctions; long lPos; - int posSoft; void *pCounterData; char pCounterName[512]; int iChannel; diff --git a/scan.tex b/scan.tex index 4f5f46e4..373c7c68 100644 --- a/scan.tex +++ b/scan.tex @@ -52,6 +52,7 @@ $\langle$scanvar {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ float fStep;@\\ \mbox{}\verb@ float *fData;@\\ \mbox{}\verb@ int dataList;@\\ +\mbox{}\verb@ int logVar;@\\ \mbox{}\verb@ }VarEntry, *pVarEntry;@\\ \mbox{}\verb@@$\diamond$ \end{list} @@ -88,6 +89,14 @@ $\langle$scanvarint {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ pVarEntry MakeScanVar(SicsInterp *pSics, SConnection *pCon, char@\\ \mbox{}\verb@ *name, float start, float step);@\\ \mbox{}\verb@ /**@\\ +\mbox{}\verb@ * make a variable which is logged during the scan but not driven.@\\ +\mbox{}\verb@ * @{\tt @}\verb@param pSics The interpreter in order to locate the variable.@\\ +\mbox{}\verb@ * @{\tt @}\verb@param pCon A connection object for error reporting@\\ +\mbox{}\verb@ * @{\tt @}\verb@param name The name of the variable to log@\\ +\mbox{}\verb@ */@\\ +\mbox{}\verb@ pVarEntry MakeLogVar(SicsInterp *pSics, @\\ +\mbox{}\verb@ SConnection *pCon, char *name);@\\ +\mbox{}\verb@ /**@\\ \mbox{}\verb@ * InitScanVar clears the list of scan points@\\ \mbox{}\verb@ * @{\tt @}\verb@param pvar The scna variable to clear@\\ \mbox{}\verb@ */@\\ @@ -145,6 +154,21 @@ $\langle$scanvarint {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ * @{\tt @}\verb@param np The number of slots in fData.@\\ \mbox{}\verb@ */@\\ \mbox{}\verb@ void CopyScanVar(pVarEntry pVar, float *fData, int np);@\\ +\mbox{}\verb@ /**@\\ +\mbox{}\verb@ * CheckScanVar checks if the scan variable can be driven through the@\\ +\mbox{}\verb@ * whole range.@\\ +\mbox{}\verb@ * @{\tt @}\verb@param pVar The scan variable to check@\\ +\mbox{}\verb@ * @{\tt @}\verb@param pCon The connection object to which to report errors.@\\ +\mbox{}\verb@ * @{\tt @}\verb@param np The number of points to check for.@\\ +\mbox{}\verb@ * @{\tt @}\verb@return 0 on failuyre, 1 on success@\\ +\mbox{}\verb@ */@\\ +\mbox{}\verb@ int CheckScanVar(pVarEntry pVar, SConnection *pCon, int np);@\\ +\mbox{}\verb@ /**@\\ +\mbox{}\verb@ * queries if the variable is alogged variable or a drive one.@\\ +\mbox{}\verb@ * @{\tt @}\verb@param pVar The variable to query.@\\ +\mbox{}\verb@ * @{\tt @}\verb@return 1 if log var, 0 else@\\ +\mbox{}\verb@ */@\\ +\mbox{}\verb@ int isLogVar(pVarEntry pVar);@\\ \mbox{}\verb@@$\diamond$ \end{list} \vspace{-1ex} @@ -198,7 +222,6 @@ $\langle$scandata {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ int iP);@\\ \mbox{}\verb@ pStringDict scanFunctions;@\\ \mbox{}\verb@ long lPos;@\\ -\mbox{}\verb@ int posSoft;@\\ \mbox{}\verb@ void *pCounterData;@\\ \mbox{}\verb@ char pCounterName[512];@\\ \mbox{}\verb@ int iChannel;@\\ @@ -329,7 +352,6 @@ $\langle$scaninter {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ char *pName, int iLength);@\\ \mbox{}\verb@ int GetScanVarStep(pScanData self, int iWhich, @\\ \mbox{}\verb@ float *fStep);@\\ -\mbox{}\verb@ int isScanVarSoft(pScanData self);@\\ \mbox{}\verb@ int GetScanMonitor(pScanData self, int iWhich, @\\ \mbox{}\verb@ long *lData, int iDataLen);@\\ \mbox{}\verb@ int GetScanNP(pScanData self);@\\ @@ -451,6 +473,15 @@ $\langle$stdscan {\footnotesize ?}$\rangle\equiv$ \begin{list}{}{} \item \mbox{}\verb@@\\ \mbox{}\verb@ /**@\\ +\mbox{}\verb@ * make a filename according to SICS rules for this scan@\\ +\mbox{}\verb@ */@\\ +\mbox{}\verb@ char *ScanMakeFileName(SicsInterp *pSics, SConnection *pCon);@\\ +\mbox{}\verb@ /*@\\ +\mbox{}\verb@ * write the header bits from the template@\\ +\mbox{}\verb@ */@\\ +\mbox{}\verb@ void WriteTemplate(FILE *fd, FILE *temp, char *filename, pScanData pScan, @\\ +\mbox{}\verb@ SConnection *pCon, SicsInterp *pSics);@\\ +\mbox{}\verb@ /**@\\ \mbox{}\verb@ * write the header of the scan file@\\ \mbox{}\verb@ */@\\ \mbox{}\verb@ int WriteHeader(pScanData self);@\\ @@ -474,6 +505,10 @@ $\langle$stdscan {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ */ @\\ \mbox{}\verb@ int NonCheckPrepare(pScanData self);@\\ \mbox{}\verb@ /**@\\ +\mbox{}\verb@ * prepare for a scan without complaining...@\\ +\mbox{}\verb@ */@\\ +\mbox{}\verb@ int SilentPrepare(pScanData self);@\\ +\mbox{}\verb@ /**@\\ \mbox{}\verb@ * ScanDrive handles driving to the scan point iPoint.@\\ \mbox{}\verb@ */ @\\ \mbox{}\verb@ int ScanDrive(pScanData self, int iPoint);@\\ @@ -494,6 +529,7 @@ $\langle$stdscan {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ */@\\ \mbox{}\verb@ int CollectScanData(pScanData self, int iPoint);@\\ \mbox{}\verb@ int CollectScanDataJochen(pScanData self, int iPoint);@\\ +\mbox{}\verb@ int CollectSilent(pScanData self, int iPoint);@\\ \mbox{}\verb@/*===================================================================*/@\\ \mbox{}\verb@ /**@\\ \mbox{}\verb@ * Script invocation for writing the scan header.@\\ diff --git a/scan.w b/scan.w index 545394a4..960e758e 100644 --- a/scan.w +++ b/scan.w @@ -47,6 +47,7 @@ Scan variables are held in this data structure: float fStep; float *fData; int dataList; + int logVar; }VarEntry, *pVarEntry; @} The VarEntry structure holds the data for each single scan variable. @@ -69,6 +70,14 @@ Scan variables have an interface: */ pVarEntry MakeScanVar(SicsInterp *pSics, SConnection *pCon, char *name, float start, float step); + /** + * make a variable which is logged during the scan but not driven. + * @@param pSics The interpreter in order to locate the variable. + * @@param pCon A connection object for error reporting + * @@param name The name of the variable to log + */ + pVarEntry MakeLogVar(SicsInterp *pSics, + SConnection *pCon, char *name); /** * InitScanVar clears the list of scan points * @@param pvar The scna variable to clear @@ -127,6 +136,21 @@ Scan variables have an interface: * @@param np The number of slots in fData. */ void CopyScanVar(pVarEntry pVar, float *fData, int np); + /** + * CheckScanVar checks if the scan variable can be driven through the + * whole range. + * @@param pVar The scan variable to check + * @@param pCon The connection object to which to report errors. + * @@param np The number of points to check for. + * @@return 0 on failuyre, 1 on success + */ + int CheckScanVar(pVarEntry pVar, SConnection *pCon, int np); + /** + * queries if the variable is alogged variable or a drive one. + * @@param pVar The variable to query. + * @@return 1 if log var, 0 else + */ + int isLogVar(pVarEntry pVar); @} \subsubsection{The Scan Object} @@ -168,7 +192,6 @@ Scan variables have an interface: int iP); pStringDict scanFunctions; long lPos; - int posSoft; void *pCounterData; char pCounterName[512]; int iChannel; @@ -288,7 +311,6 @@ functions: char *pName, int iLength); int GetScanVarStep(pScanData self, int iWhich, float *fStep); - int isScanVarSoft(pScanData self); int GetScanMonitor(pScanData self, int iWhich, long *lData, int iDataLen); int GetScanNP(pScanData self); @@ -396,6 +418,15 @@ format of this template is documented in the SICS managers documentation. @d stdscan @{ + /** + * make a filename according to SICS rules for this scan + */ + char *ScanMakeFileName(SicsInterp *pSics, SConnection *pCon); + /* + * write the header bits from the template + */ + void WriteTemplate(FILE *fd, FILE *temp, char *filename, pScanData pScan, + SConnection *pCon, SicsInterp *pSics); /** * write the header of the scan file */ @@ -419,6 +450,10 @@ documentation. * second version of PrepareScan which does not check scan limits */ int NonCheckPrepare(pScanData self); + /** + * prepare for a scan without complaining... + */ + int SilentPrepare(pScanData self); /** * ScanDrive handles driving to the scan point iPoint. */ @@ -440,6 +475,7 @@ documentation. */ int CollectScanData(pScanData self, int iPoint); int CollectScanDataJochen(pScanData self, int iPoint); + int CollectSilent(pScanData self, int iPoint); /*===================================================================*/ /** * Script invocation for writing the scan header. diff --git a/scanvar.h b/scanvar.h index 7bb1dbfb..e73c5080 100644 --- a/scanvar.h +++ b/scanvar.h @@ -21,7 +21,7 @@ float fStep; float *fData; int dataList; - int logVar; + int logVar; }VarEntry, *pVarEntry; /*---------------------------------------------------------------------*/ @@ -35,22 +35,21 @@ * @param start The start position from which to scan * @param step The step width with which to scan. * @return A pointer to a new scan variable object on success, NULL - * else NULL + * else */ pVarEntry MakeScanVar(SicsInterp *pSics, SConnection *pCon, char *name, float start, float step); - /* - * Make a variable which is logged during the scan - * @param pSics The SICS interpreter to search for the variable - * @param pCon A connection to report problems to - * @param name The name of the variable to log - * @return A pointer to a new scan variable object on success, NULL - * else NULL + /** + * make a variable which is logged during the scan but not driven. + * @param pSics The interpreter in order to locate the variable. + * @param pCon A connection object for error reporting + * @param name The name of the variable to log */ - pVarEntry MakeLogVar(SicsInterp *pSics, SConnection *pCon, char *name); + pVarEntry MakeLogVar(SicsInterp *pSics, + SConnection *pCon, char *name); /** * InitScanVar clears the list of scan points - * @param pvar The scan variable to clear + * @param pvar The scna variable to clear */ void InitScanVar(pVarEntry pVar); /** @@ -76,14 +75,6 @@ * @return The step width for the scan. */ float ScanVarStep(pVarEntry pVar); - /** - * check if the scan is possible, i.e the scan variable does - * not violate the limits in the scan - * @param pVar The scan variable to check - * @param pCon The connection to report errors to - * @return 1 when OK, 0 when the limits are violated. - */ - int CheckScanVar(pVarEntry pVar, SConnection *pCon, int np); /** * StartScanVar starts the scan variable to drive to the next * position. @@ -115,7 +106,19 @@ */ void CopyScanVar(pVarEntry pVar, float *fData, int np); /** - * enquires if a variable is a logged variable or a proper scan variable - */ + * CheckScanVar checks if the scan variable can be driven through the + * whole range. + * @param pVar The scan variable to check + * @param pCon The connection object to which to report errors. + * @param np The number of points to check for. + * @return 0 on failuyre, 1 on success + */ + int CheckScanVar(pVarEntry pVar, SConnection *pCon, int np); + /** + * queries if the variable is alogged variable or a drive one. + * @param pVar The variable to query. + * @return 1 if log var, 0 else + */ int isLogVar(pVarEntry pVar); + #endif diff --git a/scontroller.c b/scontroller.c deleted file mode 100644 index 141a59a4..00000000 --- a/scontroller.c +++ /dev/null @@ -1,282 +0,0 @@ -/*-------------------------------------------------------------------------- - - Some code to connect to a serial port through the SINQ system. - - Just a wrapper around serialsinq derived from David Maden's - EL734 routines. - - You are free to use and modify this software for noncommercial - usage. - - No warranties or liabilities of any kind taken by me or my employer - - Very primitive device to send commands to a motor from Tcl - - Modified version for use within SICS. -----------------------------------------------------------------------------*/ -#include "sinq_prototypes.h" -/* #include */ -#include -#include -#include -#include -#include "rs232c_def.h" -#include "el734_def.h" -#include "psi/hardsup/serialsinq.h" -#include "sics.h" - -#define False 0 -#define True 1 - -/*--------------------------------------------------------------------------- - Tcl has a high niceness level. It deletes a command properly when - exiting, reinitializing etc. I use this facility to kill off the - motor initialised in Controller. ----------------------------------------------------------------------------*/ -EXTERN void SerialMurder(ClientData pData) -{ - /* - NetReadRemoveUserSocket(pServ->pReader, SerialGetSocket(&(pData))); - */ - SerialClose(&(pData)); - free(pData); -} -/*------------------ a forward declaration -----------------------------*/ -EXTERN int SurielSend(ClientData clientData, Tcl_Interp *interp, - int argc, char *argv[]); - -/*---------------------------------------------------------------------------- - Controller is the main entry point for this stuff. It connects to a motor - and, on success, creates a new command with the name of the motor. - Syntax: - Controller name host port channel ----------------------------------------------------------------------------*/ - -int Controller(ClientData clientData, Tcl_Interp *interp, - int argc, char *argv[]) -{ - int iRet; - int iPort, iChannel, iMotor; - char *pErr = NULL; - char pBueffel[80]; - void **pData = NULL; - - /* check arguments */ - if(argc < 5) - { - Tcl_AppendResult(interp, - " Insufficient arguments: Controller name host port channel index" - , (char *) NULL); - return TCL_ERROR; - } - - /* convert arguments */ - iRet = Tcl_GetInt(interp,argv[3],&iPort); - if(iRet == TCL_ERROR) - { - Tcl_AppendResult(interp,"Need integer value for port", - (char *)NULL); - return iRet; - } - - iRet = Tcl_GetInt(interp,argv[4],&iChannel); - if(iRet == TCL_ERROR) - { - Tcl_AppendResult(interp,"Need integer value for channel", - (char *)NULL); - return iRet; - } - - iMotor = 1; - - - /* make a new pointer, initialise EL734st */ - pData = malloc(sizeof(void *)); - if(pData ==NULL) - { - Tcl_AppendResult(interp,"No memory in SerialSinq",NULL); - return TCL_ERROR; - } - - /* check for optional force flag */ - if(argc > 5) - { - iRet = SerialForceOpen(pData, argv[2],iPort,iChannel); - } - - /* open the motor, finally */ - iRet = SerialOpen(pData, argv[2],iPort,iChannel); - if(iRet == 1) /* success */ - { - /* handle TCL, create new command: the serial line */ - Tcl_CreateCommand(interp,strdup(argv[1]),SurielSend, - *pData,SerialMurder); - strcpy(pBueffel,argv[1]); - Tcl_AppendResult(interp,strdup(argv[1]),(char *)NULL); - if(pServ->pReader != NULL) - { - NetReadRegisterUserSocket(pServ->pReader, - SerialGetSocket(pData)); - } - return TCL_OK; - } - else - { - SerialError(iRet,pBueffel,79); - Tcl_AppendResult(interp,pBueffel,(char *) NULL); - SerialClose(pData); - free(pData); - return TCL_ERROR; - } -} -/*-------------------------------------------------------------------------- - Now, this interprets some commands: - -tmo val - will set a timeout value for the connection. Everything - else will be concatenated and sent to the device. - -sendterm text - defines the terminator which will be appended automatically - to each command. - -replyterm text - sets the expected reply terminator from the Macintosh - Serial Port server. - -----------------------------------------------------------------------------*/ -EXTERN int SurielSend(ClientData clientData, Tcl_Interp *interp, - int argc, char *argv[]) -{ - char pBueffel[256]; - char pAnswer[256]; - char *pErr = NULL; - int iRet, iMSR; - int i; - void *pData; - - pData = clientData; - - if(argc >= 2) - { - if(strcmp(argv[1],"-tmo") == 0) - { - if(argc >= 3) - { - iRet = Tcl_GetInt(interp,argv[2],&iMSR); - if(iRet != TCL_OK) - { - sprintf(pBueffel,"%s is not a valid number",argv[2]); - Tcl_AppendResult(interp,pBueffel,NULL); - return TCL_ERROR; - } - SerialConfig(&(pData),iMSR); - return TCL_OK; - } - else - { - Tcl_AppendResult(interp, "Expected parameter after -tmo",NULL); - return TCL_ERROR; - } - } - else if(strcmp(argv[1],"-sendterm") == 0) - { - if(argc < 3) - { - Tcl_AppendResult(interp, "Expected parameter after -tmo",NULL); - return TCL_ERROR; - } - iRet = SerialSendTerm(&(pData),argv[2]); - if(iRet != 1) - { - Tcl_AppendResult(interp,"To many characters for terminator",NULL); - return TCL_ERROR; - } - return TCL_OK; - } - else if(strcmp(argv[1],"-replyterm") == 0) - { - if(argc < 3) - { - Tcl_AppendResult(interp, "Expected parameter after -tmo",NULL); - return TCL_ERROR; - } - iRet = SerialATerm(&(pData),argv[2]); - if(!iRet) - { - Tcl_AppendResult(interp,"To many characters for terminator",NULL); - return TCL_ERROR; - } - return TCL_OK; - } - else if(strcmp(argv[1],"-put") == 0) - { - NetReadResetUser(pServ->pReader,SerialGetSocket(&(pData))); - if(argc > 2) - { - strcpy(pBueffel,argv[2]); - } - for(i = 3; i < argc; i++) - { - strcat(pBueffel," "); - strcat(pBueffel,argv[i]); - } - iRet = SerialSend(&(pData),pBueffel); - if(iRet != 1) - { - Tcl_AppendResult(interp,pAnswer,NULL); - return TCL_ERROR; - } - return TCL_OK; - } - else if(strcmp(argv[1],"-readable") == 0) - { - iRet = NetReadReadable(pServ->pReader,SerialGetSocket(&(pData))); - if(iRet) - { - Tcl_AppendResult(interp,"1",NULL); - return TCL_OK; - } - else - { - Tcl_AppendResult(interp,"0",NULL); - return TCL_OK; - } - } - else if(strcmp(argv[1],"-get") == 0) - { - if(NetReadReadable(pServ->pReader,SerialGetSocket(&(pData)))) - { - iRet = SerialReceive(&(pData),pAnswer, 255); - Tcl_AppendResult(interp,pAnswer,NULL); - if(iRet == 1) - { - return TCL_OK; - } - else - { - return TCL_ERROR; - } - } - Tcl_AppendResult(interp,"Not Readable",NULL); - return TCL_ERROR; - } - } - - - if(argc >= 2) - { - strcpy(pBueffel,argv[1]); - } - for(i = 2; i < argc; i++) - { - strcat(pBueffel," "); - strcat(pBueffel,argv[i]); - } - i = strlen(pBueffel); - iRet = SerialWriteRead(&(pData),pBueffel,pAnswer,254); - if(iRet != 1) - { - Tcl_AppendResult(interp,pAnswer,NULL); - return TCL_ERROR; - } - Tcl_AppendResult(interp,pAnswer,NULL); - return TCL_OK; -} diff --git a/scriptcontext.c b/scriptcontext.c new file mode 100644 index 00000000..4f8524de --- /dev/null +++ b/scriptcontext.c @@ -0,0 +1,1035 @@ +#include +#include +#include +#include +#include "sics.h" +#include "sicsobj.h" +#include "splitter.h" +#include "initializer.h" +#include "commandlog.h" +#include "hipadaba.h" +#include "sicshipadaba.h" +#include "dynstring.h" +#include "devser.h" +#include "ascon.h" +#include "macro.h" +#include "scriptcontext.h" + + +#define MAX_HDB_PATH 1024 + +typedef struct ContextItem { + struct ContextItem *next; + Hdb *node; + Hdb *controllerNode; +} ContextItem; + +typedef struct ScriptContext { + ObjectDescriptor *desc; + ContextItem *nodes; + ContextItem *trash; +} ScriptContext; + +struct SctController { + DevSer *devser; + Hdb *node; /* the controller node */ + SCStore *conn; + int verbose; +}; + +/* action data and write callback data */ +typedef struct SctData { + char *name; + SctController *controller; + SCStore *conCtx; + int answered; + Hdb *node; +} SctData; + +static ScriptContext *sct = NULL; +static SCStore *currentCon = NULL; + +static struct { + char *name; +} actionCallback; + +static char *mainCallback = "main callback"; + +void PushContext(Hdb *node, Hdb *controllerNode) { + ContextItem *new; + + if (sct->trash == NULL) { + new = calloc(1, sizeof(*new)); + } else { + new = sct->trash; + sct->trash = sct->trash->next; + } + new->next = sct->nodes; + sct->nodes = new; + new->node = node; + new->controllerNode = controllerNode; +} + +void PopContext(void) { + ContextItem *c; + + c = sct->nodes; + assert(c); + sct->nodes = c->next; + c->next = sct->trash; + sct->trash = c; +} + +void CleanStack(Hdb *node) { + ContextItem *s; + + /* clean context from killed nodes */ + for (s = sct->nodes; s != NULL; s = s->next) { + if (s->node == node) { + s->node = NULL; + } + if (s->controllerNode == node) { + s->controllerNode = NULL; + } + } +} + +static void SetProp(Hdb *node, Hdb *cNode, char *key, char *value) { + char *val; + + if (node == NULL) { + if (cNode == NULL) return; + node = cNode; + } else { + val = GetHdbProp(node, key); + if (val == NULL && cNode != NULL) { + val = GetHdbProp(cNode, key); + if (val != NULL) { + node = cNode; + } + } + } + if (value == NULL) { + if (GetHdbProperty(node, key, NULL, 0) > 0) { + SetHdbProperty(node, key, ""); + } + } else { + SetHdbProperty(node, key, value); + } +} + +static char *GetProp(Hdb *node, Hdb *cNode, char *key) { + char *val; + + if (node != NULL) { + val = GetHdbProp(node, key); + if (val != NULL) return val; + } + if (cNode != NULL) { + val = GetHdbProp(cNode, key); + } + return val; +} + +/* + * This is the actual sct command available in scripts. + */ +int SctCommand(SConnection *con, SicsInterp *sics, void *object, + int argc, char *argv[]) { + static char value[1024]; + char *val; + char error[512]; + Hdb *node = NULL; + Hdb *cNode = NULL; + hdbValue v; + double dtime; + + assert(sct == object); + if (sct->nodes != NULL) { + node = sct->nodes->node; + cNode = sct->nodes->controllerNode; + } + if (node == NULL && cNode == NULL) { + SCPrintf(con, eError, "ERROR: %s may be called only in proper context", + argv[0]); + return 0; + } + if (argc <= 1) { + GetHdbPath(node, value, sizeof value); + SCWrite(con, value, eValue); + return 1; + } + + /* + * update command + */ + if(strcmp(argv[1],"update") == 0){ + cloneHdbValue(&node->value, &v); + Arg2Text(argc-2, argv+2, value, sizeof value); + if(!readHdbValue(&v, value, error, 512)){ + SCWrite(con, error, eError); + return 0; + } + UpdateHipadabaPar(node,v,con); + SetHdbProperty(node,"geterror", NULL); + return 1; + } + + /* + * print + */ + if(strcmp(argv[1],"print") == 0){ + Arg2Text(argc-2, argv+2, value, sizeof value); + SCWrite(con,value,eWarning); + return 1; + } + + /* + * time stamping + */ + if(strcmp(argv[1],"utime") == 0){ + if(argc < 3){ + SCWrite(con,"ERROR: need property to write time stamp too", + eError); + return 0; + } + dtime = DoubleTime(); + snprintf(value,1024,"%.3f",dtime); + SetHdbProperty(node,argv[2], value); + return 1; + } + + /* + * property handling + */ + if (argc == 2) { /* get case */ + val = GetProp(node, cNode, argv[1]); + if (val == NULL) { + SCPrintf(con, eError, "ERROR: %s %s not found", argv[0], argv[1]); + return 0; + } + SCWrite(con, val, eValue); + } else { /* set case */ + if (argc == 3) { + SetProp(node, cNode, argv[1], argv[2]); + } else { + val = Arg2Tcl(argc-2, argv+2, value, sizeof value); + SetProp(node, cNode, argv[1], val); + if (val != NULL && val != value) free(val); + } + } + return 1; +} + +int SctCallInContext(SConnection *con, char *script, Hdb *node, + SctController *controller, char **resPtr) { + Tcl_Interp *pTcl = InterpGetTcl(pServ->pSics); + int ret, l; + char *result = NULL; + int iRet = 1; + int verbose = controller->verbose; + + PushContext(node, controller->node); + if (verbose) { + SCPrintf(con, eInError, "\nscript: %s\n", script); + } + + MacroPush(con); + l = strlen(script); + ret = Tcl_EvalEx(pTcl, script, l, 0); + result = (char *)Tcl_GetStringResult(pTcl); + if (ret != TCL_OK && result[0]!='\0') { + if (verbose) { + SCPrintf(con, eInError, "\nerror: %s\n", result); + } + iRet = 0; + } + *resPtr = result; + + MacroPop(); + PopContext(); + return iRet; +} + +static int SctMatch(void *data1, void *data2) { + SctData *a = data1; + SctData *b = data2; + + return a->node == b->node && strcasecmp(a->name, b->name) == 0; +} + +static char *SctActionHandler(void *actionData, char *lastReply) { + SctData *data = actionData; + Hdb *node = data->node; + SctController *controller = data->controller; + char *state; + char *result; + char *script; + char *send = NULL; + int i; + SConnection *con; + char timeKey[50], timeVal[50]; + + if (currentCon) { + con = SCStorePush(currentCon); + } else { + con = SCStorePush(controller->conn); + } + SetProp(node, controller->node, "result", lastReply); + if (controller->verbose && lastReply != NULL && *lastReply != '\0') { + SCPrintf(con, eWarning, "reply : %s", lastReply); + } + state = GetProp(node, controller->node, "state"); + if (state == NULL || strcasecmp(state, "idle") == 0) { + state = data->name; + SetProp(node, controller->node, "state", state); + } + for (i = 0; i < 10; i++) { + SetProp(node, controller->node, "send", NULL); + script = GetProp(node, controller->node, state); + if (script == NULL) script = state; + + if (! SctCallInContext(con, script, node, data->controller, &result)) { + SCPrintf(con, eError, "ERROR: %s", result); + goto finish; + } + state = result; + if (strcasecmp(state, "idle") == 0) { + if (currentCon && ! data->answered) { + SCWrite(con, "o.k.", eValue); + } + if(strcmp(data->name,"write") == 0){ + SetHdbProperty(data->node,"writestatus","commandsent"); + } + snprintf(timeKey,50,"%s_time",data->name); + snprintf(timeVal,50,"%.3f", DoubleTime()); + SetHdbProperty(data->node,timeKey,timeVal); + goto finish; + } + SetProp(node, controller->node, "state", state); + send = GetProp(node, controller->node, "send"); + if (send != NULL && send[0] != '\0') { + if (controller->verbose) { + SCPrintf(con, eWarning, "send : %s", send); + } + if (currentCon) { + SCStorePop(currentCon); + } else { + SCStorePop(controller->conn); + } + return send; + } + } + SCPrintf(con, eError, "ERROR: too many quick scripts chained"); +finish: + SetProp(node, controller->node, "state", "idle"); + if (currentCon) { + SCStorePop(currentCon); + } else { + SCStorePop(controller->conn); + } + if (data->conCtx != NULL) { + SCStoreFree(data->conCtx); + data->conCtx = NULL; + } + return send; +} + +static char *SctWriteHandler(void *actionData, char *lastReply) { + SctData *data = actionData; + SCStore *old; + char *result; + + old = currentCon; + currentCon = data->conCtx; + result = SctActionHandler(data, lastReply); + currentCon = old; + return result; +} + +static int SctMatchNode(void *vNode, void *vData) { + Hdb *node = vNode; + SctData *d = vData; + + return node == d->node; +} + +static hdbCallbackReturn SctMainCallback(Hdb *node, void *userData, + hdbMessage *msg) { + SctController *controller = userData; + hdbDataSearch *dsm; + hdbDataMessage *mm; + hdbPtrMessage *pm; + hdbMessage *km; + ContextItem *s; + SConnection *con; + char *geterror; + + pm = GetKillPtrMessage(msg); + if (pm != NULL) { + if (controller == pm->pPtr) { + return hdbKill; + } + return hdbContinue; + } + dsm = GetHdbDataSearchMessage(msg); + if (dsm != NULL) { + if (dsm->testPtr == controller) { + dsm->result = controller; + return hdbAbort; + } + return hdbContinue; + } + km = GetHdbKillNodeMessage(msg); + if (km != NULL) { + /* unschedule all actions related to this node */ + DevUnschedule(controller->devser, node, SctActionHandler, SctMatchNode); + CleanStack(node); + return hdbContinue; + } + mm = GetHdbGetMessage(msg); + if (mm != NULL) { + con = mm->callData; + geterror = GetHdbProp(node, "geterror"); + if (geterror != NULL) { + SCPrintf(con, eError, "ERROR: %s", geterror); + return hdbAbort; + } + return hdbContinue; + } + + return hdbContinue; +} + +static hdbCallbackReturn SctActionCallback(Hdb *node, void *userData, + hdbMessage *msg) { + hdbDataSearch *dsm; + hdbDataMessage *mm; + hdbPtrMessage *pm; + hdbMessage *km; + SctData *data = userData; + Hdb *target; + char *script; + int l; + int iRet; + pDynString text; + DevPrio prio; + char *writeprio; + char *error; + SConnection *con; + SConnection *con2; + char path[MAX_HDB_PATH]; + + pm = GetKillPtrMessage(msg); + if (pm != NULL) { + if (data->controller == pm->pPtr) { + return hdbKill; + } + return hdbContinue; + } + dsm = GetHdbDataSearchMessage(msg); + if (dsm != NULL) { + if (dsm->testPtr == &actionCallback) { + if (strcasecmp(data->name, actionCallback.name) == 0) { + dsm->result = data; + } + return hdbAbort; + } + return hdbContinue; + } + + mm = GetHdbSetMessage(msg); + if (mm != NULL) { + con = mm->callData; + + /* set target value */ + text = formatValue(*(mm->v), node); + SetHdbProperty(node, "target", GetCharArray(text)); + + /* call check script, if available */ + script = GetProp(node, data->controller->node, "check"); + if (script != NULL) { + if (SctCallInContext(con, script, node, data->controller, &error) == 0) { + SCPrintf(con, eError, "ERROR: %s", error); + SetHdbProperty(node,"target", NULL); + return hdbAbort; + } + } + + + /* enqueue write action */ + writeprio = GetProp(node, data->controller->node, "writeprio"); + if (writeprio != NULL) { + prio = DevText2Prio(writeprio); + if (prio == NullPRIO) { + SCPrintf(con, eError, "ERROR: unknown priority: %s", writeprio); + prio = WritePRIO; + } + } else { + prio = WritePRIO; + } + + if (data->conCtx != NULL) { + con2 = SCStorePush(data->conCtx); + GetHdbPath(node, path, sizeof path); + SCPrintf(con2, eValue, "%s target changed to %s before completion", + path, GetCharArray(text)); + SCStorePop(data->conCtx); + } + DeleteDynString(text); + data->conCtx = SCSave(con, data->conCtx); + data->answered = 0; + DevQueue(data->controller->devser, data, prio, + SctWriteHandler, SctMatch, NULL); + /* no kill function in DevQueue: data is owned by the node (callback list) */ + return hdbContinue; + } + + mm = GetHdbUpdateMessage(msg); + if (mm != NULL) { + if (currentCon) { /* update called from a write action */ + data->answered = 1; + GetHdbPath(node, path, sizeof path); + con = SCStorePush(currentCon); + text = formatValue(*(mm->v), node); + SCPrintf(con, eStatus, "%s = %s", path, + GetCharArray(text)); + DeleteDynString(text); + SCStorePop(currentCon); + } + return hdbContinue; + } + return hdbContinue; +} + +static char *ParText(Hdb *cmdNode, char *name, + int nPar, char *defaultValue) { + Hdb *par; + + for (par = cmdNode->child; nPar > 0 && par != NULL; + par = par->next, nPar--) { + if (strcasecmp(par->name, name) == 0) { + if (par->value.dataType == HIPTEXT) { + return par->value.v.text; + } + } + } + return defaultValue; +} + +static double ParValue(Hdb *cmdNode, char *name, + int nPar, double defaultValue) { + Hdb *par; + + for (par = cmdNode->child; nPar > 0 && par != NULL; + par = par->next, nPar--) { + if (strcasecmp(par->name, name) == 0) { + switch (par->value.dataType) { + case HIPINT: return par->value.v.intValue; + case HIPFLOAT: return par->value.v.doubleValue; + } + } + } + return defaultValue; +} + +static void SctKillData(void *d) { + SctData *data = d; + + if (data->name) free(data->name); + if (data->conCtx) SCStoreFree(data->conCtx); + free(data); +} + +static void SctKillCBData(void *d) { + SctData *data = d; + + DevRemoveAction(data->controller->devser, data); + SctKillData(d); +} + +int SctAddPollNode(SctController *controller, Hdb *node, double interval, + DevPrio prio, char *action) { + SctData *data; + hdbCallback *cb; + + if (! FindHdbCallbackData(node, controller)) { + cb = MakeHipadabaCallback(SctMainCallback, controller, NULL); + assert(cb); + AppendHipadabaCallback(node, cb); + SetHdbProperty(node,"geterror","Not read yet"); + } + + data = calloc(1, sizeof(*data)); + assert(data); + data->controller = controller; + data->node = node; + data->conCtx = NULL; + data->name = strdup(action); + + return DevSchedule(controller->devser, data, prio, interval, + SctActionHandler, SctMatch, SctKillData); + +} + +static int SctPollCmd(pSICSOBJ ccmd, SConnection *con, + Hdb *cmdNode, Hdb *par[], int nPar) { + Hdb *node; + SctController *controller; + double interval; + char *path; + DevPrio prio; + char *action; + char *prioText; + + if(nPar < 1){ + SCPrintf(con,eError, + "ERROR: should be: %s poll ( )", + ccmd->objectNode->name); + return 0; + } + controller = ccmd->pPrivate; + path = ParText(cmdNode, "node", nPar, ""); + node = FindHdbNode(NULL, path, con); + if (node == NULL) { + SCPrintf(con, eError, "ERROR: %s not found", path); + return 0; + } + interval = ParValue(cmdNode, "interval", nPar, 5); + prioText = ParText(cmdNode, "prio", nPar, "read"); + prio = DevText2Prio(prioText); + if (prio == NullPRIO) { + SCPrintf(con,eError, "ERROR: unknown priority: %s", prioText); + return 0; + } + action = ParText(cmdNode, "action", nPar, "read"); + + if (SctAddPollNode(controller, node, interval, prio, action) > 0) { + SCPrintf(con, eValue, + "%s poll on %s changed to %g sec, %s prio", + action, path, interval, prioText); + } else { + SCPrintf(con, eValue, + "%s poll registered on %s (%g sec, %s prio)", + action, path, interval, prioText); + } + return 1; +} + +static int SctUnpollCmd(pSICSOBJ ccmd, SConnection *con, + Hdb *cmdNode, Hdb *par[], int nPar) { + SctController *controller; + double interval; + char *path; + DevPrio prio; + char *prioText; + SctData data; + + if(nPar < 1){ + SCPrintf(con,eError, + "ERROR: should be: %s poll ()", + ccmd->objectNode->name); + return 0; + } + controller = ccmd->pPrivate; + path = ParText(cmdNode, "node", nPar, ""); + data.node = FindHdbNode(NULL, path, con); + if (data.node == NULL) { + SCPrintf(con, eError, "ERROR: %s not found", path); + return 0; + } + data.name = ParText(cmdNode, "action", nPar, "read"); + if (DevUnschedule(controller->devser, &data, SctActionHandler, SctMatch) == 0) { + SCPrintf(con, eValue, + "%s poll not found on %s", data.name, path); + } else { + SCSendOK(con); + } + return 1; +} + +static int SctConnectCmd(pSICSOBJ ccmd, SConnection *con, + Hdb *cmdNode, Hdb *par[], int nPar) { + Hdb *node; + SctController *controller; + char *path; + hdbCallback *cb; + + if(nPar < 1){ + SCPrintf(con,eError, + "ERROR: should be: %s connect ", + ccmd->objectNode->name); + return 0; + } + controller = ccmd->pPrivate; + path = ParText(cmdNode, "node", nPar, ""); + node = FindHdbNode(NULL, path, con); + if (node == NULL) { + SCPrintf(con, eError, "ERROR: %s not found", path); + return 0; + } + + if (! FindHdbCallbackData(node, controller)) { + cb = MakeHipadabaCallback(SctMainCallback, controller, NULL); + assert(cb); + AppendHipadabaCallback(node, cb); + } + SCSendOK(con); + return 1; +} + +int SctAddWriteNode(SctController *controller, Hdb *node) { + hdbCallback *cb; + SctData *data; + + if (! FindHdbCallbackData(node, controller)) { + cb = MakeHipadabaCallback(SctMainCallback, controller, NULL); + assert(cb); + AppendHipadabaCallback(node, cb); + } + + actionCallback.name = "write"; /* local, so no strdup here */ + data = FindHdbCallbackData(node, &actionCallback); + if (data != NULL) { + return 0; + } + data = calloc(1, sizeof(*data)); + assert(data); + data->controller = controller; + data->node = node; + data->conCtx = NULL; + data->name = strdup("write"); + + cb = MakeHipadabaCallback(SctActionCallback, data, SctKillCBData); + assert(cb); + data->node = node; + AppendHipadabaCallback(node, cb); + RemoveSetUpdateCallback(node); + return 1; +} + +static int SctWriteCmd(pSICSOBJ ccmd, SConnection *con, + Hdb *cmdNode, Hdb *par[], int nPar) { + Hdb *node; + SctController *controller; + double interval; + char *path; + + if(nPar < 1){ + SCPrintf(con, eError, "ERROR: should be: %s write ", + ccmd->objectNode->name); + return 0; + } + controller = ccmd->pPrivate; + path = ParText(cmdNode, "node", nPar, ""); + node = FindHdbNode(NULL, path, con); + if (node == NULL) { + SCPrintf(con, eError, "ERROR: %s not found", path); + return 0; + } + if (SctAddWriteNode(controller, node) == 0) { + SCPrintf(con, eError, + "ERROR: %s has already a write action", path); + return 0; + } + + SCSendOK(con); + return 1; +} + +void SctQueueNode(SctController *controller, Hdb *node, + DevPrio prio, char *action, SConnection *con) { + SctData *data; + hdbCallback *cb; + + if (! FindHdbCallbackData(node, controller)) { + cb = MakeHipadabaCallback(SctMainCallback, controller, NULL); + assert(cb); + AppendHipadabaCallback(node, cb); + } + + data = calloc(1, sizeof(*data)); + assert(data); + data->controller = controller; + data->node = node; + data->name = strdup(action); + data->conCtx = NULL; + + DevQueue(data->controller->devser, data, prio, + SctWriteHandler, SctMatch, SctKillData); + if (con != NULL) { + data->conCtx = SCSave(con, NULL); + } + return; +} + +static int SctQueueCmd(pSICSOBJ ccmd, SConnection *con, + Hdb *cmdNode, Hdb *par[], int nPar) { + Hdb *node; + SctController *controller; + double interval; + char *path; + char *action; + DevPrio prio; + SctData *data; + char *prioText; + + if(nPar < 2){ + SCPrintf(con,eError, + "ERROR: should be: %s queue ", + ccmd->objectNode->name); + return 0; + } + controller = ccmd->pPrivate; + path = ParText(cmdNode, "node", nPar, ""); + node = FindHdbNode(NULL, path, con); + if (node == NULL) { + SCPrintf(con, eError, "ERROR: %s not found", path); + return 0; + } + prioText = ParText(cmdNode, "prio", nPar, "write"); + prio = DevText2Prio(prioText); + if (prio == NullPRIO) { + SCPrintf(con,eError, "ERROR: unknown priority: %s", prioText); + return 0; + } + action = ParText(cmdNode, "action", nPar, "write"); + + SctQueueNode(controller, node, prio, action, con); + return 1; +} + +typedef struct SctTransact { + char *command; + int sent; + SConnection *con; +}SctTransact, *pSctTransact; + +static void KillSctTransact(void *data){ + pSctTransact self = (pSctTransact)data; + if(self == NULL){ + return; + } + if(self->command){ + free(self->command); + } + free(self); +} + +static char *TransactionHandler(void *actionData, char *lastReply){ + pSctTransact st = (pSctTransact)actionData; + + if(st->sent == 0){ + st->sent = 1; + return st->command; + } else { + st->sent = 2; + SCWrite(st->con,lastReply, eValue); + return NULL; + } +} +static int SctTransactMatch(void *d1, void *d2){ + return d1 == d2; +} + +static int SctTransactCmd(pSICSOBJ ccmd, SConnection *con, + Hdb *cmdNode, Hdb *par[], int nPar) { + pSctTransact st = NULL; + SctController *c; + + c = (SctController *)ccmd->pPrivate; + + st = calloc(sizeof(SctTransact),1); + if(st == NULL){ + SCWrite(con,"ERROR: out of memory in SctTransactCommand", eError); + return 0; + } + st->con = con; + st->command = strdup(par[0]->value.v.text); + + DevQueue(c->devser, st, WritePRIO, + TransactionHandler, SctTransactMatch, NULL); + while(st->sent != 2){ + TaskYield(pServ->pTasker); + if(SCGetInterrupt(con) != eContinue){ + break; + } + } + KillSctTransact(st); + return 1; +} + +static int SctSendCmd(pSICSOBJ ccmd, SConnection *con, + Hdb *cmdNode, Hdb *par[], int nPar) { + pSctTransact st = NULL; + SctController *c; + + c = (SctController *)ccmd->pPrivate; + + st = calloc(sizeof(SctTransact),1); + if(st == NULL){ + SCWrite(con,"ERROR: out of memory in SctSendCmd", eError); + return 0; + } + st->con = con; + st->command = strdup(par[0]->value.v.text); + + DevQueue(c->devser, st, WritePRIO, + TransactionHandler, SctTransactMatch, KillSctTransact); + return 1; +} + + +static hdbCallbackReturn SctDebugCallback(Hdb *node, void *userData, + hdbMessage *msg) { + hdbDataMessage *mm; + SctController *controller = userData; + SConnection *con; + int i; + + mm = GetHdbSetMessage(msg); + if (mm != NULL) { + i = mm->v->v.intValue; + if (i < 0) { + controller->verbose = 0; + DevDebugMode(controller->devser, -1); + } else if (i == 0) { + controller->verbose = 1; + DevDebugMode(controller->devser, -1); + } else { + controller->verbose = 1; + DevDebugMode(controller->devser, i); + } + } + return hdbContinue; +} + +static void SctKillController(void *c) { + SctController *controller = c; + SConnection *con; + + CleanStack(controller->node); + RemoveSICSInternalCallback(controller); + if (controller->conn) { + con = SCLoad(controller->conn); + SCDeleteConnection(con); + SCStoreFree(controller->conn); + } + DevKill(controller->devser); + free(controller); +} + +static int SctMakeController(SConnection *con, SicsInterp *sics, + void *object, int argc, char *argv[]) { + SICSOBJ *ccmd; + Hdb *parent, *par, *cmd; + char *nodeName; + hdbCallback *cb; + SctController *controller; + + if (argc < 2) { + SCPrintf(con, eError, + "ERROR: should be %s ...", + argv[0]); + return 0; + } + + parent = FindHdbParent(NULL, argv[1], &nodeName, con); + if (parent == NULL) return 0; /* error message already written */ + + controller = calloc(1, sizeof(*controller)); + assert(controller); + controller->verbose = 0; + + ccmd = MakeSICSOBJv(nodeName, "SctController", HIPNONE, usSpy); + controller->node = ccmd->objectNode; + controller->conn = SCSave(SCCreateDummyConnection(pServ->pSics), NULL); + + assert(ccmd); + assert(controller->node->mama == NULL); + ccmd->pPrivate = controller; + ccmd->KillPrivate = SctKillController; + + AddHipadabaChild(parent, controller->node, con); + + controller->devser = DevMake(con, argc - 2, argv + 2); + if (!controller->devser) return 0; + + AddCommand(pServ->pSics, nodeName, InvokeSICSOBJ, KillSICSOBJ, ccmd); + SetDescriptorKey(ccmd->pDes, "creationCommand", "0"); + + cmd = AddSICSHdbPar(controller->node, + "poll", usMugger, MakeSICSFunc(SctPollCmd)); + AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText("")); + AddSICSHdbPar(cmd, "interval", usMugger, MakeHdbFloat(5.0)); + AddSICSHdbPar(cmd, "prio", usMugger, MakeHdbText("read")); + AddSICSHdbPar(cmd, "action", usMugger, MakeHdbText("read")); + + cmd = AddSICSHdbPar(controller->node, + "unpoll", usMugger, MakeSICSFunc(SctUnpollCmd)); + AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText("")); + AddSICSHdbPar(cmd, "action", usMugger, MakeHdbText("read")); + + cmd = AddSICSHdbPar(controller->node, + "connect", usMugger, MakeSICSFunc(SctConnectCmd)); + AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText("")); + + cmd = AddSICSHdbPar(controller->node, + "write", usMugger, MakeSICSFunc(SctWriteCmd)); + AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText("")); + + + cmd = AddSICSHdbPar(controller->node, + "queue", usMugger, MakeSICSFunc(SctQueueCmd)); + AddSICSHdbPar(cmd, "node", usMugger, MakeHdbText("")); + AddSICSHdbPar(cmd, "prio", usMugger, MakeHdbText("write")); + AddSICSHdbPar(cmd, "action", usMugger, MakeHdbText("write")); + + cmd = AddSICSHdbPar(controller->node, + "transact", usMugger, MakeSICSFunc(SctTransactCmd)); + AddSICSHdbPar(cmd, "data", usMugger, MakeHdbText("")); + + cmd = AddSICSHdbPar(controller->node, + "send", usMugger, MakeSICSFunc(SctSendCmd)); + AddSICSHdbPar(cmd, "data", usMugger, MakeHdbText("")); + + + par = AddSICSHdbPar(controller->node, "debug", usUser, MakeHdbInt(-1)); + + cb = MakeHipadabaCallback(SctDebugCallback, controller, NULL); + if (cb) AppendHipadabaCallback(par, cb); + + return 1; +} + +void SctKill(void *object) { + assert(sct == object); + ContextItem *p, *q; + + for (p = sct->nodes; p != NULL; p = q) { + q = p->next; + free(p); + } + for (p = sct->trash; p != NULL; p = q) { + q = p->next; + free(p); + } + if (sct->desc != NULL) { + DeleteDescriptor(sct->desc); + } + free(sct); + sct = NULL; +} + +void SctInit(void) { + if (sct) return; + sct = calloc(1, sizeof(*sct)); + assert(sct); + sct->desc = CreateDescriptor("ScriptContext"); + AddCommand(pServ->pSics, "sct", SctCommand, SctKill, sct); + AddCmd("makesctcontroller", SctMakeController); +} + +int SctVerbose(SctController *c){ + return c->verbose; +} diff --git a/scriptcontext.h b/scriptcontext.h new file mode 100644 index 00000000..30c5bc87 --- /dev/null +++ b/scriptcontext.h @@ -0,0 +1,38 @@ +#ifndef SCRIPTCONTEXT_H +#define SCRIPTCONTEXT_H + +#include "sics.h" +#include "devser.h" + +/* \brief an sct controller + */ +typedef struct SctController SctController; + +/** \brief queue node action to a controller + * + * \param controller the controller + * \param node the node + * \param prio the priority + * \param action the initial state + * \param con an optional connection to be used by the action scripts + */ +void SctQueueNode(SctController *controller, Hdb *node, + DevPrio prio, char *action, SConnection *con); + +/** \brief call a script and configure the sct command to be used + * in connection with the given node and controller + * + * \param con the connection + * \param script a tcl script + * \param the node to which the sct command + * \return 0 when this was a new action, > 0 when an action was overwritten + */ +int SctCallInContext(SConnection *con, char *script, Hdb *node, + SctController *controller, char **resPtr); +/** + * test the controller verbose flag + * \param c The SctController to test + * \return 1 for verbose, 0 for silent + */ +int SctVerbose(SctController *c); +#endif diff --git a/sctdriveadapter.c b/sctdriveadapter.c new file mode 100644 index 00000000..6b496fee --- /dev/null +++ b/sctdriveadapter.c @@ -0,0 +1,322 @@ +/** + * This is an adapter to a node under the control of the new + * scriptcontext generic device model. This is a wrapper around + * such a node which implements the drivable interface. + * + * Some cooperation from the node is required: It has to provide + * certain properties the value of which define scripts which + * have to be called at various stages. These are: + * + * checklimits, for limits checking + * checkstatus, for evaluating progress + * halt , for halting things + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, June 2008 + * --------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include "scriptcontext.h" +/*---------------------------------------------------------------*/ +typedef struct { + pObjectDescriptor pDes; + pIDrivable pDriv; + pHdb node; + SctController *c; +}SctDrive, *pSctDrive; +/*---------------------------------------------------------------*/ +static void *SCTDRIVGetInterface(void *data, int iD){ + pSctDrive self = NULL; + + self = (pSctDrive)data; + if(self != NULL && iD == DRIVEID){ + if (self->node == NULL) return NULL; + return self->pDriv; + } else { + return NULL; + } + return NULL; +} +/*---------------------------------------------------------------- + This routine can return either OKOK or HWFault when thing + go wrong. However, the return value of Halt is usually ignored! +------------------------------------------------------------------*/ +static int SCTDRIVHalt(void *data) { + pSctDrive self = NULL; + char dummy[16]; + + self = (pSctDrive)data; + if (GetHdbProperty(self->node,"halt", dummy, sizeof dummy)) { + SctQueueNode(self->c, self->node, HaltPRIO, "halt", NULL); + } else if (GetHdbProperty(self->node, "status", dummy, sizeof dummy)) { + SetHdbProperty(self->node, "status", "idle"); + } + return OKOK; +} +/*---------------------------------------------------------------- + This routine can return either 1 or 0. 1 means the position can + be reached, 0 NOT + If 0, error shall contain up to errlen characters of information + about which limit was violated +------------------------------------------------------------------*/ +static int SCTDRIVCheckLimits(void *data, float val, + char *error, int errlen){ + pSctDrive self = NULL; + char script[1024]; + int status; + Tcl_Interp *pTcl = NULL; + char *result; + + self = (pSctDrive)data; + snprintf(script,1024,"%f", val); + SetHdbProperty(self->node,"target", script); + if(GetHdbProperty(self->node,"checklimits",script,1024)){ + status = SctCallInContext(pServ->dummyCon, script, + self->node, self->c, &result); + if(SctVerbose(self->c)){ + SCPrintf(pServ->dummyCon, eWarning, "script %s called with result %s\n ", + script, result); + } + if(status == 0){ + strncpy(error,result,errlen); + return 0; + } + } + return 1; +} +/*---------------------------------------------------------------- + This routine can return 0 when a limit problem occurred + OKOK when the motor was successfully started + HWFault when a problem occured starting the device + Possible errors shall be printed to pCon + For real motors, this is supposed to try at least three times + to start the motor in question + val is the value to drive the motor too +------------------------------------------------------------------*/ +static long SCTDRIVSetValue(void *data, SConnection *pCon, float val){ + pSctDrive self = NULL; + int status; + hdbValue v; + + self = (pSctDrive)data; + v.dataType = HIPFLOAT; + v.v.doubleValue = (double)val; + SetHdbProperty(self->node,"writestatus", "start"); + status = SetHipadabaPar(self->node, v, pCon); + if(status == 1){ + return OKOK; + } else { + return HWFault; + } +} +/*---------------------------------------------------------------- + Checks the status of a running motor. Possible return values + HWBusy The motor is still running + OKOK or HWIdle when the motor finished driving + HWFault when a hardware problem ocurred + HWPosFault when the hardware cannot reach a position + Errors are duly to be printed to pCon + For real motors CheckStatus again shall try hard to fix any + issues with the motor +------------------------------------------------------------------*/ +static int SCTDRIVCheckStatus(void *data, SConnection *pCon){ + pSctDrive self = NULL; + char script[1024]; + int status; + Tcl_Interp *pTcl = NULL; + char *result; + SConnection *con; + + self = (pSctDrive)data; + + /* + * check if the write command has gone through + */ + if(GetHdbProperty(self->node,"writestatus", script,1024)){ + if(strcmp(script,"start") == 0){ + return HWBusy; + } + } + + /* + * run the checkstatus script + */ + if(!GetHdbProperty(self->node,"checkstatus",script,1024)){ + if (!GetHdbProperty(self->node,"status",script,1024)){ + SCWrite(pCon, + "ERROR: configuration problem: no checkstatus script!", eError); + return HWFault; + } + result = script; + } else { + status = SctCallInContext(pCon,script, self->node, + self->c, &result); + if (status == 0) { + SCPrintf(pCon,eError," script %s returned %s", + script, result); + return HWFault; + } + if(SctVerbose(self->c)){ + SCPrintf(pCon,eError," script %s returned %s", + script, result); + } + } + if(strstr(result,"busy") != NULL){ + return HWBusy; + } else if(strstr(result,"posfault") != NULL){ + return HWPosFault; + } else if(strstr(result,"fault") != NULL){ + return HWFault; + } else if(strstr(result,"idle") != NULL){ + return HWIdle; + } else { + SCPrintf(pCon,eError, + "ERROR: invalid status code %s returned from checkstatus script", + result); + return HWFault; + } + return HWFault; +} +/*---------------------------------------------------------------- + GetValue is supposed to read a motor position + On errors, -99999999.99 is returned and messages printed to pCon +------------------------------------------------------------------*/ +static float SCTDRIVGetValue(void *data, SConnection *pCon){ + pSctDrive self = NULL; + float val = -99999999.99; + int status; + char error[256]; + hdbValue v; + + self = (pSctDrive)data; + if(GetHdbProperty(self->node,"geterror", error, 256)){ + SCWrite(pCon,error, eError); + return val; + } + return (float)self->node->value.v.doubleValue; +} +/*---------------------------------------------------------------- + returns NULL on failure, a new datastructure else +------------------------------------------------------------------*/ +static pSctDrive SCTDRIVMakeObject(){ + pSctDrive self = NULL; + + self = calloc(sizeof(SctDrive),1); + if(self == NULL){ + return NULL; + } + self->pDes = CreateDescriptor("SctDriveAdapter"); + self->pDriv = CreateDrivableInterface(); + if(self->pDes == NULL || self->pDriv == NULL){ + free(self); + return NULL; + } + + self->pDes->GetInterface = SCTDRIVGetInterface; + self->pDriv->Halt = SCTDRIVHalt; + self->pDriv->CheckLimits = SCTDRIVCheckLimits; + self->pDriv->SetValue = SCTDRIVSetValue; + self->pDriv->CheckStatus = SCTDRIVCheckStatus; + self->pDriv->GetValue = SCTDRIVGetValue; + + return self; +} +/*-----------------------------------------------------------------*/ +static int SctDriveCommand(SConnection *pCon, SicsInterp *sics, void *object, + int argc, char *argv[]) { + pSctDrive self = (pSctDrive)object; + float val; + + assert(self != NULL); + + if (self->node == NULL) { + SCWrite(pCon, "ERROR: defunct object", eError); + return 0; + } + /* + * only action: print value + */ + val = self->pDriv->GetValue(self,pCon); + SCPrintf(pCon,eValue,"%s = %f", argv[0], val); + return 1; +} +/*----------------------------------------------------------------*/ +static void SctDriveKill(void *data){ + pSctDrive self = (pSctDrive)data; + + if(self == NULL){ + return; + } + if(self->pDriv != NULL){ + free(self->pDriv); + } + if(self->pDes != NULL){ + DeleteDescriptor(self->pDes); + } + free(self); +} +/*----------------------------------------------------------------*/ +static hdbCallbackReturn SctDummyCallback(Hdb *node, void *userData, + hdbMessage *msg) { + return hdbContinue; +} +/*----------------------------------------------------------------*/ +static void SctDriveDeleteNode(void *data) { + pSctDrive self = (pSctDrive)data; + self->node = NULL; +} +/*---------------------------------------------------------------*/ +int SctMakeDriveAdapter(SConnection *pCon, SicsInterp *pSics, void *object, + int argc, char *argv[]) { + pSctDrive pNew = NULL; + pSICSOBJ obj = NULL; + hdbCallback *cb; + + pNew = SCTDRIVMakeObject(); + if(pNew == NULL){ + SCWrite(pCon,"ERROR: out of memory in SctMakeDriveAdapter", + eError); + return 0; + } + + if(argc < 4){ + SCWrite(pCon,"ERROR: not enough arguments for SctMakeDriveAdapter", eError); + return 0; + } + + pNew->node = FindHdbNode(NULL,argv[2], pCon); + obj = FindCommandData(pSics,argv[3], "SctController"); + if(pNew->node == NULL || obj == NULL){ + SCWrite(pCon,"ERROR: node or controller not found", eError); + SctDriveKill(pNew); + return 0; + } + pNew->c =(SctController *)obj->pPrivate; + + if (strcasecmp(argv[0],"dynsctdrive") == 0) { + /* make object dynamic by defining a descriptor command */ + SetDescriptorKey(pNew->pDes, "creationCommand", "0"); + } + AddCommand(pSics, argv[1], SctDriveCommand, SctDriveKill, pNew); + SetHdbProperty(pNew->node,"sicsdev",argv[1]); + + cb = MakeHipadabaCallback(SctDummyCallback, pNew, SctDriveDeleteNode); + assert(cb); + AppendHipadabaCallback(pNew->node, cb); + + return 1; +} +/*---------------------------------------------------------------*/ +int SctMakeDriveObject(SConnection *pCon, SicsInterp *pSics, void *object, + int argc, char *argv[]); + +void SctDriveInit(void) { + AddCmd("makesctdrive", SctMakeDriveAdapter); + AddCmd("dynsctdrive", SctMakeDriveAdapter); + AddCmd("makesctdriveobj",SctMakeDriveObject); +} diff --git a/sctdriveobj.c b/sctdriveobj.c new file mode 100644 index 00000000..e747a863 --- /dev/null +++ b/sctdriveobj.c @@ -0,0 +1,337 @@ +/** + * This is a base class for any drivable object which uses the new + * scriptcontext to control the actual driving operation. This can also + * serve as a drivable adapter to nodes in other objects. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, June-July 2008 + */ +#include +#include +#include +#include +#include + +typedef struct { + pIDrivable pDriv; + SctController *c; + int doNotKillNode; +} DrivObjPriv, *pDrivObjPriv; +/*---------------------------------------------------------------*/ +static void *SCTDRIVGetInterface(void *data, int iD){ + pSICSOBJ self = NULL; + pDrivObjPriv pPriv; + + self = (pSICSOBJ)data; + pPriv = (pDrivObjPriv)self->pPrivate; + if(self != NULL && pPriv != NULL && iD == DRIVEID){ + if (self->objectNode == NULL) return NULL; + return pPriv->pDriv; + } else { + return NULL; + } + return NULL; +} +/*---------------------------------------------------------------- + This routine can return either OKOK or HWFault when thing + go wrong. However, the return value of Halt is usually ignored! +------------------------------------------------------------------*/ +static int SCTDRIVHalt(void *data) { + pSICSOBJ self = NULL; + pDrivObjPriv pPriv; + char dummy[16]; + + self = (pSICSOBJ)data; + pPriv = (pDrivObjPriv)self->pPrivate; + if (GetHdbProperty(self->objectNode,"halt", dummy, sizeof dummy)) { + SctQueueNode(pPriv->c, self->objectNode, HaltPRIO, "halt", NULL); + } else if (GetHdbProperty(self->objectNode, "status", dummy, sizeof dummy)) { + SetHdbProperty(self->objectNode, "status", "idle"); + } + return OKOK; +} +/*---------------------------------------------------------------- + This routine can return either 1 or 0. 1 means the position can + be reached, 0 NOT + If 0, error shall contain up to errlen characters of information + about which limit was violated +------------------------------------------------------------------*/ +static int SCTDRIVCheckLimits(void *data, float val, + char *error, int errlen){ + pSICSOBJ self = NULL; + pDrivObjPriv pPriv; + char script[1024]; + int status; + Tcl_Interp *pTcl = NULL; + char *result; + + self = (pSICSOBJ)data; + pPriv = (pDrivObjPriv)self->pPrivate; + snprintf(script,1024,"%f", val); + SetHdbProperty(self->objectNode,"target", script); + if(GetHdbProperty(self->objectNode,"checklimits",script,1024)){ + status = SctCallInContext(pServ->dummyCon, script, + self->objectNode, pPriv->c, &result); + if(SctVerbose(pPriv->c)){ + SCPrintf(pServ->dummyCon, eWarning, "script %s called with result %s\n ", + script, result); + } + if(status == 0){ + strncpy(error,result,errlen); + return 0; + } + } + return 1; +} +/*---------------------------------------------------------------- + This routine can return 0 when a limit problem occurred + OKOK when the motor was successfully started + HWFault when a problem occured starting the device + Possible errors shall be printed to pCon + For real motors, this is supposed to try at least three times + to start the motor in question + val is the value to drive the motor too +------------------------------------------------------------------*/ +static long SCTDRIVSetValue(void *data, SConnection *pCon, float val){ + pSICSOBJ self = NULL; + pDrivObjPriv pPriv; + int status; + hdbValue v; + + self = (pSICSOBJ)data; + pPriv = (pDrivObjPriv)self->pPrivate; + + v.dataType = HIPFLOAT; + v.v.doubleValue = (double)val; + SetHdbProperty(self->objectNode,"writestatus", "start"); + status = SetHipadabaPar(self->objectNode, v, pCon); + if(status == 1){ + return OKOK; + } else { + return HWFault; + } +} +/*---------------------------------------------------------------- + Checks the status of a running motor. Possible return values + HWBusy The motor is still running + OKOK or HWIdle when the motor finished driving + HWFault when a hardware problem ocurred + HWPosFault when the hardware cannot reach a position + Errors are duly to be printed to pCon + For real motors CheckStatus again shall try hard to fix any + issues with the motor +------------------------------------------------------------------*/ +static int SCTDRIVCheckStatus(void *data, SConnection *pCon){ + pSICSOBJ self = NULL; + pDrivObjPriv pPriv; + char script[1024]; + int status; + Tcl_Interp *pTcl = NULL; + char *result; + SConnection *con; + + self = (pSICSOBJ)data; + pPriv = (pDrivObjPriv)self->pPrivate; + + /* + * check if the write command has gone through + */ + if(GetHdbProperty(self->objectNode,"writestatus", script,1024)){ + if(strcmp(script,"start") == 0){ + return HWBusy; + } + } + + /* + * run the checkstatus script + */ + if(!GetHdbProperty(self->objectNode,"checkstatus",script,1024)){ + if (!GetHdbProperty(self->objectNode,"status",script,1024)){ + SCWrite(pCon, + "ERROR: configuration problem: no checkstatus script!", eError); + return HWFault; + } + result = script; + } else { + status = SctCallInContext(pCon,script, self->objectNode, + pPriv->c, &result); + if (status == 0) { + SCPrintf(pCon,eError," script %s returned %s", + script, result); + return HWFault; + } + if(SctVerbose(pPriv->c)){ + SCPrintf(pCon,eError," script %s returned %s", + script, result); + } + } + if(strstr(result,"busy") != NULL){ + return HWBusy; + } else if(strstr(result,"posfault") != NULL){ + return HWPosFault; + } else if(strstr(result,"fault") != NULL){ + return HWFault; + } else if(strstr(result,"idle") != NULL){ + return HWIdle; + } else { + SCPrintf(pCon,eError, + "ERROR: invalid status code %s returned from checkstatus script", + result); + return HWFault; + } + return HWFault; +} +/*---------------------------------------------------------------- + GetValue is supposed to read a motor position + On errors, -99999999.99 is returned and messages printed to pCon +------------------------------------------------------------------*/ +static float SCTDRIVGetValue(void *data, SConnection *pCon){ + pSICSOBJ self = NULL; + pDrivObjPriv pPriv; + float val = -99999999.99; + int status; + char error[256]; + hdbValue v; + + self = (pSICSOBJ)data; + pPriv = (pDrivObjPriv)self->pPrivate; + if(GetHdbProperty(self->objectNode,"geterror", error, 256)){ + SCWrite(pCon,error, eError); + return val; + } + return (float)self->objectNode->value.v.doubleValue; +} +/*-----------------------------------------------------------------*/ +void AssignSctDrive(pIDrivable pDriv){ + pDriv->Halt = SCTDRIVHalt; + pDriv->CheckLimits = SCTDRIVCheckLimits; + pDriv->SetValue = SCTDRIVSetValue; + pDriv->CheckStatus = SCTDRIVCheckStatus; + pDriv->GetValue = SCTDRIVGetValue; +} +/*---------------------------------------------------------------------------*/ +static void KillDriveOBJ(void *data){ + pSICSOBJ self = (pSICSOBJ)data; + pDrivObjPriv pPriv; + + if(self == NULL){ + return; + } + + pPriv = (pDrivObjPriv)self->pPrivate; + if(pPriv->doNotKillNode && self->pDes != NULL){ + if(self->pDes->name) free(self->pDes->name); + if(self->pDes->pKeys) IFDeleteOptions(self->pDes->pKeys); + free(self->pDes); + } else { + DeleteDescriptor(self->pDes); /* kill descriptor including node */ + } + if(self->KillPrivate != NULL && self->pPrivate != NULL){ + self->KillPrivate(self->pPrivate); + } + RemoveHdbNodeFromParent(self->objectNode, pServ->dummyCon); + free(self); +} +/*-----------------------------------------------------------------------------*/ +pSICSOBJ MakeSctDriveObj(pHdb node, char *class, SctController *c){ + pSICSOBJ pNew = NULL; + pDrivObjPriv pPriv = NULL; + hdbValue val; + + pNew = (pSICSOBJ)malloc(sizeof(SICSOBJ)); + pPriv = (pDrivObjPriv)malloc(sizeof(DrivObjPriv)); + if(pNew == NULL || pPriv == NULL){ + return NULL; + } + memset(pNew,0,sizeof(SICSOBJ)); + memset(pPriv,0,sizeof(DrivObjPriv)); + + pNew->pDes = CreateDescriptor(class); + pPriv->pDriv = CreateDrivableInterface(); + if(pNew->pDes == NULL || pPriv->pDriv == NULL){ + free(pNew); + free(pPriv); + return(NULL); + } + pNew->objectNode = node; + AssignSctDrive(pPriv->pDriv); + pPriv->c = c; + pNew->pDes->parNode = pNew->objectNode; + pNew->pDes->GetInterface = SCTDRIVGetInterface; + pNew->pPrivate = pPriv; + pNew->KillPrivate = DefaultKill; + return pNew; +} +/*-------------------------------------------------------------------------- + * This actually has two syntaxes: + * makesctdrive name path-to-existing-node class SctController + * makesctdrive name type priv class SctController + *--------------------------------------------------------------------------*/ +int SctMakeDriveObject(SConnection *pCon, SicsInterp *pSics, void *object, + int argc, char *argv[]) { + pHdb node = NULL; + pDrivObjPriv pPriv = NULL; + pSICSOBJ pNew = NULL; + pSICSOBJ pSct = NULL; + SctController *c = NULL; + int priv, type, status; + hdbValue val; + + if(argc < 5){ + SCWrite(pCon,"ERROR: not enough arguments to SctMakeDriveObject", eError); + return 0; + } + + node = FindHdbNode(NULL,argv[2], pCon); + if(node != NULL){ + pSct = FindCommandData(pSics,argv[4], "SctController"); + if(pSct == NULL){ + SCPrintf(pCon,eError, "ERROR: SctController %s not found", argv[4]); + return 0; + } + c = (SctController *)pSct->pPrivate; + pNew = MakeSctDriveObj(node,argv[3],c); + pPriv = (pDrivObjPriv)pNew->pPrivate; + pPriv->doNotKillNode = 1; /* the node is owned by someone else*/ + } else { + /* convert privilege */ + priv = decodeSICSPriv(argv[3]); + /* convert datatype */ + strtolower(argv[4]); + type = convertHdbType(argv[2]); + if (type == HIPNONE) { + node = MakeHipadabaNode(argv[1], HIPNONE, 1); + } else { + val = makeHdbValue(type,0); + node = MakeSICSHdbPar(argv[1], priv, val); + ReleaseHdbValue(&val); + } + if(node == NULL){ + SCWrite(pCon,"ERROR: node creation failed", eError); + return 0; + } + c = FindCommandData(pSics,argv[5], "SctController"); + if(c == NULL){ + SCPrintf(pCon,eError, "ERROR: SctController %s not found", argv[4]); + return 0; + } + pNew = MakeSctDriveObj(node,argv[3],c); + } + if(pNew == NULL){ + SCWrite(pCon,"ERROR: failed to create drive object", eError); + return 0; + } + status = AddCommand(pSics, + argv[1], + InterInvokeSICSOBJ, + KillDriveOBJ, + pNew); + if(status != 1){ + KillSICSOBJ(pNew); + SCPrintf(pCon,eError,"ERROR: failed create duplicate command %s", argv[1]); + return 0; + } + return 1; +} + diff --git a/selvar.c b/selvar.c index 492dec11..a8533910 100644 --- a/selvar.c +++ b/selvar.c @@ -163,6 +163,7 @@ static int CheckVal(void *pSelf, SConnection *pCon ) { pSelVar self = NULL; + int status; self = (pSelVar)pSelf; assert(self); @@ -173,7 +174,12 @@ self->pCon = pCon; InvokeCallBack(self->pCall, WLCHANGE, self); - return MonoCheck(self->pSel,pCon); + status = MonoCheck(self->pSel,pCon); + if(status != HWBusy) + { + InvokeCallBack(self->pCall, WLCHANGE, self); + } + return status; } /*------------------------------------------------------------------------*/ static int HaltSelVar(void *pSelf) diff --git a/servlog.c b/servlog.c index 82936710..a6ebb607 100644 --- a/servlog.c +++ b/servlog.c @@ -236,6 +236,7 @@ int OpenVerifyLogFile() { char pFile[256]; + char filnam[512]; char *pChar = NULL; pChar = IFindOption(pSICSOptions,"LogFileBaseName"); @@ -245,10 +246,10 @@ } else { - strcpy(pFile,pChar); + strncpy(pFile,pChar,255); } - sprintf(pFile,"%s%2.2d.log",pFile, iFile); - fLogFile = fopen(pFile,"w"); + snprintf(filnam,511,"%s%2.2d.log",pFile, iFile); + fLogFile = fopen(filnam,"w"); if(!fLogFile) { fprintf(stderr,"ERROR: Cannot open logfile %s for writing\n",pFile); @@ -261,6 +262,10 @@ } } +/*---------------------------------------------------------------------------*/ + void SICSLogEnable(int flag) { + iLogUsable=flag; + } /*---------------------------------------------------------------------------*/ void SICSLogWrite(char *pText, OutCode eOut) { @@ -280,6 +285,7 @@ if( (pCurrent->iOut == eOut) || (pCurrent->iAllFlag == 1) ) { NETWrite(pCurrent->pCon->pSock,pText,strlen(pText)); + NETWrite(pCurrent->pCon->pSock,"\n",1); } pCurrent = pCurrent->pNext; } @@ -316,16 +322,5 @@ fprintf(fLogFile,"%s\n",pText); fflush(fLogFile); iLineCount++; - - /* do all captured as well */ - pCurrent = pCapture; - while(pCurrent) - { - if( (pCurrent->iOut == eOut) || (pCurrent->iAllFlag == 1) ) - { - NETWrite(pCurrent->pCon->pSock,pText,strlen(pText)); - } - pCurrent = pCurrent->pNext; - } } } diff --git a/servlog.h b/servlog.h index dfb3cbb7..6d88bbbf 100644 --- a/servlog.h +++ b/servlog.h @@ -16,6 +16,7 @@ #define SICSLOG #include "Scommon.h" void SICSLogWrite(char *ptext, OutCode eOut ); + void SICSLogEnable(int flag); int KillCapture(SConnection *pCon); int LogCapture(SConnection *pCon, SicsInterp *pInter, void *pData, diff --git a/sics.h b/sics.h index b7044e5b..c585831d 100644 --- a/sics.h +++ b/sics.h @@ -16,8 +16,9 @@ }CounterMode; /* the following line suppresses const declarations in tcl.h. + WARNING: including sics.h must always be done before tcl.h -> makes the compiler happy M.Z. */ -#define NO_CONST +#define NO_CONST #include #include @@ -36,6 +37,12 @@ extern pServer pServ; +/** + * Decode privilege text. Implemented in access.c + * @param the text to decode + * @return -1 if code invalid, else the privilege code + */ +int decodeSICSPriv(char *privText); #endif diff --git a/sicscron.c b/sicscron.c index ce49d5d2..38585f38 100644 --- a/sicscron.c +++ b/sicscron.c @@ -2,7 +2,7 @@ S I C S C R O N A cron like command facility for SICS which allows to repeat a command - at given time intervalls. + at given time intervals. copyright: see copyright.h @@ -11,7 +11,7 @@ #include #include #include -#include +#include "macro.h" #include "fortify.h" #include "sics.h" #include "splitter.h" @@ -19,13 +19,19 @@ typedef struct { - int iIntervall; + int iInterval; time_t tNext; char *pCommand; SConnection *pCon; int iEnd; + Statistics *stat; } Cron, *pCron; + typedef struct { + SConnection *pCon; + int dolater; + } CronListData; + /*------------------------------------------------------------------------*/ static void KillCron(void *pData) { @@ -43,66 +49,121 @@ { SCDeleteConnection(self->pCon); } + if (self->stat) { + StatisticsKill(self->stat); + } free(self); } /*-----------------------------------------------------------------------*/ static int CronTask(void *pData) { + Statistics *old; + pCron self = (pCron)pData; - + int iRet; + Tcl_Interp *pTcl = pServ->pSics->pTcl; + time_t now; + if(!self) { return 0; } - if(time(NULL) > self->tNext) + now = time(NULL); + if(now >= self->tNext) { - SCInvoke(self->pCon,pServ->pSics,self->pCommand); - self->tNext = time(NULL) + self->iIntervall; + MacroPush(self->pCon); + old=StatisticsBegin(self->stat); + iRet = Tcl_Eval(pTcl,self->pCommand); + StatisticsEnd(old); + MacroPop(); + if (iRet != TCL_OK) { + SCPrintf(self->pCon, eStatus, + "ERROR in sicscron script: %s", pTcl->result); + self->iEnd = 0; + return 0; + } + if (self->iEnd == 2) { /* dolater command */ + self->iEnd = 0; + return 0; + } + self->tNext += self->iInterval; + if (now > self->tNext) self->tNext = now + 1; } - return self->iEnd; + return self->iEnd > 0; } /*-----------------------------------------------------------------------*/ static void CronSignal(void *pData, int iID, void *pSigData) { pCron self = (pCron)pData; int *iInt; - + struct tm tm; + char datim[24]; + CronListData *data; + if(iID == SICSINT) { iInt = (int *)pSigData; if(*iInt >= eEndServer) - { + { self->iEnd = 0; } } + if(iID == CRONLIST) { + data = pSigData; + if (self->iEnd == 2 && data->dolater) { + tm = *localtime(&self->tNext); + strftime(datim, sizeof datim, "%Y-%m-%d %T", &tm); + SCPrintf(data->pCon, eStatus, "%s %s", datim, + self->pCommand); + } else if (self->iEnd == 1 && !data->dolater) { + tm = *localtime(&self->tNext); + strftime(datim, sizeof datim, "%Y-%m-%d %T", &tm); + SCPrintf(data->pCon, eStatus, "%s %8d %s", datim, + self->iInterval, self->pCommand); + } + } } /*-----------------------------------------------------------------------*/ int MakeCron(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) { pCron pNew = NULL; - int iVal, iRet; + int iVal, iRet, rights; char *cmd; + CronListData data; - /* only managers may do this */ - if(!SCMatchRights(pCon,usMugger)) + /* need user priv. */ + if(!SCMatchRights(pCon,usUser)) { return 0; } + if (argc == 2 && strcasecmp(argv[1], "list") == 0) { + if (strcasecmp(argv[0], "dolater") == 0) { + data.dolater = 1; + SCPrintf(pCon, eError, "Date Time Command"); + } else { + data.dolater = 0; + SCPrintf(pCon, eError, "Date Time Interval Command"); + } + data.pCon = pCon; + TaskSignal(pServ->pTasker, CRONLIST, &data); + return 1; + } + /* enough arguments? */ if(argc < 3) { - SCWrite(pCon,"ERROR: not enough arguments to sicscron",eError); + SCPrintf(pCon,eError,"ERROR: not enough arguments to %s", argv[0]); return 0; } - /* interpret first argument as intervall in seconds */ + /* interpret first argument as interval in seconds */ iRet = Tcl_GetInt(pSics->pTcl,argv[1],&iVal); if(iRet != TCL_OK) { - SCWrite(pCon,"ERROR: failed to convert intervall argument to int", + SCWrite(pCon,"ERROR: failed to convert interval argument to int", eError); return 0; } @@ -123,10 +184,21 @@ SCWrite(pCon,"ERROR: out of memory in sicscron",eError); return 0; } - pNew->iIntervall = iVal; + rights = SCGetRights(pCon); + if (rights > usMugger) { + /* transfer the rights to the dummy connection */ + SCSetRights(pNew->pCon, rights); + } pNew->pCommand = cmd; - pNew->tNext = 0; - pNew->iEnd = 1; + pNew->tNext = time(NULL) + iVal; + if (strcasecmp(argv[0], "dolater") == 0) { + pNew->iInterval = -1; + pNew->iEnd = 2; + } else { + pNew->iInterval = iVal; + pNew->iEnd = 1; + } + pNew->stat = StatisticsNew(cmd); TaskRegister(pServ->pTasker, CronTask, diff --git a/sicsdata.c b/sicsdata.c index 417415e5..6c1d12a9 100644 --- a/sicsdata.c +++ b/sicsdata.c @@ -4,6 +4,8 @@ An attempt to a generic interface to SICS data for all sorts of SICS clients. + WARNING: this code only works when ints and floats are of the same size! + copyright: see file COPYRIGHT Mark Koennecke, June 2003 @@ -11,13 +13,14 @@ #include #include #include +#include #include "fortify.h" #include "sics.h" #include "splitter.h" #include "scan.h" #include "HistMem.h" #include "sicsdata.h" - +#define ABS(x) (x < 0 ? -(x) : (x)) /*--------------------------------------------------------------------*/ static void KillSICSData(void *pData){ pSICSData self = NULL; @@ -60,6 +63,47 @@ pSICSData createSICSData(void){ pNew->dataUsed = 0; return pNew; } +/*---------------------------------------------------------------------------*/ +int getSICSDataInt(pSICSData self, int pos, int *value){ + + if(pos >= self->dataUsed || self->dataType[pos] != INTTYPE){ + return 0; + } + *value = self->data[pos]; + return 1; +} +/*---------------------------------------------------------------------------*/ +int getSICSDataFloat(pSICSData self, int pos, float *value){ + if(pos >= self->dataUsed || self->dataType[pos] != FLOATTYPE){ + return 0; + } + memcpy(value,&self->data[pos],sizeof(float)); + return 1; +} +/*---------------------------------------------------------------------------*/ +int setSICSDataInt(pSICSData self, int pos, int value){ + int *idata = NULL; + + idata = getSICSDataPointer(self,0,pos+1); + if(idata == NULL){ + return 0; + } + idata[pos] = value; + self->dataType[pos] = INTTYPE; + return 1; +} +/*----------------------------------------------------------------------------*/ +int setSICSDataFloat(pSICSData self, int pos, float value){ + int *idata = NULL; + + idata = getSICSDataPointer(self,0,pos+1); + if(idata == NULL){ + return 0; + } + memcpy(&idata[pos],&value,sizeof(float)); + self->dataType[pos] = FLOATTYPE; + return 1; +} /*-------------------------------------------------------------------*/ int *getSICSDataPointer(pSICSData self, int start, int end){ int newSize; @@ -134,15 +178,23 @@ static void netEncode(pSICSData self){ } } /*---------------------------------------------------------------------*/ -static void clearSICSData(pSICSData self){ +void clearSICSData(pSICSData self){ assert(self); + int clearSize = 8192; self->dataUsed = 0; + if(self->currentDataSize > clearSize){ + free(self->data); + free(self->dataType); + self->data = (int *)malloc(clearSize*sizeof(int)); + self->dataType = (char *)malloc(clearSize*sizeof(char)); + self->currentDataSize = clearSize; + } memset(self->data,0,self->currentDataSize*sizeof(int)); memset(self->dataType,0,self->currentDataSize*sizeof(char)); } /*--------------------------------------------------------------------*/ -static int dumpSICSData(pSICSData self, char *filename, SConnection *pCon){ +static int dumpSICSDataXY(pSICSData self, char *filename, SConnection *pCon){ FILE *fd = NULL; char pBueffel[132]; int i; @@ -167,6 +219,32 @@ static int dumpSICSData(pSICSData self, char *filename, SConnection *pCon){ SCSendOK(pCon); return 1; } +/*--------------------------------------------------------------------*/ +static int dumpSICSData(pSICSData self, char *filename, SConnection *pCon){ + FILE *fd = NULL; + char pBueffel[132]; + int i; + float fVal; + + fd = fopen(filename,"w"); + if(fd == NULL){ + snprintf(pBueffel,131,"ERROR: cannot open %s", filename); + SCWrite(pCon,pBueffel,eError); + return 0; + } + for(i = 0; i < self->dataUsed; i++){ + if(self->dataType[i] == INTTYPE){ + fprintf(fd," %d", self->data[i]); + } + if(self->dataType[i] == FLOATTYPE){ + memcpy(&fVal,self->data + i,sizeof(float)); + fprintf(fd," %.5f",fVal); + } + } + fclose(fd); + SCSendOK(pCon); + return 1; +} /*-------------------------------------------------------------------*/ static int putInt(pSICSData self, int argc, char *argv[], SConnection *pCon, SicsInterp *pSics){ @@ -206,6 +284,7 @@ static int putFloat(pSICSData self, int argc, char *argv[], int status, pos, *iData = NULL; float fVal; double dVal; + char buffer[256]; assert(self); @@ -221,8 +300,9 @@ static int putFloat(pSICSData self, int argc, char *argv[], } status = Tcl_GetDouble(InterpGetTcl(pSics),argv[1],&dVal); if(status != TCL_OK){ - SCWrite(pCon,"ERROR: failed to convert putint value to float", - eError); + snprintf(buffer,255,"ERROR: faiuld to convert putfloat value %s to float", + argv[1]); + SCWrite(pCon,buffer,eError); return 0; } iData = getSICSDataPointer(self,pos,pos+1); @@ -236,6 +316,91 @@ static int putFloat(pSICSData self, int argc, char *argv[], SCSendOK(pCon); return 1; } +/*------------------------------------------------------------------*/ +static int getPos(pSICSData self, char *name, + SConnection *pCon, int pos){ + char pBueffel[512]; + float value; + + if(pos >= self->dataUsed){ + SCWrite(pCon,"ERROR: requested position out of range",eError); + return 0; + } + if(self->dataType[pos] == FLOATTYPE){ + memcpy(&value,&self->data[pos],sizeof(float)); + snprintf(pBueffel,511,"%s = %f", name, value); + SCWrite(pCon,pBueffel,eValue); + return 1; + } + if(self->dataType[pos] == INTTYPE){ + snprintf(pBueffel,511,"%s = %d", name, self->data[pos]); + SCWrite(pCon,pBueffel,eValue); + return 1; + } + return 0; +} +/*------------------------------------------------------------------*/ +static float getDataPos(pSICSData self, int pos){ + float value; + + assert(pos < self->dataUsed); + if(self->dataType[pos] == FLOATTYPE){ + memcpy(&value,&self->data[pos],sizeof(float)); + } else { + value = (float)self->data[pos]; + } + return value; +} +/*------------------------------------------------------------------*/ +static int divideSicsData(pSICSData self, SicsInterp *pSics, + SConnection *pCon, char *name){ + int i; + pSICSData other = NULL; + float val, div; + + other = (pSICSData)FindCommandData(pSics,name,"SICSData"); + if(other == NULL){ + SCWrite(pCon,"ERROR: requested SICSData object to divide not found", + eError); + return 0; + } + if(other->dataUsed < self->dataUsed){ + SCWrite(pCon,"ERROR: not enough data in SICSData for division", + eError); + return 0; + } + for(i = 0; i < self->dataUsed; i++){ + div = getDataPos(other,i); + if(ABS(div) > .00001){ + val = getDataPos(self,i)/div; + } else { + val = .0; + } + if(self->dataType[i] == INTTYPE){ + self->data[i] = (int)val; + } else { + memcpy(&self->data[i],&val,sizeof(float)); + } + } + return 1; +} +/*------------------------------------------------------------------*/ +static int scaleSicsData(pSICSData self, SicsInterp *pSics, + SConnection *pCon, float scale){ + int i; + float div; + + for(i = 0; i < self->dataUsed; i++){ + div = getDataPos(self,i); + div *= scale; + if(self->dataType[i] == INTTYPE){ + self->data[i] = (int)fabsf(div); + } else { + memcpy(&self->data[i],&div,sizeof(float)); + } + } + return 1; +} /*-------------------------------------------------------------------*/ static int copyScanCounts(pSICSData self, int argc, char *argv[], SConnection *pCon, SicsInterp *pSics){ @@ -412,8 +577,7 @@ static int copyTimeBin(pSICSData self, int argc, char *argv[], return 0; } for(i = 0; i < noTimeBin; i++){ - memcpy(iData + pos + i, fTimeBin + i, sizeof(float)); - self->dataType[pos+i] = FLOATTYPE; + setSICSDataInt(self,pos+i,(int)(fTimeBin[i])); } SCSendOK(pCon); return 1; @@ -475,6 +639,87 @@ static int copyHM(pSICSData self, int argc, char *argv[], SCSendOK(pCon); return 1; } +/*--------------------------------------------------------------------*/ +static int copyHMBank(pSICSData self, int argc, char *argv[], + SConnection *pCon, SicsInterp *pSics){ + int status, pos, i, bank, dataLength; + pHistMem pHist = NULL; + const float *fTimeBin = NULL; + int *iData = NULL; + + if(argc < 4){ + SCWrite(pCon,"ERROR: not enough arguments to SICSData copyhm", + eError); + return 0; + } + status = Tcl_GetInt(InterpGetTcl(pSics),argv[0],&pos); + if(status != TCL_OK){ + SCWrite(pCon, + "ERROR: failed to convert copyhmbank position to integer", + eError); + return 0; + } + pHist = (pHistMem)FindCommandData(pSics,argv[1],"HistMem"); + if(!pHist){ + SCWrite(pCon,"ERROR: histogram memory not found in copyhmbank", + eError); + return 0; + } + status = Tcl_GetInt(InterpGetTcl(pSics),argv[2],&bank); + if(status != TCL_OK){ + SCWrite(pCon, + "ERROR: failed to convert copyhmbank bank to integer", + eError); + return 0; + } + status = Tcl_GetInt(InterpGetTcl(pSics),argv[3],&dataLength); + if(status != TCL_OK){ + SCWrite(pCon, + "ERROR: failed to convert copyhmbank dataLength to integer", + eError); + return 0; + } + + iData = getSICSDataPointer(self,pos,pos+dataLength); + if(!iData){ + SCWrite(pCon,"ERROR: out of memory in SICSData copyhmbank",eError); + return 0; + } + GetHistogramDirect(pHist,pCon,bank,0,dataLength,iData, + dataLength*sizeof(int)); + assignType(self,pos,pos+dataLength,INTTYPE); + SCSendOK(pCon); + return 1; +} +/*----------------------------------------------------------------------*/ +static int copyData(pSICSData self,SicsInterp *pSics, + SConnection *pCon,int argc, char *argv[]){ + pSICSData other = NULL; + int pos, start, end, i; + + if(argc < 6){ + SCWrite(pCon,"ERROR: Insufficient number of arguments to copydata", + eError); + return 0; + } + pos = atoi(argv[2]); + start = atoi(argv[4]); + end = atoi(argv[5]); + if((other = FindCommandData(pSics,argv[3],"SICSData")) == NULL){ + SCWrite(pCon,"ERROR: invalid SICSData requested",eError); + return 0; + } + if(start > end || end > other->dataUsed){ + SCWrite(pCon,"ERROR: invalid copy range specified",eError); + return 0; + } + getSICSDataPointer(self,pos, pos + (end -start)); + memcpy(&self->data[pos],&other->data[start],(end-start)*sizeof(int)); + memcpy(&self->dataType[pos],&other->dataType[start], + (end-start)*sizeof(char)); + + return 1; +} /*---------------------------------------------------------------------- Look here in order to find out about commands understood ----------------------------------------------------------------------*/ @@ -482,6 +727,8 @@ int SICSDataAction(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]){ pSICSData self = NULL; char pBueffel[132]; + int pos; + float scale; self = (pSICSData)pData; assert(self); @@ -502,6 +749,13 @@ int SICSDataAction(SConnection *pCon, SicsInterp *pSics, void *pData, snprintf(pBueffel,131,"%s = %d", argv[0], self->dataUsed); SCWrite(pCon,pBueffel,eValue); return 1; + } else if(strcmp(argv[1],"dumpxy") == 0){ + /* --------- dump */ + if(argc < 3){ + SCWrite(pCon,"ERROR: need a file name to dump to",eError); + return 0; + } + return dumpSICSDataXY(self,argv[2],pCon); } else if(strcmp(argv[1],"dump") == 0){ /* --------- dump */ if(argc < 3){ @@ -509,6 +763,27 @@ int SICSDataAction(SConnection *pCon, SicsInterp *pSics, void *pData, return 0; } return dumpSICSData(self,argv[2],pCon); + } else if(strcmp(argv[1],"get") == 0){ + if(argc < 3){ + SCWrite(pCon,"ERROR: need a position to read",eError); + return 0; + } + pos = atoi(argv[2]); + return getPos(self,argv[0],pCon,pos); + } else if(strcmp(argv[1],"divideby") == 0){ + if(argc < 3){ + SCWrite(pCon,"ERROR: need a SICSdata to divide by",eError); + return 0; + } + return divideSicsData(self,pSics,pCon,argv[2]); + } else if(strcmp(argv[1],"scale") == 0){ + if(argc < 3){ + SCWrite(pCon,"ERROR: need a scale factor to apply",eError); + return 0; + } + return scaleSicsData(self,pSics,pCon,atof(argv[2])); + } else if(strcmp(argv[1],"copydata") == 0){ + return copyData(self,pSics,pCon,argc, argv); } else if(strcmp(argv[1],"putint") == 0){ /*---------- putint */ return putInt(self,argc-2,&argv[2],pCon, pSics); @@ -528,8 +803,11 @@ int SICSDataAction(SConnection *pCon, SicsInterp *pSics, void *pData, /*--------- copytimebin */ return copyTimeBin(self,argc-2,&argv[2],pCon,pSics); } else if(strcmp(argv[1],"copyhm") == 0){ - /*--------- copytimebin */ + /*--------- copyhm */ return copyHM(self,argc-2,&argv[2],pCon,pSics); + } else if(strcmp(argv[1],"copyhmbank") == 0){ + /*--------- copyhmbank */ + return copyHMBank(self,argc-2,&argv[2],pCon,pSics); } else if(strcmp(argv[1],"writezipped") == 0){ /*--------- writezipped */ if(argc < 3){ diff --git a/sicsdata.h b/sicsdata.h index 6c6eaa03..9d3bf8d3 100644 --- a/sicsdata.h +++ b/sicsdata.h @@ -39,5 +39,12 @@ int SICSDataAction(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); + + void clearSICSData(pSICSData self); + int getSICSDataInt(pSICSData self, int pos, int *value); + int getSICSDataFloat(pSICSData self, int pos, float *value); + int setSICSDataInt(pSICSData self, int pos, int value); + int setSICSDataFloat(pSICSData self, int pos, float value); + #endif diff --git a/sicsdata.w b/sicsdata.w index 7832e9a3..9d5e19f0 100644 --- a/sicsdata.w +++ b/sicsdata.w @@ -6,6 +6,8 @@ sources such as histogram memories or scans. Data assembled in this way, for instance through scripts, can then be forwarded to clients either in UUencoded form or as a zipped array. +WARNING: this code only works right when integers and floats are of the same size! + In a later stage this may be extended to support selected mathematical operations as well. In another stage this may supersede the uuget and zipget methods in the scan, histogram memory and specialized status @@ -48,6 +50,13 @@ This object exports the following functions: int SICSDataAction(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); + + void clearSICSData(pSICSData self); + int getSICSDataInt(pSICSData self, int pos, int *value); + int getSICSDataFloat(pSICSData self, int pos, float *value); + int setSICSDataInt(pSICSData self, int pos, int value); + int setSICSDataFloat(pSICSData self, int pos, float value); + @} \begin{description} \item[getSICSDataPointer] returns a pointer to the first element of diff --git a/sicshdbadapter.c b/sicshdbadapter.c new file mode 100644 index 00000000..ba287ea7 --- /dev/null +++ b/sicshdbadapter.c @@ -0,0 +1,819 @@ +/* + * Experience has shown that integrating existing SICS objects into the + * Hierarchical Parameter Database (Hdb) is a difficult task without reworking + * the complete SICS object model. Rather, it seems easier to adapt some + * critical objects to the Hdb with some glue code. Following the facade or + * adapter design pattern. This is the purpose of this module. For the moment + * the external interface is only an interpreter function which will be used to + * install suitable SICS objects into the Hdb tree and generates the necessary + * adapters internally. This code can be used to adapt to: + * - motors + * - the data segment of histogram memories + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, November 2006 + */ +#include +#include +#include +#include "stptok.h" +#include "motor.h" +#include "HistMem.h" +#include "HistMem.i" +#include "HistDriv.i" +#include "sicsvar.h" +#include "counter.h" +#include "lld.h" +#include "sicshipadaba.h" +#include "sicshdbadapter.h" +#include "sicsdata.h" + +#define PRIVNAM "priv" +/*==================== support code ====================================*/ +static void AddPrivProperty(pHdb node, int priv){ + char pPriv[80]; + switch(priv){ + case usInternal: + strcpy(pPriv,"internal"); + break; + case usMugger: + strcpy(pPriv,"manager"); + break; + case usUser: + strcpy(pPriv,"user"); + break; + case usSpy: + strcpy(pPriv,"spy"); + break; + default: + assert(0); + break; + } + SetHdbProperty(node,PRIVNAM,pPriv); +} +/*=================== motor code =======================================*/ +static int MoveCallback(int iEvent, void *eventData, void *userData, + commandContext cc){ + MotCallback *motData = (MotCallback *)eventData; + pHdb motor = (pHdb)userData; + pHdb pos = NULL; + + if(iEvent == MOTDRIVE && motData != NULL && motor != NULL){ + UpdateHipadabaPar(motor,MakeHdbFloat((double)motData->fVal) + ,NULL); + pos = GetHipadabaNode(motor,"position"); + if(pos != NULL){ + UpdateHipadabaPar(pos,MakeHdbFloat((double)motData->fVal) + ,NULL); + } + } + return 1; +} +/*---------------------------------------------------------------------*/ +static int MotorValueCallback(int iEvent, void *eventData, void *userData, + commandContext cc){ + pHdb motor = (pHdb)userData; + pMotor pMot = (pMotor)eventData; + pHdb current = NULL; + float fVal; + + /* + * as setting some motor parameters might cause other motor + * parametes to change too, I opt for the cheap solution to check + * them all. + */ + if(iEvent == HDBVAL && motor != NULL && pMot != NULL){ + current = motor->child; + while(current != NULL){ + MotorGetPar(pMot,current->name,&fVal); + if(fVal != current->value.v.doubleValue) { + UpdateHipadabaPar(current,MakeHdbFloat((double)fVal),NULL); + } + current = current->next; + } + } + return 1; +} +/*---------------------------------------------------------------------*/ +static hdbCallbackReturn MotorParSetCallback(pHdb currentNode, void *userData, + pHdbMessage message){ + pMotor pMot = (pMotor)userData; + SConnection *pCon = NULL; + int status; + pHdbDataMessage mm = NULL; + + if((mm = GetHdbSetMessage(message)) == NULL){ + return hdbContinue; + } + pCon = mm->callData; + + assert(pMot != NULL && pCon != NULL); + status = MotorSetPar(pMot,pCon,currentNode->name, + (float)mm->v->v.doubleValue); + if(status == 1){ + return hdbContinue; + } else { + return hdbAbort; + } +} +/*----------------------------------------------------------------------*/ +static hdbCallbackReturn MotorParGetCallback(pHdb currentNode, void *userData, + pHdbMessage message){ + pMotor pMot = (pMotor)userData; + float fVal; + int status; + pHdbDataMessage mm = NULL; + + if((mm = GetHdbGetMessage(message)) == NULL){ + return hdbContinue; + } + + assert(pMot != NULL); + status = MotorGetPar(pMot,currentNode->name,&fVal); + currentNode->value.v.doubleValue = fVal; + if(status == 1){ + return hdbContinue; + } else { + return hdbAbort; + } +} +/*---------------------------------------------------------------------*/ +static pHdb MakeMotParNode(char *name, pMotor pMot){ + pHdb node = NULL; + pHdbCallback pCall = NULL; + char command[1024]; + + node = MakeHipadabaNode(name, HIPFLOAT, 1); + if(node != NULL) { + pCall = MakeHipadabaCallback(MotorParSetCallback,pMot,NULL); + if(pCall == NULL){ + return NULL; + } + AppendHipadabaCallback(node,pCall); + pCall = MakeHipadabaCallback(MotorParGetCallback,pMot,NULL); + if(pCall == NULL){ + return NULL; + } + AppendHipadabaCallback(node,pCall); + snprintf(command,1023,"%s %s ", pMot->name, name); + SetHdbProperty(node,"sicscommand", command); + } + return node; +} +/*---------------------------------------------------------------------*/ +static int AddStdMotorPar(pHdb motorNode, pMotor pMot){ + int i; + pHdb parNode = NULL; + char *addPar[] = {"target", + "hardlowerlim", + "hardupperlim", + NULL}; + + i = 0; + while(addPar[i] != NULL){ + parNode = MakeMotParNode(addPar[i],pMot); + SetHdbProperty(parNode,PRIVNAM,"internal"); + if(parNode == NULL){ + return 0; + } + AddHipadabaChild(motorNode,parNode, NULL); + i++; + } + + /* + * Add the parameters in the obpar array + */ + for(i = 0; i < MOTOBPARLENGTH; i++){ + parNode = MakeMotParNode(pMot->ParArray[i].name,pMot); + if(parNode == NULL){ + return 0; + } + AddHipadabaChild(motorNode,parNode, NULL); + AddPrivProperty(parNode,pMot->ParArray[i].iCode); + } + return 1; +} +/*--------------------------------------------------------------------------*/ +static char *getDriverParList(MotorDriver *pDriv){ + SConnection *pCon = NULL; + pDynString list = NULL; + char *listData = NULL; + + if(pDriv->ListDriverPar != NULL){ + pCon = SCCreateDummyConnection(pServ->pSics); + if(pCon == NULL){ + return NULL; + } + SCStartBuffering(pCon); + pDriv->ListDriverPar(pDriv,"test.", pCon); + list = SCEndBuffering(pCon); + if(list != NULL){ + listData = strdup(GetCharArray(list)); + SCDeleteConnection(pCon); + } else { + listData = NULL; + } + return listData; + } + return NULL; +} +/*--------------------------------------------------------------------------*/ +extern char *trim(char *str); +/*--------------------------------------------------------------------------*/ +static char *extractName(char *line){ + char *name = NULL, *pEnd = NULL; + + name = strchr(line,'.'); + assert(name != NULL); + while(*name == '.'){ + name++; + } + pEnd = strchr(name,'='); + assert(pEnd != NULL); + *pEnd = '\0'; + return trim(name); +} +/*------------------------------------------------------------------------*/ +static int CreateDriverParameters(pMotor pM, pHdb parent){ + char *listPtr = NULL, line[80], *pPtr, *name; + pHdb node = NULL; + + listPtr = getDriverParList(pM->pDriver); + if(listPtr == NULL){ + /* + * no driver parameters + */ + return 1; + } + pPtr = listPtr; + while((pPtr = stptok(pPtr,line,79,"\n")) != NULL){ + name = extractName(line); + node = MakeMotParNode(name,pM); + SetHdbProperty(node,PRIVNAM,"manager"); + if(node != NULL){ + AddHipadabaChild(parent,node,NULL); + } + } + free(listPtr); + return 1; +} +/*----------------------------------------------------------------------*/ +static pHdb CreateMotorAdapter(char *name, pMotor pMot){ + pHdb result = NULL; + commandContext comCom; + float access; + + assert(pMot != NULL); + + result = MakeSICSHdbDriv(name,usUser,pMot,HIPFLOAT); + if(result == NULL){ + return NULL; + } + MotorGetPar(pMot,"accesscode",&access); + AddPrivProperty(result,(int)access); + SetHdbProperty(result,"type","drivable"); + SetHdbProperty(result,"sicsdev",pMot->name); + /* + * We want to be notified when this motor drives around. Or + * its parameters change. + */ + strncpy(comCom.deviceID,name,255); + comCom.transID = -77; + RegisterCallback(pMot->pCall,comCom, MOTDRIVE, MoveCallback, + result,NULL); + RegisterCallback(pMot->pCall,comCom, HDBVAL, MotorValueCallback, + result,NULL); + + if(!AddStdMotorPar(result,pMot)){ + DeleteHipadabaNode(result,NULL); + return NULL; + } + + if(!CreateDriverParameters(pMot,result)){ + DeleteHipadabaNode(result,NULL); + return NULL; + } + result->protected = 1; + + return result; +} +/*============== histogram memory ======================================*/ +static long totalSum(int *data, int length){ + long result = 0l; + int i; + + if(data == NULL){ + return 0; + } + for(i = 0; i < length; i++){ + result += data[i]; + } + return result; +} +/*----------------------------------------------------------------------*/ +typedef struct { + pHistMem pHM; + long oldSum; +} HMAdapter, *pHMAdapter; +/*-------------------------------------------------------------------------*/ +static hdbCallbackReturn HMDataGetCallback(pHdb currentNode, void *userData, + pHdbMessage message){ + pHMAdapter pHMA = (pHMAdapter)userData; + SConnection *pCon = NULL; + long sum1; + pHdbDataMessage mm = NULL; + + if((mm = GetHdbGetMessage(message)) == NULL){ + return hdbContinue; + } + pCon = mm->callData; + assert(pHMA != NULL && pHMA->pHM != NULL); + if(pCon == NULL){ + return hdbAbort; + } + currentNode->value.arrayLength = GetHistLength(pHMA->pHM); + currentNode->value.v.intArray = (int *)GetHistogramPointer(pHMA->pHM,pCon); + sum1 = totalSum(currentNode->value.v.intArray, currentNode->value.arrayLength); + if(sum1 != pHMA->oldSum){ + UpdateHipadabaPar(currentNode,currentNode->value,NULL); + pHMA->oldSum = sum1; + } + return hdbContinue; +} +/*----------------------------------------------------------------------*/ +static pHdb MakeHMDataNode(pHistMem pHM, char *name){ + pHdb node = NULL; + pHdbCallback pCall = NULL; + pHMAdapter pHMA = NULL; + + node = MakeHipadabaNode(name,HIPINTVARAR,2); + pHMA = malloc(sizeof(HMAdapter)); + if(node == NULL || pHMA == NULL){ + return NULL; + } + pHMA->pHM = pHM; + pHMA->oldSum = 0; + node->value.doNotFree = 1; + pCall = MakeHipadabaCallback(HMDataGetCallback,pHMA,free); + if(pCall == NULL){ + return NULL; + } + AppendHipadabaCallback(node,pCall); + AppendHipadabaCallback(node,MakeReadOnlyCallback()); + + return node; +} +/*================ SICS Variable ======================================*/ +static hdbCallbackReturn SicsVarSetCallback(pHdb currentNode, void *userData, + pHdbMessage message){ + pSicsVariable pVar = (pSicsVariable)userData; + SConnection *pCon = NULL; + int userRights = usMugger; + pHdbDataMessage mm = NULL; + + if((mm = GetHdbSetMessage(message)) == NULL){ + return hdbContinue; + } + pCon = mm->callData; + + assert(pVar != NULL); + + if(pCon != NULL){ + userRights = SCGetRights(pCon); + } + switch(currentNode->value.dataType){ + case HIPINT: + VarSetInt(pVar, mm->v->v.intValue, userRights); + break; + case HIPFLOAT: + VarSetFloat(pVar, (float)mm->v->v.doubleValue, userRights); + break; + case HIPTEXT: + VarSetText(pVar, mm->v->v.text, userRights); + break; + } + return hdbContinue; +} +/*----------------------------------------------------------------------*/ +static int ValueCallback(int iEvent, void *eventData, void *userData, + commandContext cc){ + pSicsVariable pVar = (pSicsVariable)eventData; + pHdb node = (pHdb)userData; + hdbValue v; + + if(iEvent == VALUECHANGE && pVar != NULL && node != NULL){ + switch(pVar->eType){ + case veInt: + v = MakeHdbInt(pVar->iVal); + break; + case veFloat: + v = MakeHdbFloat((double)pVar->fVal); + break; + case veText: + v = MakeHdbText(pVar->text); + break; + } + UpdateHipadabaPar(node,v,NULL); + } + + return 1; +} +/*----------------------------------------------------------------------*/ +static pHdb MakeSicsVarNode(pSicsVariable pVar, char *name){ + pHdb node = NULL; + pHdbCallback pCall = NULL; + commandContext comCom; + int type; + char command[1024]; + + switch(pVar->eType){ + case veInt: + type = HIPINT; + break; + case veFloat: + type = HIPFLOAT; + break; + case veText: + type = HIPTEXT; + break; + } + node = MakeHipadabaNode(name,type,1); + if(node == NULL){ + return NULL; + } + if(pVar->iLock == 1) { + AddPrivProperty(node,usInternal); + } else { + AddPrivProperty(node,pVar->iAccessCode); + } + pCall = MakeHipadabaCallback(SicsVarSetCallback,pVar,NULL); + if(pCall == NULL){ + return NULL; + } + strncpy(comCom.deviceID,name,255); + comCom.transID = -77; + AppendHipadabaCallback(node,pCall); + RegisterCallback(pVar->pCall,comCom, VALUECHANGE, ValueCallback, + node,NULL); + snprintf(command,1023,"%s ", pVar->name); + SetHdbProperty(node,"sicscommand",command); + + node->protected = 1; + return node; +} +/*================ counter =============================================*/ +typedef struct { + pHdb node; + int monitor; /* -1 == time */ + pCounter counter; +} CountEntry; +static int countList = -10; +/*---------------------------------------------------------------------*/ +static void updateCountList(){ + int status; + hdbValue v; + CountEntry hugo; + long monitor; + float time; + SConnection *pDummy = NULL; + + if(countList < 0){ + return; + } + pDummy = SCCreateDummyConnection(pServ->pSics); + if(pDummy == NULL){ + return; + } + + status = LLDnodePtr2First(countList); + while(status != 0){ + LLDnodeDataTo(countList,&hugo); + if(hugo.monitor < 0){ + time = GetCountTime(hugo.counter,pDummy); + v = MakeHdbFloat((double)time); + UpdateHipadabaPar(hugo.node,v, NULL); + } else { + monitor = GetMonitor(hugo.counter, hugo.monitor, pDummy); + v = MakeHdbInt((int)monitor); + UpdateHipadabaPar(hugo.node,v, NULL); + } + status = LLDnodePtr2Next(countList); + } + SCDeleteConnection(pDummy); +} +/*---------------------------------------------------------------------------*/ +static int CounterCallback(int iEvent, void *eventData, void *userData, + commandContext cc){ + if(iEvent == MONITOR || iEvent == COUNTEND){ + updateCountList(); + } + return 1; +}/*=================== SICSData ========================================*/ +static void copyIntSicsData(pHdb node, pSICSData data){ + if(node->value.arrayLength != data->dataUsed){ + if(node->value.v.intArray != NULL){ + free(node->value.v.intArray); + } + node->value.arrayLength = data->dataUsed; + node->value.v.intArray = malloc(data->dataUsed*sizeof(int)); + if(node->value.v.intArray == NULL){ + node->value.arrayLength = 0; + return; + } + } + memcpy(node->value.v.intArray, data->data, + data->dataUsed*sizeof(int)); +} +/*-----------------------------------------------------------------------*/ +static void copyFloatSicsData(pHdb node, pSICSData data){ + int i; + float val; + + if(node->value.arrayLength != data->dataUsed){ + if(node->value.v.floatArray != NULL){ + free(node->value.v.floatArray); + } + node->value.arrayLength = data->dataUsed; + node->value.v.floatArray = malloc(data->dataUsed*sizeof(double)); + if(node->value.v.floatArray == NULL){ + node->value.arrayLength = 0; + return; + } + } + for(i = 0; i < data->dataUsed; i++){ + getSICSDataFloat(data,i,&val); + node->value.v.floatArray[i] = val; + } +} +/*----------------------------------------------------------------------*/ +static hdbCallbackReturn SICSDataCallback(pHdb node, void *userData, + pHdbMessage message){ + pSICSData self = (pSICSData)userData; + pHdbDataMessage mm = NULL; + int i, status; + char script[256], error[1024]; + + assert(self != NULL); + + /* + * I have to make copies because the floats in SICSData + * are floats but doubles in Hipdaba. Siiiigggghhhh! + * But it is cleaner in some way anyway. + */ + if((mm = GetHdbGetMessage(message)) != NULL){ + memset(script,0,256); + if(GetHdbProperty(node,"readscript", script,256) == 1){ + status = Tcl_Eval(InterpGetTcl(pServ->pSics),script); + if(status != TCL_OK){ + snprintf(error,1023,"ERROR: Tcl returned error: %s", + Tcl_GetStringResult(InterpGetTcl(pServ->pSics))); + if(mm->callData != NULL){ + SCWrite((SConnection *)mm->callData, error, eError); + return hdbAbort; + } + } + } + if(node->value.dataType == HIPINTVARAR){ + copyIntSicsData(node, self); + } else if(node->value.dataType == HIPFLOATVARAR){ + copyFloatSicsData(node, self); + } + return hdbContinue; + } + + if((mm = GetHdbSetMessage(message)) != NULL){ + if(node->value.dataType == HIPINTVARAR){ + for(i = 0; i < mm->v->arrayLength; i++){ + setSICSDataInt(self,i,mm->v->v.intArray[i]); + } + } else if(node->value.dataType == HIPFLOATVARAR){ + for(i = 0; i < mm->v->arrayLength; i++){ + setSICSDataFloat(self,i,(float)mm->v->v.floatArray[i]); + } + } + memset(script,0,256); + if(GetHdbProperty(node,"writescript", script,256) == 1){ + status = Tcl_Eval(InterpGetTcl(pServ->pSics),script); + if(status != TCL_OK){ + snprintf(error,1023,"ERROR: Tcl returned error: %s", + Tcl_GetStringResult(InterpGetTcl(pServ->pSics))); + if(mm->callData != NULL){ + SCWrite((SConnection *)mm->callData, error, eError); + return hdbAbort; + } + } + } + return hdbContinue; + } + + return hdbContinue; +} +/*============== interpreter function ==================================*/ +int SICSHdbAdapter(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb path = NULL; + pHdb node = NULL; + pMotor pMot = NULL; + pHistMem pHM = NULL; + CommandList *pCom = NULL; + pIDrivable pDriv = NULL; + pSicsVariable pVar = NULL; + char buffer[512]; + pCounter pCount = NULL; + CountEntry hugo; + pSICSData data = NULL; + int type; + pHdbCallback pCall = NULL; + + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + if(argc < 4) { + SCWrite(pCon,"ERROR: Insufficient number of arguments",eError); + return 0; + } + + path = FindHdbNode(NULL,argv[1],pCon); + if(path == NULL){ + SCWrite(pCon,"ERROR: path to attach object too not found",eError); + return 0; + } + + /* + * look for motors + */ + pMot = (pMotor)FindCommandData(pSics,argv[2],"Motor"); + if(pMot != NULL){ + node = CreateMotorAdapter(argv[3],pMot); + if(node == NULL){ + SCWrite(pCon,"ERROR: out of memory creating motor node",eError); + return 0; + } + AddHipadabaChild(path,node,pCon); + SCSendOK(pCon); + return 1; + } + /* + * look for drivables + */ + pDriv = FindDrivable(pSics,argv[2]); + pCom = FindCommand(pSics,argv[2]); + if(pDriv != NULL && pCom != NULL && pCom->pData != NULL){ + node = MakeSICSHdbDriv(argv[3],usUser,pCom->pData,HIPFLOAT); + if(node == NULL){ + SCWrite(pCon,"ERROR: out of memory creating drivable node",eError); + return 0; + } + SetHdbProperty(node,PRIVNAM,"user"); + SetHdbProperty(node,"type","drivable"); + SetHdbProperty(node,"sicsdev",argv[2]); + AddHipadabaChild(path,node,pCon); + SCSendOK(pCon); + return 1; + } + + /** + * look for SICS Variables + */ + pVar = (pSicsVariable)FindCommandData(pSics,argv[2],"SicsVariable"); + if(pVar != NULL){ + node = MakeSicsVarNode(pVar,argv[3]); + if(node == NULL){ + SCWrite(pCon,"ERROR: out of memory creating SICS variable node", + eError); + return 0; + } + AddHipadabaChild(path,node,pCon); + SCSendOK(pCon); + return 1; + } + + /* + * look for histogram memories + */ + pHM = (pHistMem)FindCommandData(pSics,argv[2],"HistMem"); + if(pHM != NULL){ + node = MakeHMDataNode(pHM,argv[3]); + if(node == NULL){ + SCWrite(pCon,"ERROR: out of memory creating HM node",eError); + return 0; + } + AddHipadabaChild(path,node,pCon); + SCSendOK(pCon); + return 1; + } + + /** + * look for counters + */ + pCount = (pCounter)FindCommandData(pSics,argv[2],"SingleCounter"); + if(pCount != NULL){ + hugo.monitor = atoi(argv[3]); + hugo.counter = pCount; + hugo.node = path; + if(countList < 0){ + countList = LLDcreate(sizeof(CountEntry)); + RegisterCallback(pCount->pCall, SCGetContext(pCon), + COUNTSTART, CounterCallback, + NULL, NULL); + RegisterCallback(pCount->pCall, SCGetContext(pCon), + COUNTEND, CounterCallback, + NULL, NULL); + RegisterCallback(pCount->pCall, SCGetContext(pCon), + MONITOR, CounterCallback, + NULL, NULL); + } + LLDnodeAppendFrom(countList,&hugo); + SCSendOK(pCon); + return 1; + } + + /** + * look for SICSData + */ + data = (pSICSData)FindCommandData(pSics,argv[2],"SICSData"); + if(data != NULL){ + if(argc < 5){ + SCWrite(pCon,"ERROR: need type and name to create SICSData adapter", + eError); + return 0; + } + type = convertHdbType(argv[3]); + if(type != HIPINTVARAR && type != HIPFLOATVARAR ){ + SCWrite(pCon, + "ERROR: need intvarar or floatvarar type for SICSData adapter", + eError); + return 0; + } + node = MakeHipadabaNode(argv[4],type,0); + if(node == NULL){ + SCWrite(pCon,"ERROR: out of memory in SICSHdbAdapter", eError); + return 0; + } + pCall = MakeHipadabaCallback(SICSDataCallback,data,NULL); + if(pCall == NULL){ + SCWrite(pCon,"ERROR: out of memory in SICSHdbAdapter", eError); + return 0; + } + AppendHipadabaCallback(node,pCall); + AddHipadabaChild(path,node,pCon); + SCSendOK(pCon); + return 1; + } + + snprintf(buffer,511, + "ERROR: attaching this type of object: %s at %s not implemented", + argv[2], argv[1]); + SCWrite(pCon,buffer,eError); + return 0; +} +/*====================== SubSample =========================================*/ +int HdbSubSample(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHistMem pHM = NULL; + pHdb node = NULL; + int bank = 0, length = -1, status; + HistInt *data = NULL; + char *pPtr = NULL; + hdbValue v; + + if(argc < 4){ + SCWrite(pCon,"ERROR: insufficient number of arguments to HdbSubSample", + eError); + return 0; + } + + pPtr = strchr(argv[1],':'); + if(pPtr != NULL){ + *pPtr = '\0'; + pPtr++; + sscanf(pPtr,"%d",&bank); + } + pHM = (pHistMem)FindCommandData(pSics,argv[1],"HistMem"); + node = FindHdbNode(NULL,argv[2], pCon); + if(pHM == NULL || node == NULL){ + SCWrite(pCon,"ERROR: either histogram memory or node not found!", + eError); + return 0; + } + + if(pHM->pDriv->SubSample == NULL){ + SCWrite(pCon,"ERROR: hm does not support subsampling", eError); + return 0; + } + data = pHM->pDriv->SubSample(pHM->pDriv, pCon, bank, argv[3]); + if(data == NULL){ + SCWrite(pCon,"ERROR: sub sampling failed", eError); + return 0; + } + + v.dataType = HIPINTVARAR; + v.arrayLength = data[0]; + v.v.intArray = data+1; + + UpdateHipadabaPar(node,v,pCon); + free(data); + SCSendOK(pCon); + return 1; +} diff --git a/sicshdbadapter.h b/sicshdbadapter.h new file mode 100644 index 00000000..563b95f9 --- /dev/null +++ b/sicshdbadapter.h @@ -0,0 +1,25 @@ +/* + * Experience has shown that integrating existing SICS objects into the + * Hierarchical Parameter Database (Hdb) is a difficult task without reworking + * the complete SICS object model. Rather, it seems easier to adapt some + * critical objects to the Hdb with some glue code. Following the facade or + * adapter design pattern. This is the purpose of this module. For the moment + * the external interface is only an interpreter function which will be used to + * install suitable SICS objects into the Hdb tree and generates the necessary + * adapters internally. This code can be used to adapt to: + * - motors + * - the data segment of histogram memories + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, November 2006 + */ +#ifndef SICSHDBADAPTER_H_ +#define SICSHDBADAPTER_H_ + +int SICSHdbAdapter(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); +int HdbSubSample(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + +#endif /*SICSHDBADAPTER_H_*/ diff --git a/sicshdbfactory.c b/sicshdbfactory.c new file mode 100644 index 00000000..aca0ed08 --- /dev/null +++ b/sicshdbfactory.c @@ -0,0 +1,348 @@ +/** + * This implements the hfactory command which is used to create + * hipadaba nodes. + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, March 2008, + * reusing code from former separate node creation functions. + */ +#include +#include "statusfile.h" + +#define MAX_HDB_PATH 1024 +/*-------------------------------------------------------------------------*/ +static int MakePlainNode(pHdb parent, char *name, SConnection *pCon, + int argc, char *argv[]){ + pHdb child = NULL; + int type = 0, length = 0, priv = -1; + hdbValue val; + + if(argc < 5){ + SCWrite(pCon,"ERROR: not enough arguments to create plain node", + eError); + return 0; + } + + /* + * convert privilege + */ + priv = decodeSICSPriv(argv[3]); + if(priv < 0){ + SCPrintf(pCon,eError,"ERROR: %s is no valid privilege code", argv[3]); + return 0; + } + /* + * convert datatype + */ + strtolower(argv[4]); + type = convertHdbType(argv[4]); + if(type > HIPFLOATVARAR){ + SCWrite(pCon, + "ERROR: invalid type requested: none, int, float, text, intar, floatar, intvarar, floatvarar supported", + eError); + return 0; + } + if(type >= HIPINTAR){ + if( argc < 6){ + SCWrite(pCon,"ERROR: array length missing for array data type", + eError); + return 0; + } else { + length = atoi(argv[5]); + } + } + + if(type != HIPNONE){ + val = makeHdbValue(type,length); + child = MakeSICSHdbPar(name, priv, val); + ReleaseHdbValue(&val); + } else { + child = MakeHipadabaNode(name,type,length); + } + if(child == NULL){ + SCWrite(pCon,"ERROR: out of memory creating node",eError); + return 0; + } + + AddHipadabaChild(parent,child,pCon); + SCSendOK(pCon); + return 1; +} +/*--------------------------------------------------------------------------*/ +static int MakeScriptNode(pHdb parent, char *name, SConnection *pCon, + int argc, char *argv[]){ + int type, length; + pHdb child = NULL; + hdbValue v; + + if(argc < 5){ + SCWrite(pCon, + "ERROR: not enough arguments to create script parameter node", + eError); + return 0; + } + + /* + * convert datatype + */ + strtolower(argv[5]); + type = convertHdbType(argv[5]); + if(type >= 7){ + SCWrite(pCon, + "ERROR: invalid type requested: none, int, float, text, intar, floatar, intvarar, floatvarar supported", + eError); + return 0; + } + if(type > 2){ + if( argc < 7){ + SCWrite(pCon,"ERROR: array length missing for array data type", + eError); + return 0; + } else { + length = atoi(argv[6]); + } + } + + v = makeHdbValue(type,length); + child = MakeSICSScriptPar(name, argv[4], argv[3], v); + ReleaseHdbValue(&v); + if(child == NULL){ + SCWrite(pCon,"ERROR: out of memory creating node",eError); + return 0; + } + + AddHipadabaChild(parent,child,pCon); + SCSendOK(pCon); + return 1; +} +/*-------------------------------------------------------------------------*/ +static int MakeLinkNode(pHdb parent, char *name, SConnection *pCon, + int argc, char *argv[]){ + pHdb node = NULL; + pObjectDescriptor pDes = NULL; + char buffer[256]; + + if(argc < 4){ + SCWrite(pCon,"ERROR: not enough arguments to create script node", + eError); + return 0; + } + pDes = FindCommandDescriptor(pServ->pSics,argv[3]); + if(pDes == NULL){ + snprintf(buffer,255,"ERROR: failed to find object %s", argv[3]); + SCWrite(pCon,buffer,eError); + return 0; + } + if(pDes->parNode == NULL){ + snprintf(buffer,255, + "ERROR: Object %s does not use Hipadaba natively and thus cannot be linked", + argv[3]); + SCWrite(pCon,buffer,eError); + return 0; + } + + if(pDes->parNode->mama != NULL){ + snprintf(buffer,255, + "ERROR: Object %s is already linked somewhere else", + argv[3]); + SCWrite(pCon,buffer,eError); + return 0; + } + + AddHipadabaChild(parent,pDes->parNode,pCon); + if(pDes->parNode->name != NULL){ + free(pDes->parNode->name); + } + pDes->parNode->name = strdup(name); + SCSendOK(pCon); + return 1; +} +/* -------------------------------------------------------------------------- + * This is actually SCInvoke but without advancing the context. I think this + * is only of local use. It makes sure that commands executed as Hipadaba + * commands get logged properly. + ---------------------------------------------------------------------------*/ + static int HDBInvoke(SConnection *self, SicsInterp *pInter, char *pCommand) + { + int iRet; + long lLen; + const char *pResult = NULL; + char *pBuffer = NULL, *pFile = NULL; + char pBueffel[80]; + int i, iSpace; + + assert(pInter); + + /* print command to log files */ + for( i = 0; i < self->iFiles; i++) + { + if(self->pFiles[i]) + { + fprintf(self->pFiles[i],"SICS>> %s\n",pCommand); + } + } + + /* print to command log if user or manager */ + if(SCGetRights(self) <= usUser) + { + if(self->pSock != NULL) + { + sprintf(pBueffel,"sock %d>>",self->pSock->sockid); + } + } + + /* invoke */ + self->inUse++; + self->eInterrupt = eContinue; + /* + get first word of command + */ + iRet = InterpExecute(pInter,self,pCommand); + + StatusFileTask(NULL); /* save changed parameters */ + + self->inUse--; + return iRet; + } +/*---------------------------------------------------------------------------*/ +static hdbCallbackReturn CommandSetCallback(pHdb node, void *userData, + pHdbMessage message){ + SConnection *pCon = NULL; + pDynString cmd = NULL, par = NULL; + pHdb current = NULL; + int status; + pHdbDataMessage mm = NULL; + hdbValue v; + + if((mm = GetHdbSetMessage(message)) == NULL){ + return hdbContinue; + } + pCon = (SConnection *)pCon; + v = *(mm->v); + + if(pCon == NULL){ + printf("Cannot invoke command without connection\n"); + return hdbAbort; + } + + if(v.dataType == HIPTEXT){ + if(strstr(v.v.text,"start") != NULL) { + cmd = CreateDynString(64,64); + if(cmd == 0){ + SCWrite(pCon,"ERROR: out of memory in CommandSetCallback",eError); + return 0; + } + DynStringCopy(cmd, node->value.v.text); + DynStringConcat(cmd," "); + current = node->child; + while(current != NULL){ + par = formatValue(current->value, current); + if(par != NULL){ + DynStringConcat(cmd, GetCharArray(par)); + DynStringConcat(cmd," "); + DeleteDynString(par); + } + current = current->next; + } + status = HDBInvoke(pCon,pServ->pSics, GetCharArray(cmd)); + DeleteDynString(cmd); + if(status == 1){ + return hdbContinue; + } else { + return hdbAbort; + } + } else { + SCWrite(pCon,"ERROR: this node only understands start as value",eError); + return hdbAbort; + } + } + return hdbContinue; +} +/*---------------------------------------------------------------------------*/ +static hdbCallbackReturn CommandGetCallback(pHdb node, void *userData, + pHdbMessage message){ + pHdbDataMessage mm = NULL; + + if((mm = GetHdbGetMessage(message)) == NULL){ + return hdbContinue; + } + + hdbValue v2 = MakeHdbText(strdup("Nothing to get")); + copyHdbValue(&v2, mm->v); + return hdbContinue; +} +/*--------------------------------------------------------------------------*/ +static int MakeCommandNode(pHdb parent, char *name, SConnection *pCon, + int argc, char *argv[]){ + pHdb node = NULL; + pHdbCallback kalle = NULL; + + if(argc < 4){ + SCWrite(pCon,"ERROR: not enough arguments to create command node", + eError); + return 0; + } + node = MakeHipadabaNode(name, HIPTEXT, 1); + if(node == NULL){ + SCWrite(pCon,"ERROR: out of memory in hcommand",eError); + return 0; + } + node->value.v.text = strdup(argv[3]); + node->value.arrayLength = strlen(argv[3]); + SetHdbProperty(node,"sicscommand", argv[3]); + + kalle = MakeHipadabaCallback(CommandSetCallback,NULL, NULL); + if(kalle == NULL){ + SCWrite(pCon,"ERROR: out of memory in hcommand",eError); + return 0; + } + AppendHipadabaCallback(node,kalle); + + kalle = MakeHipadabaCallback(CommandGetCallback,NULL, NULL); + if(kalle == NULL){ + SCWrite(pCon,"ERROR: out of memory in hcommand",eError); + return 0; + } + AppendHipadabaCallback(node,kalle); + + AddHipadabaChild(parent,node,pCon); + + SCSendOK(pCon); + return 1; +} +/*--------------------------------------------------------------------------*/ +int HdbNodeFactory(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + char *name = NULL; + pHdb parent = NULL; + + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + + if(argc < 3) { + SCWrite(pCon,"ERROR: not enough arguments to hfactory",eError); + return 0; + } + + parent = FindHdbParent(NULL, argv[1], &name, pCon); + if (parent == NULL) { + return 0; /* error messages written inside FindHdbParent */ + } + + strtolower(argv[2]); + if(strcmp(argv[2],"plain") == 0){ + return MakePlainNode(parent,name,pCon,argc,argv); + } else if(strcmp(argv[2],"script") == 0){ + return MakeScriptNode(parent,name,pCon,argc,argv); + } else if(strcmp(argv[2],"link") == 0){ + return MakeLinkNode(parent,name,pCon,argc,argv); + } else if(strcmp(argv[2],"command") == 0){ + return MakeCommandNode(parent,name,pCon,argc,argv); + } else { + SCWrite(pCon,"ERROR: node type not recognised", eError); + return 0; + } + return 0; +} diff --git a/sicshipadaba.c b/sicshipadaba.c new file mode 100644 index 00000000..8aca6d6c --- /dev/null +++ b/sicshipadaba.c @@ -0,0 +1,3171 @@ +/** + * This is a set of helper functions for SICS to work with the hierarchical parameter + * database hipadaba. In SICS, the calldata associated with any callback will always + * be the connection object. + * + * copyright: GPL + * + * Mark Koennecke, June 2006 + * + * Introduced notification on tree changes, Mark Koennecke, November 2006 + * + * Added property functions, Mark Koennecke, January 2007 + * + * Added hmatchprop function. Mark Koennecke, February 2008 + * + * TODO: separate this into two modules: sicshipadaba proper and sicshipadabaint for the + * interpreter interface. + * + * Refactored to new callback system, Markus Zolliker, Mark Koennecke, March 2008 + */ +#include +#include +#include +#include +#include +#include +#include "sicspoll.h" +#include +#include +#include "protocol.h" +#include "statusfile.h" +#include +#include "sicsobj.h" +#include + +#define MAX_HDB_PATH 1024 + +/*== there can be only one hipadaba in SICS, some globals to care for that == */ +static pHdb root = NULL; +static pSicsPoll poller = NULL; +typedef enum { + normal_protocol, + json_protocol, +} Protocol; +char *trim(char *str); +/*====================== Messages ==================================================*/ +static char killID[] = {"killID"}; +static char killInternalID[] = {"killInternalID"}; +static char killPtr[] = {"killPtr"}; +/*----------------------------------------------------------------------------------*/ +pHdbIDMessage GetKillIDMessage(pHdbMessage message){ + if(message->type == killID){ + return (pHdbIDMessage)message; + } + return NULL; +} +/*-----------------------------------------------------------------------------------*/ +pHdbIDMessage GetKillInternalIDMessage(pHdbMessage message){ + if(message->type == killInternalID){ + return (pHdbIDMessage)message; + } + return NULL; +} +/*-----------------------------------------------------------------------------------*/ +pHdbPtrMessage GetKillPtrMessage(pHdbMessage message){ + if(message->type == killPtr){ + return (pHdbPtrMessage)message; + } + return NULL; +} + +/*=============== common callback functions used for SICS ===========================*/ +static hdbCallbackReturn SICSCheckPermissionCallback(pHdb node, void *userData, + pHdbMessage message){ + int *testPriv = NULL; + SConnection *pCon = NULL; + pHdbDataMessage mm = NULL; + + testPriv = (int *)userData; + + mm = GetHdbSetMessage(message); + if(mm == NULL){ + return hdbContinue; + } + pCon = (SConnection *)mm->callData; + + /* + * If pCon is NULL, then this is an internal call from some driver + * code where no permission check is necessary. However, when called + * through the hipadaba tree commands and other upper level code, the + * check will be honoured. + */ + if(pCon == NULL){ + return hdbContinue; + } + + assert(testPriv != NULL); + + if(SCMatchRights(pCon,*testPriv) == 1){ + return hdbContinue; + } else { + return hdbAbort; + } +} +/*--------------------------------------------------------------------------------------*/ +pHdbCallback MakeCheckPermissionCallback(int priv){ + int *testPriv = NULL; + + testPriv = malloc(sizeof(int)); + if(testPriv == NULL){ + return NULL; + } + *testPriv = priv; + return MakeHipadabaCallback(SICSCheckPermissionCallback, testPriv,free); +} +/*-------------------------------------------------------------------------------------*/ +static char *removeSetUpdateID = "removeSetUpdate"; + +void RemoveSetUpdateCallback(pHdb node) { + hdbPtrMessage m; + + m.type = killPtr; + m.pPtr = removeSetUpdateID; + InvokeCallbackChain(node, (pHdbMessage)&m); +} +/*-------------------------------------------------------------------------------------*/ +static hdbCallbackReturn SICSSetUpdateCallback(pHdb node, void *userData, + pHdbMessage message){ + pHdbDataMessage mm = NULL; + pHdbPtrMessage pm = NULL; + int status; + + pm = GetKillPtrMessage(message); + if (pm != NULL) { + if (pm->pPtr == removeSetUpdateID) { + return hdbKill; + } + return hdbContinue; + } + mm = GetHdbSetMessage(message); + if(mm == NULL){ + return hdbContinue; + } + status = UpdateHipadabaPar(node,*(mm->v),mm->callData); + if (status) { + SCSendOK(mm->callData); + } + return hdbContinue; +} +/*-------------------------------------------------------------------------------------*/ +pHdbCallback MakeSetUpdateCallback(){ + return MakeHipadabaCallback(SICSSetUpdateCallback, NULL,NULL); +} +/*---------------------------------------------------------------------------------------*/ +static hdbCallbackReturn SICSReadOnlyCallback(pHdb node, void *userData, pHdbMessage message){ + SConnection *pCon = NULL; + pHdbDataMessage mm = NULL; + + mm = GetHdbSetMessage(message); + if(mm == NULL){ + return hdbContinue; + } + pCon = (SConnection *)mm->callData; + + if(pCon != NULL){ + SCWrite(pCon,"ERROR: parameter is READ-ONLY", eError); + } + return hdbAbort; +} +/*-------------------------------------------------------------------------------------*/ +pHdbCallback MakeReadOnlyCallback(){ + return MakeHipadabaCallback(SICSReadOnlyCallback, NULL,NULL); +} +/*-------------------------------------------------------------------------------------*/ +static hdbCallbackReturn SICSDriveCallback(pHdb node, void *userData, + pHdbMessage message){ + SConnection *pCon = NULL; + pDummy dum = NULL; + char pSicsdev[80]; + int status; + pHdbDataMessage mm = NULL; + hdbValue v; + + mm = GetHdbSetMessage(message); + if(mm == NULL){ + return hdbContinue; + } + + pCon = (SConnection *)mm->callData; + v = *(mm->v); + + dum = (pDummy)userData; + assert(pCon != NULL && dum != NULL); + if(GetHdbProperty(node,"sicsdev",pSicsdev,79)){ + status = StartDevice(pServ->pExecutor,pSicsdev,dum->pDescriptor, + userData, pCon, (float)v.v.doubleValue); + } else { + status = StartDevice(pServ->pExecutor,node->name,dum->pDescriptor, + userData, pCon, (float)v.v.doubleValue); + } + if(status == 1){ + return hdbContinue; + } else { + return hdbAbort; + } +} +/*---------------------------------------------------------------------------------------*/ +pHdbCallback MakeSICSDriveCallback(void *sicsObject){ + return MakeHipadabaCallback(SICSDriveCallback, sicsObject,NULL); +} +/*---------------------------------------------------------------------------------------*/ +static hdbCallbackReturn SICSReadDriveCallback(pHdb node, void *userData, + pHdbMessage message){ + static SConnection *defCon = NULL; + SConnection *pCon = NULL; + pDummy dum = NULL; + pIDrivable pDriv = NULL; + float value; + pHdbDataMessage mm = NULL; + + mm = GetHdbGetMessage(message); + if(mm == NULL){ + return hdbContinue; + } + + pCon = (SConnection *)mm->callData; + dum = (pDummy)userData; + assert(dum != NULL); + + if(defCon == NULL){ + defCon = SCCreateDummyConnection(pServ->pSics); + } + + pDriv = dum->pDescriptor->GetInterface(dum,DRIVEID); + assert(pDriv != NULL); + if(pCon != NULL){ + value = pDriv->GetValue(dum,pCon); + node->value.v.doubleValue = (double)value; + mm->v->v.doubleValue = (double)value; + } else { + if(defCon != NULL){ + value = pDriv->GetValue(dum,defCon); + node->value.v.doubleValue = (double)value; + mm->v->v.doubleValue = (double)value; + } + } + return hdbContinue; +} +/*--------------------------------------------------------------------------*/ +static hdbCallbackReturn SICSFuncCallback(pHdb node, void *userData, + pHdbMessage message){ + pHdb par[64], current = NULL; + int nPar = 0; + SICSOBJFunc func = NULL; + pHdbDataMessage mm = NULL; + int status; + + mm = GetHdbSetMessage(message); + if(mm == NULL){ + return hdbContinue; + } + + assert(node->value.dataType == HIPFUNC); + if(userData == NULL || mm->callData == NULL){ + printf("Great Badness in calling SICSFuncCallback\n"); + return hdbAbort; + } + + current = node->child; + while(current != NULL){ + par[nPar] = current; + nPar++; + current = current->next; + } + func = (SICSOBJFunc)node->value.v.func; + if(func != NULL){ + status = func((pSICSOBJ)userData,(SConnection *)mm->callData, + node, par,nPar); + } else { + printf("Great Badness in calling SICSFuncCallback\n"); + return hdbAbort; + } + if(status == 1){ + return hdbContinue; + } else { + return hdbAbort; + } +} +/*---------------------------------------------------------------------------*/ +pHdbCallback MakeSICSFuncCallback(void *obj){ + return MakeHipadabaCallback(SICSFuncCallback, obj,NULL); +} +/*--------------------------------------------------------------------------------------*/ +pHdbCallback MakeSICSReadDriveCallback(void *sicsObject){ + return MakeHipadabaCallback(SICSReadDriveCallback, sicsObject,NULL); +} +/*---------------------------------------------------------------------------------------*/ +typedef struct { + SConnection *pCon; + commandContext context; + int ID; + int internalID; +}HdbCBInfo; + +static Protocol isJSON(SConnection *pCon) { + char proName[128]; + void *pData; + + if(SCinMacro(pCon)){ + return normal_protocol; + } + pData = FindCommandData(pServ->pSics, "protocol","Protocol"); + ProtocolGet(pCon, pData, proName, 128); + if (strcmp(proName, "json") == 0) + return json_protocol; + else + return normal_protocol; +} + +/* Format a name,value pair according to the given protocol */ +int formatNameValue(Protocol protocol, char *name, char *value, pDynString result, int hdtype) { + char *char_arr, *ptr; + + switch(protocol) { + case normal_protocol: + DynStringCopy(result,name); + DynStringConcat(result," = "); + DynStringConcat(result,value); + break; + case json_protocol: + switch(hdtype){ + case HIPNONE: + break; + case HIPINT: + case HIPFLOAT: + DynStringCopy(result,"{\""); + DynStringConcat(result,name); + DynStringConcat(result,"\": "); + DynStringConcat(result,value); + DynStringConcat(result,"}"); + break; + case HIPTEXT: + DynStringCopy(result,"{\""); + DynStringConcat(result,name); + DynStringConcat(result,"\": \""); + DynStringConcat(result,value); + DynStringConcat(result,"\"}"); + break; + case HIPINTAR: + case HIPINTVARAR: + case HIPFLOATAR: + case HIPFLOATVARAR: + char_arr = ptr = strdup(trim(value)); + while(*ptr != '\0') { + if (isspace(*ptr)) + *ptr=','; + ptr++; + } + DynStringCopy(result,"{\""); + DynStringConcat(result,name); + DynStringConcat(result,"\": [ "); + DynStringConcat(result,char_arr); + DynStringConcat(result," ]}"); + if (char_arr != NULL ) free(char_arr); + break; + } + } + return protocol; +} +/*----------------------------------------------------------------------------*/ +static int sendZippedNodeData(pHdb node, SConnection *pCon){ + hdbValue newValue; + int i, *iData = NULL; + char *path = NULL; + + newValue = node->value; + path = GetHipadabaPath(node); + switch(newValue.dataType){ + case HIPINTAR: + case HIPINTVARAR: + if(newValue.v.intArray == NULL){ + free(path); + return 0; + } + iData = (int *)malloc(newValue.arrayLength*sizeof(int)); + if(iData == NULL){ + SCWrite(pCon,"ERROR: out of memory in sendZippedData",eError); + free(path); + return 0; + } + memset(iData,0,newValue.arrayLength*sizeof(int)); + for(i = 0; i < newValue.arrayLength; i++){ + iData[i] = htonl(newValue.v.intArray[i]); + } + SCWriteZipped(pCon,path, iData, + newValue.arrayLength*sizeof(int)); + free(iData); + break; + case HIPFLOATAR: + case HIPFLOATVARAR: + if(newValue.v.floatArray == NULL){ + free(path); + return 0; + } + iData = (int *)malloc(newValue.arrayLength*sizeof(int)); + if(iData == NULL){ + SCWrite(pCon,"ERROR: out of memory in sendZippedData",eError); + free(path); + return 0; + } + memset(iData,0,newValue.arrayLength*sizeof(int)); + for(i = 0; i < newValue.arrayLength; i++){ + iData[i] = htonl((int)(newValue.v.floatArray[i]*65536.)); + } + SCWriteZipped(pCon,path, iData, + newValue.arrayLength*sizeof(int)); + free(iData); + break; + default: + SCWrite(pCon,"ERROR: zipped writing not supported for this datatype", + eError); + free(path); + return 0; + } + free(path); + return 1; +} +/*----------------------------------------------------------------------------------------*/ +static hdbCallbackReturn SICSNotifyCallback(pHdb node, void *userData, + pHdbMessage message ){ + HdbCBInfo *cbInfo = NULL; + pDynString printedData = NULL; + pDynString result = NULL; + char *pPath = NULL; + Protocol protocol = normal_protocol; + int outCode, macro, status; + char value[80]; + pHdbIDMessage idm = NULL; + pHdbPtrMessage cmm = NULL; + pHdbDataMessage mm = NULL; + + cbInfo = (HdbCBInfo *)userData; + + /* + * handle kills first + */ + if((idm = GetKillIDMessage(message)) != NULL){ + if(idm->ID == cbInfo->ID){ + return hdbKill; + } else { + return hdbContinue; + } + } + if((idm = GetKillInternalIDMessage(message)) != NULL){ + if(idm->ID == cbInfo->internalID){ + return hdbKill; + } else { + return hdbContinue; + } + } + if((cmm = GetKillPtrMessage(message)) != NULL){ + if(cmm->pPtr == cbInfo->pCon){ + return hdbKill; + } else { + return hdbContinue; + } + } + + /* + * react only on update messages + */ + if((mm = GetHdbUpdateMessage(message)) == NULL){ + return hdbContinue; + } + + pPath = GetHipadabaPath(node); + result = CreateDynString(128,128); + if ((protocol = isJSON(cbInfo->pCon)) == 1) + outCode = eHdbEvent; + else + outCode = eEvent; + + + /* + * we want our notifications to come even when called from a macro + */ + macro = SCinMacro(cbInfo->pCon); + SCsetMacro(cbInfo->pCon,0); + /** + * if transfer = zip always transfer data in zipped form + */ + if(GetHdbProperty(node,"transfer",value,80) == 1){ + if(strstr(value,"zip") != NULL){ + SCPushContext2(cbInfo->pCon,cbInfo->context); + status = sendZippedNodeData(node, cbInfo->pCon); + SCPopContext(cbInfo->pCon); + SCsetMacro(cbInfo->pCon,macro); + free(pPath); + DeleteDynString(result); + return hdbContinue; + } + } + if(mm->v->arrayLength < 100){ + printedData = formatValue(*(mm->v), node); + if(pPath == NULL || printedData == NULL || result == NULL){ + SCWriteInContext(cbInfo->pCon,"ERROR: out of memory formatting data" , + eEvent,cbInfo->context); + SCsetMacro(cbInfo->pCon,macro); + /* + * no need to interrupt something because writing data to a client does + * not work + */ + return hdbContinue; + } + formatNameValue(protocol, pPath, GetCharArray(printedData), result, + mm->v->dataType); + SCWriteInContext(cbInfo->pCon,GetCharArray(result), + outCode,cbInfo->context); + DeleteDynString(printedData); + } else { + formatNameValue(protocol, pPath,"!!datachange!!", result, HIPTEXT); + SCWriteInContext(cbInfo->pCon,GetCharArray(result), + outCode,cbInfo->context); + } + SCsetMacro(cbInfo->pCon,macro); + free(pPath); + DeleteDynString(result); + + return hdbContinue; +} +/*-----------------------------------------------------------------------------------------*/ +pHdbCallback MakeNotifyCallback(SConnection *pCon, int id){ + HdbCBInfo *cbInfo = NULL; + + cbInfo = malloc(sizeof(HdbCBInfo)); + if(cbInfo == NULL){ + return NULL; + } + cbInfo->pCon = pCon; + cbInfo->context = SCGetContext(pCon); + cbInfo->ID = id; + cbInfo->internalID = -1; + return MakeHipadabaCallback(SICSNotifyCallback, cbInfo,free); +} +/*-------------------------------------------------------------------------*/ +static hdbCallbackReturn TreeChangeCallback(pHdb node, void *userData, + pHdbMessage message){ + char *path = NULL; + char buffer[1024]; + pDynString result = NULL; + Protocol protocol = normal_protocol; + int outCode; + pHdbIDMessage idm = NULL; + pHdbPtrMessage cmm = NULL; + pHdbTreeChangeMessage tm = NULL; + + HdbCBInfo *cbInfo = (HdbCBInfo *)userData; + + /* + * handle kills first + */ + if((idm = GetKillIDMessage(message)) != NULL){ + if(idm->ID == cbInfo->ID){ + return hdbKill; + } else { + return hdbContinue; + } + } + if((idm = GetKillInternalIDMessage(message)) != NULL){ + if(idm->ID == cbInfo->internalID){ + return hdbKill; + } else { + return hdbContinue; + } + } + if((cmm = GetKillPtrMessage(message)) != NULL){ + if(cmm->pPtr == cbInfo->pCon){ + return hdbKill; + } else { + return hdbContinue; + } + } + + if((tm = GetHdbTreeChangeMessage(message)) == NULL){ + return hdbContinue; + } + + if(cbInfo != NULL && cbInfo->pCon != NULL){ + result = CreateDynString(128,128); + if(result == NULL){ + SCWriteInContext(cbInfo->pCon,"ERROR: out of memory in TreeChangeCallback",outCode,cbInfo->context); + return hdbAbort; + } + path = GetHipadabaPath(node); + if ((protocol = isJSON(cbInfo->pCon)) == 1) + outCode = eHdbEvent; + else + outCode = eEvent; + formatNameValue(protocol, "treechange", path, result, node->value.dataType); + SCWriteInContext(cbInfo->pCon,GetCharArray(result),outCode,cbInfo->context); + DeleteDynString(result); + free(path); + } + return hdbContinue; +} +/*-------------------------------------------------------------------------*/ +pHdbCallback MakeTreeChangeCallback(SConnection *pCon, int id){ + HdbCBInfo *cbInfo = NULL; + + cbInfo = malloc(sizeof(HdbCBInfo)); + if(cbInfo == NULL){ + return NULL; + } + cbInfo->pCon = pCon; + cbInfo->context = SCGetContext(pCon); + cbInfo->ID = id; + return MakeHipadabaCallback(TreeChangeCallback, cbInfo,free); +} +/*----------------------------------------------------------------------------------------*/ +static hdbCallbackReturn SICSScriptWriteCallback(pHdb node, void *userData, + pHdbMessage message){ + char *command = NULL; + SConnection *pCon = NULL; + pDynString newVal = NULL; + char error[1024]; + int status; + pHdbDataMessage mm = NULL; + + if((mm = GetHdbSetMessage(message)) == NULL){ + return hdbContinue; + } + + command = (char *)userData; + pCon = (SConnection *)mm->callData; + + assert(command != NULL && pCon != NULL); + + newVal = formatValue(*(mm->v), node); + if(newVal == NULL){ + SCWrite(pCon,"ERROR: out of memory setting parameter",eError); + return hdbAbort; + } + + /** + * prepend command + */ + DynStringInsert(newVal," ", 0); + DynStringInsert(newVal,command,0); + + /* + * evaluate + */ + status = Tcl_Eval(InterpGetTcl(pServ->pSics),GetCharArray(newVal)); + if(status != TCL_OK){ + snprintf(error,1023,"ERROR: tcl returned error: %s", + Tcl_GetStringResult(InterpGetTcl(pServ->pSics))); + SCWrite(pCon,error,eError); + status = 0; + } else { + status = 1; + } + DeleteDynString(newVal); + if(status == 1){ + return hdbContinue; + } else{ + return hdbAbort; + } +} +/*---------------------------------------------------------------------------------------*/ +static pHdbCallback MakeSICSWriteScriptCallback(char *script){ + return MakeHipadabaCallback(SICSScriptWriteCallback, strdup(script),free); +} +/*----------------------------------------------------------------------------------------*/ +static hdbCallbackReturn SICSScriptReadCallback(pHdb node, void *userData, + pHdbMessage message){ + char *command = NULL, *data = NULL, *equal = NULL; + SConnection *pCon = NULL; + pDynString newVal = NULL; + char error[1024]; + int status; + pHdbDataMessage mm = NULL; + + if((mm = GetHdbGetMessage(message)) == NULL){ + return hdbContinue; + } + + command = (char *)userData; + pCon = (SConnection *)mm->callData; + + assert(command != NULL); + + /* + * evaluate + */ + if(pCon != NULL){ + MacroPush(pCon); + } + status = Tcl_Eval(InterpGetTcl(pServ->pSics),command); + if(pCon != NULL){ + MacroPop(); + } + if(status != TCL_OK){ + snprintf(error,1023,"ERROR: Tcl returned error: %s", + Tcl_GetStringResult(InterpGetTcl(pServ->pSics))); + if(pCon != NULL){ + SCWrite(pCon,error,eError); + } + status = 0; + } else { + status = 1; + } + + /* + * decode result. This handles both the case of the standard SICS answer + * something = anything + * as well as a plain value alone + */ + data = (char *)Tcl_GetStringResult(InterpGetTcl(pServ->pSics)); + if(data == NULL){ + if(pCon != NULL){ + SCWrite(pCon,"ERROR: no result returned from script",eError); + } + return hdbAbort; + } + + /* + * do we need to copy? + */ + if(strstr(data,"@@NOCOPY@@") != NULL){ + return hdbContinue; + } + + /* + * we need to copy: do it + */ + equal = strchr(data,'='); + if(equal != NULL){ + data = equal + 1; + } + strcpy(error,"ERROR: "); + status = readHdbValue(&node->value,data, error+7, 1024-7); + if(status != 1){ + if(pCon != NULL){ + SCWrite(pCon,error,eError); + } + return hdbAbort; + } + if(status == 1){ + return hdbContinue; + } else { + return hdbAbort; + } +} +/*----------------------------------------------------------------------------*/ +static pHdbCallback MakeSICSReadScriptCallback(char *script){ + return MakeHipadabaCallback(SICSScriptReadCallback, strdup(script), + free); +} +/*---------------------------------------------------------------------------*/ +typedef struct { + int min; + int max; +}hdbIntRange, *pHdbIntRange; +/*---------------------------------------------------------------------------*/ +static hdbCallbackReturn SICSIntRangeCallback(pHdb node, void *userData, + pHdbMessage message){ + char buffer[256]; + pHdbIntRange range = NULL; + SConnection *pCon = NULL; + int status = 1; + pHdbDataMessage mm = NULL; + + if((mm = GetHdbSetMessage(message)) == NULL){ + return hdbContinue; + } + + range = (pHdbIntRange)userData; + pCon = (SConnection *)mm->callData; + + assert(range != NULL); + + if(mm->v->v.intValue > range->max || mm->v->v.intValue < range->min) { + status = SICSCBRANGE; + if(pCon != NULL){ + snprintf(buffer,255,"ERROR: %d is not within permitted range: %d to %d", + (int)mm->v->v.intValue, range->min, range->max); + SCWrite(pCon,buffer,eError); + } + return hdbAbort; + } + return hdbContinue; +} +/*---------------------------------------------------------------------------*/ +pHdbCallback MakeIntRangeCallback(int min, int max){ + pHdbIntRange range = NULL; + + range = malloc(sizeof(hdbIntRange)); + if(range == NULL){ + return NULL; + } + range->min = min; + range->max = max; + return MakeHipadabaCallback(SICSIntRangeCallback, range, + free); +} +/*---------------------------------------------------------------------------*/ +typedef struct { + double min; + double max; +}hdbFloatRange, *pHdbFloatRange; +/*---------------------------------------------------------------------------*/ +static hdbCallbackReturn SICSFloatRangeCallback(pHdb node, void *userData, + pHdbMessage message){ + char buffer[256]; + pHdbFloatRange range = NULL; + SConnection *pCon = NULL; + int status = 1; + pHdbDataMessage mm = NULL; + + if((mm = GetHdbSetMessage(message)) == NULL){ + return hdbContinue; + } + + range = (pHdbFloatRange)userData; + pCon = (SConnection *)mm->callData; + + assert(range != NULL); + + if(mm->v->v.doubleValue > range->max || mm->v->v.doubleValue < range->min) { + status = SICSCBRANGE; + if(pCon != NULL){ + snprintf(buffer,255,"ERROR: %lf is not within permitted range: %lf to %lf", + mm->v->v.doubleValue, range->min, range->max); + SCWrite(pCon,buffer,eError); + } + return hdbAbort; + } + return hdbContinue; +} +/*---------------------------------------------------------------------------*/ +pHdbCallback MakeFloatRangeCallback(double min, double max){ + pHdbFloatRange range = NULL; + + range = malloc(sizeof(hdbFloatRange)); + if(range == NULL){ + return NULL; + } + range->min = min; + range->max = max; + return MakeHipadabaCallback(SICSFloatRangeCallback, range, + free); +} +/*------------------------------------------------------------------------*/ +static hdbCallbackReturn MemGenReadCallback(pHdb node, void *userData, + pHdbMessage message){ + pHdbDataMessage mm = NULL; + + if((mm = GetHdbGetMessage(message)) == NULL){ + return hdbContinue; + } + + switch(node->value.dataType){ + case HIPINT: + node->value.v.intValue = *(int *)userData; + break; + case HIPFLOAT: + node->value.v.doubleValue = *(double *)userData; + break; + case HIPTEXT: + if(node->value.v.text != NULL){ + free(node->value.v.text); + } + node->value.v.text = strdup((char *)userData); + break; + case HIPINTAR: + memcpy(&node->value.v.intArray,userData, + node->value.arrayLength *sizeof(int)); + break; + case HIPFLOATAR: + memcpy(&node->value.v.floatArray,userData, + node->value.arrayLength *sizeof(double)); + break; + default: + assert(0); + break; + } + return hdbContinue; +} +/*-------------------------------------------------------------------------*/ +pHdbCallback MakeMemGenReadCallback(void *address){ + return MakeHipadabaCallback(MemGenReadCallback, address, + NULL); +} +/*-------------------------------------------------------------------------*/ +static hdbCallbackReturn MemGenSetCallback(pHdb node, void *userData, + pHdbMessage message){ + const char *pPtr = NULL; + pHdbDataMessage mm = NULL; + + if((mm = GetHdbSetMessage(message)) == NULL){ + return hdbContinue; + } + + + if(mm->v->dataType != node->value.dataType){ + assert(0); + return 0; + } + + switch(node->value.dataType){ + case HIPINT: + memcpy(userData,&mm->v->v.intValue,sizeof(int)); + break; + case HIPFLOAT: + memcpy(userData,&mm->v->v.doubleValue,sizeof(double)); + break; + case HIPTEXT: + strncpy((char *)userData,(const char *)mm->v->v.text, + node->value.arrayLength); + break; + case HIPINTAR: + memcpy(userData,&mm->v->v.intArray,node->value.arrayLength*sizeof(int)); + break; + case HIPFLOATAR: + memcpy(userData,&mm->v->v.floatArray, + node->value.arrayLength*sizeof(double)); + break; + default: + assert(0); + return 0; + break; + } + UpdateHipadabaPar(node,*(mm->v),mm->callData); + return hdbContinue; +} +/*-------------------------------------------------------------------------*/ +pHdbCallback MakeMemGenSetCallback(void *address){ + return MakeHipadabaCallback(MemGenSetCallback, address, + NULL); +} +/*--------------------------------------------------------------------------*/ +static void killHdbValue(void *pData){ + hdbValue *v = NULL; + + v = (hdbValue *)pData; + if(v == NULL){ + return; + } + ReleaseHdbValue(v); + free(v); +} +/*--------------------------------------------------------------------------*/ +static hdbCallbackReturn SICSIntFixedCallback(pHdb node, void *userData, + pHdbMessage message){ + hdbValue *allowed = NULL; + SConnection *pCon = NULL; + int i; + pHdbDataMessage mm = NULL; + + if((mm = GetHdbSetMessage(message)) == NULL){ + return hdbContinue; + } + + allowed = (hdbValue *)userData; + pCon = (SConnection *)mm->callData; + assert(allowed != NULL && allowed->dataType == HIPINTAR); + for(i = 0; i < allowed->arrayLength; i++){ + if(mm->v->v.intValue == allowed->v.intArray[i]){ + return hdbContinue; + } + } + if(pCon != NULL){ + SCWrite(pCon,"ERROR: value is not in the list of allowed values",eError); + } + return hdbAbort; +} +/*---------------------------------------------------------------------------*/ +pHdbCallback MakeIntFixedCallback(int *data, int length){ + pHdbCallback result = NULL; + hdbValue *v = NULL; + + v = malloc(sizeof(hdbValue)); + if(v == NULL){ + return NULL; + } + v->dataType = HIPINTAR; + v->arrayLength = length; + v->v.intArray = malloc(length*sizeof(int)); + if(v->v.intArray == NULL){ + return NULL; + } + memcpy(v->v.intArray,data,length*sizeof(int)); + return MakeHipadabaCallback(SICSIntFixedCallback, v, + killHdbValue); +} +/*============= Parameter Creation ===========================================*/ +pHdb MakeSICSHdbPar(char *name, int priv, hdbValue v){ + pHdb result = NULL; + pHdbCallback pHcb = NULL; + char pPriv[20]; + + result = MakeHipadabaNode(name,v.dataType,v.arrayLength); + if(result == NULL){ + return NULL; + } + copyHdbValue(&v,&result->value); + + pHcb = MakeCheckPermissionCallback(priv); + if(pHcb == NULL){ + DeleteHipadabaNode(result,NULL); + return NULL; + } + AppendHipadabaCallback(result,pHcb); + + pHcb = MakeSetUpdateCallback(); + if(pHcb == NULL){ + DeleteHipadabaNode(result,NULL); + return NULL; + } + AppendHipadabaCallback(result,pHcb); + switch(priv){ + case 0: + strcpy(pPriv,"internal"); + break; + case 1: + strcpy(pPriv,"manager"); + break; + case 2: + strcpy(pPriv,"user"); + break; + case 3: + strcpy(pPriv,"spy"); + break; + } + SetHdbProperty(result,"priv",pPriv); + + return result; +} +/*----------------------------------------------------------------------------*/ +pHdb MakeSICSHdbDriv(char *name, int priv, void *sicsObject, int dataType){ + pHdb result = NULL; + pHdbCallback pHcb = NULL; + + result = MakeHipadabaNode(name,dataType,0); + if(result == NULL){ + return NULL; + } + + pHcb = MakeCheckPermissionCallback(priv); + if(pHcb == NULL){ + DeleteHipadabaNode(result,NULL); + return NULL; + } + AppendHipadabaCallback(result,pHcb); + + pHcb = MakeSICSDriveCallback(sicsObject); + if(pHcb == NULL){ + DeleteHipadabaNode(result,NULL); + return NULL; + } + AppendHipadabaCallback(result,pHcb); + + pHcb = MakeSICSReadDriveCallback(sicsObject); + if(pHcb == NULL){ + DeleteHipadabaNode(result,NULL); + return NULL; + } + AppendHipadabaCallback(result,pHcb); + + return result; +} +/*---------------------------------------------------------------------------*/ +pHdb MakeSICSMemPar(char *name, int priv, float *address){ + pHdb result = NULL; + pHdbCallback pHcb = NULL; + + result = MakeHipadabaNode(name,HIPFLOAT,1); + if(result == NULL){ + return NULL; + } + + pHcb = MakeCheckPermissionCallback(priv); + if(pHcb == NULL){ + DeleteHipadabaNode(result,NULL); + return NULL; + } + AppendHipadabaCallback(result,pHcb); + + pHcb = MakeMemGenSetCallback(address); + if(pHcb == NULL){ + DeleteHipadabaNode(result,NULL); + return NULL; + } + AppendHipadabaCallback(result,pHcb); + + pHcb = MakeMemGenReadCallback(address); + if(pHcb == NULL){ + DeleteHipadabaNode(result,NULL); + return NULL; + } + AppendHipadabaCallback(result,pHcb); + + return result; +} +/*----------------------------------------------------------------------------*/ +pHdb MakeSICSROPar(char *name, hdbValue v){ + pHdb result = NULL; + pHdbCallback pHcb = NULL; + + result = MakeHipadabaNode(name,v.dataType,v.arrayLength); + if(result == NULL){ + return NULL; + } + copyHdbValue(&v,&result->value); + + pHcb = MakeReadOnlyCallback(); + if(pHcb == NULL){ + DeleteHipadabaNode(result,NULL); + return NULL; + } + AppendHipadabaCallback(result,pHcb); + + return result; +} +/*---------------------------------------------------------------------------*/ +pHdb MakeSICSScriptPar(char *name, char *setScript, char *readScript, + hdbValue v){ + pHdb result = NULL; + pHdbCallback pHcb = NULL; + + result = MakeHipadabaNode(name,v.dataType,v.arrayLength); + if(result == NULL){ + return NULL; + } + copyHdbValue(&v,&result->value); + + pHcb = MakeSICSWriteScriptCallback(setScript); + if(pHcb == NULL){ + DeleteHipadabaNode(result,NULL); + return NULL; + } + AppendHipadabaCallback(result,pHcb); + + pHcb = MakeSICSReadScriptCallback(readScript); + if(pHcb == NULL){ + DeleteHipadabaNode(result,NULL); + return NULL; + } + AppendHipadabaCallback(result,pHcb); + /** + * put into the list of nodes to check with the update task + */ + /* LLDnodeAppend(scriptUpdate,&result); */ + SetHdbProperty(result,"sicscommand",setScript); + return result; +} +/*--------------------------------------------------------------------------*/ +static void removeNodeFromUpdateList(pHdb node){ + pHdb current = NULL; + int status; + char *objName = NULL; + + objName = GetHipadabaPath(node); + if(objName != NULL){ + removePollObject(poller, objName); + free(objName); + } +} +/*-----------------------------------------------------------------------*/ +static void SICSDeleteNodeData(pHdb node){ + pHdb tmp = NULL; + + if(node == NULL){ + return; + } + + removeNodeFromUpdateList(node); + while(node->child != NULL){ + tmp = node->child; + node->child = node->child->next; + SICSDeleteNodeData(tmp); + } + if(node->properties != NULL){ + DeleteStringDict(node->properties); + } + DeleteCallbackChain(node); + + if(node->name != NULL){ + free(node->name); + } + ReleaseHdbValue(&node->value); + node->magic = 000000; + + free(node); +} +/*--------------------------------------------------------------------------*/ +void RemoveSICSPar(pHdb node, void *callData){ + pHdb current = NULL, tmp = NULL; + + if(node == NULL){ + return; + } + + RemoveHdbNodeFromParent(node,NULL); + + SICSDeleteNodeData(node); +} +/*===================== add functions =======================================*/ +pHdb AddSICSHdbPar(pHdb parent, char *name, int priv, hdbValue v){ + pHdb child = NULL; + + child = MakeSICSHdbPar(name,priv,v); + if(child != NULL){ + AddHipadabaChild(parent,child,NULL); + } + return child; +} +/*---------------------------------------------------------------------------*/ +pHdb AddSICSHdbROPar(pHdb parent, char *name, hdbValue v){ + pHdb child = NULL; + + child = MakeSICSROPar(name,v); + if(child != NULL){ + AddHipadabaChild(parent,child,NULL); + } + return child; +} +/*--------------------------------------------------------------------------*/ +pHdb AddSICSHdbMemPar(pHdb parent, char *name, int priv, + void *data, int datalength, int type, int length){ + pHdb child = NULL; + pHdbCallback pHcb = NULL; + + if(type == HIPINTVARAR || type == HIPFLOATVARAR){ + assert(0); + return NULL; + } + + child = MakeHipadabaNode(name,type,length); + if(child == NULL){ + return NULL; + } + + pHcb = MakeCheckPermissionCallback(priv); + if(pHcb == NULL){ + DeleteHipadabaNode(child,NULL); + return NULL; + } + AppendHipadabaCallback(child,pHcb); + + pHcb = MakeMemGenSetCallback(data); + if(pHcb == NULL){ + DeleteHipadabaNode(child,NULL); + return NULL; + } + AppendHipadabaCallback(child,pHcb); + + pHcb = MakeMemGenReadCallback(data); + if(pHcb == NULL){ + DeleteHipadabaNode(child,NULL); + return NULL; + } + AppendHipadabaCallback(child,pHcb); + AddHipadabaChild(parent,child,NULL); + + return child; +} +/*==================== access support functions ==============================*/ +pHdb FindHdbParent(char *rootpath, char *relpath, char **namePtr, SConnection *pCon) { + /* for namePtr == NULL, implements also "find node" */ + char *element; + char buffer[MAX_HDB_PATH]; + pHdb node = NULL; + pHdb parent = NULL; + char *name; + char *slash; + pObjectDescriptor pDes; + int iret; + + if (relpath[0] == '/' || rootpath == NULL) { /* absolute path */ + iret = snprintf(buffer, sizeof buffer, "%s", relpath); + } else { + iret = snprintf(buffer, sizeof buffer, "%s/%s", rootpath, relpath); + } + if (iret < 0 || iret >= sizeof(buffer)) { + SCWrite(pCon,"ERROR: path too long",eError); + return NULL; + } + element = buffer; + if (strncmp(element, "/sics/", 6) == 0) { + /* sics object case */ + slash = strchr(element+6, '/'); + if (slash != NULL) *slash = '\0'; /* split off object name */ + + pDes = FindCommandDescriptor(pServ->pSics, element+6); + if (pDes == NULL) { + SCPrintf(pCon, eError, "ERROR: object %s not found", element); + return NULL; + } + node = pDes->parNode; + if (node == NULL) { + SCPrintf(pCon, eError, "ERROR: object %s does not use hipadaba", element); + return NULL; + } + if (slash == NULL) goto nodeFound; + *slash = '/'; + element = slash+1; + parent = node; + /* parent is sics object, path is relative to it */ + } else { + /* normal path */ + parent = GetHipadabaRoot(); + } + while (1) { + slash = strchr(element, '/'); + if (slash != NULL) *slash = '\0'; /* split off next path element */ + if (strcmp(element, "") == 0 || strcmp(element, ".") == 0) { + /* cases "//" and "/./" : do not move in tree */ + if (slash == NULL) { + node = parent; + goto nodeFound; + } + *slash = '/'; + element = slash + 1; + } else { + for (node = parent->child; node != NULL; node = node->next) { + if (strcasecmp(element, node->name) == 0) { + break; + } + } + if (node == NULL) { + if (namePtr) { /* "find parent" case */ + if (slash != NULL) { /* element is not the last in path */ + *slash = '/'; + SCPrintf(pCon, eError, "ERROR: parent of %s not found", buffer); + return NULL; + } + /* the name must be taken from the end of relpath, as element is no longer valid */ + *namePtr = relpath + (element - buffer); + return parent; /* parent found, and node does not yet exist */ + } + /* "find node" case */ + if (slash != NULL) *slash = '/'; + SCPrintf(pCon, eError, "ERROR: node %s not found", buffer); + return NULL; + } + /* node found */ + if (slash == NULL) goto nodeFound; + parent = node; + *slash = '/'; + element = slash + 1; + } + } +nodeFound: + if (namePtr) { /* "find parent" case */ + *namePtr = node->name; + SCPrintf(pCon, eError, "ERROR: node %s exists already", buffer); + return NULL; + } + return node; /* node found */ +} +/*--------------------------------------------------------------------------*/ +pHdb FindHdbNode(char *rootpath, char *relpath, SConnection *pCon) { + return FindHdbParent(rootpath, relpath, NULL, pCon); +} +/*--------------------------------------------------------------------------*/ +int GetHdbPath(pHdb nodeArg, char *path, size_t pathlen) { + pHdb node, parent; + int len, pos, l; + static char *sics="/sics"; + pObjectDescriptor pDes; + + path[0]='\0'; + if (nodeArg == NULL) { + return 0; + } + /* determine path length and root node */ + parent = nodeArg; + len = 0; + for (node = nodeArg; node != NULL && node != root; node = node->mama) { + len += strlen(node->name) + 1; + if (len >= pathlen) return 0; /* buffer overflow (recursive path?) */ + parent = node; + } + + /* check root and add prefix */ + if (parent->mama != root) { /* not anchored in root */ + pDes = FindCommandDescriptor(pServ->pSics, parent->name); + if (!pDes) { + return 0; /* not a sics object */ + } + if (pDes->parNode != parent) { + /* node named as a sics object, but command is not related to node */ + return 0; + } + l= strlen(sics); + len += l; + if (len > pathlen) return 0; /* buffer overflow */ + strncpy(path, sics, l); + } + + /* build the path backwards */ + path[len]='\0'; + pos = len; + for (node = nodeArg; node != NULL && node != root; node = node->mama) { + len = strlen(node->name); + pos -= len; + assert(pos>0); + strncpy(path+pos, node->name, len); + pos--; + path[pos]='/'; + } + return 1; +} +/*--------------------------------------------------------------------------*/ +static int RemoveParNodeCallback(char *name, pDummy object, void *internalID) { + hdbPtrMessage m; + + m.type = killPtr; + m.pPtr = internalID; + if (object->pDescriptor->parNode) { + RecurseCallbackChains(object->pDescriptor->parNode,(pHdbMessage)&m); + } + return 1; +} +/*--------------------------------------------------------------------------*/ +void RemoveSICSInternalCallback(void *internalID) { + hdbPtrMessage m; + + m.type = killPtr; + m.pPtr = internalID; + RecurseCallbackChains(GetHipadabaRoot(),(pHdbMessage)&m); + ForEachCommand(RemoveParNodeCallback, internalID); +} +/*--------------------------------------------------------------------------*/ +int SICSHdbGetPar(void *obj, SConnection *pCon, char *path, hdbValue *v){ + pHdb par = NULL; + int status; + char buffer[256]; + pDummy pDum; + + pDum = (pDummy)obj; + if(pDum == NULL || pDum->pDescriptor->parNode == NULL){ + if(pCon != NULL){ + snprintf(buffer,255,"ERROR: parameter %s not found", path); + SCWrite(pCon,buffer,eError); + } + return SICSNOPAR; + } + + par = GetHipadabaNode(pDum->pDescriptor->parNode,path); + if(par == NULL){ + if(pCon != NULL){ + snprintf(buffer,255,"ERROR: parameter %s not found", path); + SCWrite(pCon,buffer,eError); + } + return SICSNOPAR; + } + + status = GetHipadabaPar(par,v,pCon); + if(status < 0){ + return status; + } + return 1; +} +/*--------------------------------------------------------------------------*/ +int SICSHdbUpdatePar(void *obj, SConnection *pCon, char *path, hdbValue v){ + pHdb par = NULL; + int status; + char buffer[256]; + pDummy pDum; + + pDum = (pDummy)obj; + if(pDum == NULL || pDum->pDescriptor->parNode == NULL){ + if(pCon != NULL){ + snprintf(buffer,255,"ERROR: parameter %s not found", path); + SCWrite(pCon,buffer,eError); + } + return SICSNOPAR; + } + + par = GetHipadabaNode(pDum->pDescriptor->parNode,path); + if(par == NULL){ + if(pCon != NULL){ + snprintf(buffer,255,"ERROR: parameter %s not found", path); + SCWrite(pCon,buffer,eError); + } + return SICSNOPAR; + } + + status = UpdateHipadabaPar(par,v,pCon); + if(status < 0){ + return status; + } + return 1; +} +/*--------------------------------------------------------------------------*/ +int SICSHdbSetPar(void *obj, SConnection *pCon, char *path, hdbValue v){ + pHdb par = NULL; + int status; + char buffer[256]; + pDummy pDum; + + pDum = (pDummy)obj; + if(pDum == NULL || pDum->pDescriptor->parNode == NULL){ + if(pCon != NULL){ + snprintf(buffer,255,"ERROR: parameter %s not found", path); + SCWrite(pCon,buffer,eError); + } + return SICSNOPAR; + } + + par = GetHipadabaNode(pDum->pDescriptor->parNode,path); + if(par == NULL){ + if(pCon != NULL){ + snprintf(buffer,255,"ERROR: parameter %s not found", path); + SCWrite(pCon,buffer,eError); + } + return SICSNOPAR; + } + + status = SetHipadabaPar(par,v,pCon); + if(status <= 0){ + return status; + } + return 1; +} +/*---------------------------------------------------------------------------*/ +int InstallSICSNotify(pHdb node, SConnection *pCon, int id, int recurse){ + pHdb currentChild = NULL; + pHdbCallback noty = NULL; + pHdbCallback treeChange = NULL; + + treeChange = MakeTreeChangeCallback(pCon,id); + noty = MakeNotifyCallback(pCon,id); + if(noty == NULL || treeChange == NULL){ + SCWrite(pCon,"ERROR: out of memory installing callback", eError); + return 0; + } + AppendHipadabaCallback(node, noty); + AppendHipadabaCallback(node, treeChange); + + if(recurse == 1){ + currentChild = node->child; + while(currentChild != NULL){ + InstallSICSNotify(currentChild,pCon,id,recurse); + currentChild = currentChild->next; + } + } + return 1; +} +/*---------------------------------------------------------------------------*/ +void RemoveConnectionCallbacks(pHdb root, SConnection *pCon){ + hdbPtrMessage dsm; + + dsm.type = killPtr; + dsm.pPtr = pCon; + RecurseCallbackChains(root,(pHdbMessage)&dsm); +} +/*---------------------------------------------------------------------------*/ +int ProcessSICSHdbPar(pHdb root, SConnection *pCon, + char *printPrefix, int argc, char *argv[]){ + hdbValue input; + pHdb parNode = NULL; + pDynString parData = NULL; + char error[512]; + int i, status; + + assert(root != NULL && pCon != NULL); + + if(argc < 1){ + SCWrite(pCon,"ERROR: no parameter to treat specified",eError); + return -1; + } + + parNode = GetHipadabaNode(root,argv[0]); + if(parNode == NULL){ + /* no error reporting here, upper level code might wish to continue + * processing commands after having tested for parameters. + */ + return -1; + } + + if(argc > 1) { + /* + * setting the value is attempted. + */ + memset(&input,0,sizeof(hdbValue)); + input.dataType = parNode->value.dataType; + copyHdbValue(&parNode->value,&input); + parData = CreateDynString(64,64); + if(parData == NULL){ + SCWrite(pCon,"ERROR: out of memory processing parameter",eError); + return 0; + } + for(i = 1; i < argc; i++){ + DynStringConcat(parData," "); + DynStringConcat(parData, argv[i]); + } + strcpy(error,"ERROR: "); + if(!readHdbValue(&input, GetCharArray(parData), + error+7,512-7)){ + SCWrite(pCon,error, eError); + return 0; + } + DeleteDynString(parData); + status = SetHipadabaPar(parNode,input,pCon); + ReleaseHdbValue(&input); + if(status == 1){ + /* SCSendOK(pCon); do not send OK. this has to be done by the callback */ + SCparChange(pCon); + } + return status; + } else { + /* + * reading is in demand + */ + status = GetHipadabaPar(parNode,&input, pCon); + if(status != 1){ + return 0; + } + parData = formatValue(input, parNode); + if(parData == NULL){ + SCWrite(pCon,"ERROR: out of memory reading parameter data",eError); + return 0; + } + DynStringInsert(parData," = ", 0); + DynStringInsert(parData,argv[0],0); + if(printPrefix != NULL){ + DynStringInsert(parData,printPrefix,0); + } + SCWrite(pCon,GetCharArray(parData),eValue); + DeleteDynString(parData); + ReleaseHdbValue(&input); + return 1; + } +} +/*--------------------------------------------------------------------------*/ +void PrintSICSParList(pHdb node, SConnection *pCon, char *prefix){ + char childPrefix[1024]; + pHdb child = NULL; + pDynString value = NULL; + hdbValue v; + + child = node->child; + while(child != NULL){ + if(child->value.dataType != HIPNONE){ + GetHipadabaPar(child,&v,pCon); + value = formatValue(child->value, child); + if(value != NULL){ + SCPrintf(pCon,eValue,"%s%s = %s", prefix, child->name, + GetCharArray(value)); + DeleteDynString(value); + } + } + if(child->child != NULL){ + strncpy(childPrefix,prefix,1024); + strncat(childPrefix,child->name, 1024); + strncat(childPrefix,"/",1024); + PrintSICSParList(child, pCon,prefix); + } + child = child->next; + } +} +/*---------------------------------------------------------------------------*/ +void SaveSICSHipadaba(FILE *fd, pHdb node, char *prefix){ + pHdb currentChild = NULL; + pDynString data = NULL; + hdbValue v; + + currentChild = node->child; + while(currentChild != NULL){ + if(currentChild->value.dataType != HIPNONE && !isSICSHdbRO(currentChild)){ + data = formatValue(currentChild->value, currentChild); + if(data != NULL){ + fprintf(fd,"%s%s %s\n", prefix, currentChild->name, GetCharArray(data)); + DeleteDynString(data); + } + } + if(currentChild->child != NULL){ + /* + * build a new prefix string and recurse + */ + data = CreateDynString(64,64); + if(data != NULL){ + DynStringCopy(data,prefix); + DynStringConcat(data,currentChild->name); + DynStringConcat(data,"/"); + SaveSICSHipadaba(fd,currentChild,GetCharArray(data)); + DeleteDynString(data); + } + } + currentChild = currentChild->next; + } +} +/*================ value helpers ============================================*/ +pDynString formatValue(hdbValue v, pHdb node){ + pDynString result = NULL; + int i; + char number[30]; + char format[16]; + + result = CreateDynString(64,64); + if(result == NULL){ + return NULL; + } + switch(v.dataType){ + case HIPNONE: + break; + case HIPINT: + snprintf(number,30,"%d", v.v.intValue); + DynStringCopy(result,number); + break; + case HIPFLOAT: + if (GetHdbProperty(node, "fmt", format, sizeof format -1)) { + snprintf(number,30,format, v.v.doubleValue); + } else { + snprintf(number,30,"%.6g", v.v.doubleValue); + } + DynStringCopy(result,number); + break; + case HIPTEXT: + DynStringCopy(result,v.v.text); + break; + case HIPINTAR: + case HIPINTVARAR: + for(i = 0; i < v.arrayLength; i++){ + snprintf(number,30," %d", v.v.intArray[i]); + DynStringConcat(result,number); + } + break; + case HIPFLOATAR: + case HIPFLOATVARAR: + if (GetHdbProperty(node, "fmt", format+1, sizeof format -2)) { + format[0]=' '; + } else { + strcpy(format, " %.6g"); + } + for(i = 0; i < v.arrayLength; i++){ + snprintf(number,30,format, v.v.floatArray[i]); + DynStringConcat(result,number); + } + break; + } + return result; +} +/*-------------------------------------------------------------------*/ +static char *getNextHdbNumber(char *pStart, char pNumber[80]){ + int charCount = 0; + pNumber[0] = '\0'; + + /* advance to first digit */ + while(isspace(*pStart) && *pStart != '\0'){ + pStart++; + } + if(*pStart == '\0'){ + return NULL; + } + + /* copy */ + while(!isspace(*pStart) && *pStart != '\0' && charCount < 78){ + pNumber[charCount] = *pStart; + pStart++; + charCount++; + } + pNumber[charCount] = '\0'; + return pStart; +} +/*---------------------------------------------------------------------------------*/ +static int adjustDataLength(hdbValue *v, char *data){ + char number[80]; + int count = 0; + char *pPtr = NULL; + + pPtr = data; + while((pPtr = getNextHdbNumber(pPtr,number)) != NULL){ + count++; + } + if(count != v->arrayLength){ + v->arrayLength = count; + if(v->dataType == HIPINTVARAR){ + if(v->v.intArray != NULL){ + free(v->v.intArray); + } + v->v.intArray = malloc(count*sizeof(int)); + if(v->v.intArray == NULL){ + return 0; + } + memset(v->v.intArray,0,count*sizeof(int)); + } + if(v->dataType == HIPFLOATVARAR){ + if(v->v.floatArray != NULL){ + free(v->v.floatArray); + } + v->v.floatArray = malloc(count*sizeof(double)); + if(v->v.floatArray == NULL){ + return 0; + } + memset(v->v.floatArray,0,count*sizeof(double)); + } + } + return 1; +} +/*---------------------------------------------------------------------------------*/ +int readHdbValue(hdbValue *v, char *data, char *error, int errlen){ + int i, status; + int lValue; + double dValue; + char number[80]; + char *pPtr = NULL; + void *objData; + + switch(v->dataType){ + case HIPNONE: + break; + case HIPINT: + getNextHdbNumber(data,number); + status = sscanf(number,"%d", &v->v.intValue); + if(status != 1){ + snprintf(error,errlen,"Failed to convert %s to integer", + data); + return 0; + } + break; + case HIPFLOAT: + getNextHdbNumber(data,number); + status = sscanf(number,"%lf", &v->v.doubleValue); + if(status != 1){ + snprintf(error,errlen,"Failed to convert %s to double", + data); + return 0; + } + break; + case HIPTEXT: + if(v->v.text != NULL){ + free(v->v.text); + } + v->v.text = strdup(data); + break; + case HIPINTVARAR: + if(!adjustDataLength(v,data)){ + snprintf(error,errlen,"Out of memory allocating variable length data"); + return 0; + } + case HIPINTAR: + for(i = 0; i < v->arrayLength; i++){ + data = getNextHdbNumber(data,number); + if(data == NULL){ + snprintf(error,errlen,"Not enough values to parse, current index %d", + i); + return 0; + } + status = sscanf(number,"%d", &lValue); + if(status != 1){ + snprintf(error,errlen,"Failed to convert %s to integer", + data); + return 0; + } + v->v.intArray[i] = lValue; + } + break; + case HIPFLOATVARAR: + if(!adjustDataLength(v,data)){ + snprintf(error,errlen,"Out of memory allocating variable length data"); + return 0; + } + case HIPFLOATAR: + for(i = 0; i < v->arrayLength; i++){ + data = getNextHdbNumber(data,number); + if(data == NULL){ + snprintf(error,errlen,"Not enough values to parse, current index %d", + i); + return 0; + } + status = sscanf(number,"%lf", &dValue); + if(status != 1){ + snprintf(error,errlen,"Failed to convert %s to double", + data); + return 0; + } + v->v.floatArray[i] = dValue; + } + break; + case HIPOBJ: + break; + case HIPFUNC: + break; + default: + assert(0); + break; + } + return 1; +} +/*-------------------------------------------------------------------------*/ +hdbValue MakeSICSFunc(SICSOBJFunc func) { + return MakeHdbFunc((voidFunc *)func); +} +/*================ interpreter interface ==================================*/ +pHdb GetHipadabaRoot(){ + return root; +} +/*-------------------------------------------------------------------------*/ +static char *hdbTypes[] = {"none", + "int", + "float", + "text", + "intar", + "floatar", + "intvarar", + "floatvarar", + "object", + "func", + NULL}; +/*-------------------------------------------------------------------------*/ +int convertHdbType(char *text){ + int type; + + type = 0; + while(hdbTypes[type] != NULL){ + if(strcmp(hdbTypes[type], text) == 0){ + break; + } + type++; + } + type--; /* we start counting at -1 */ + return type; +} +/*-------------------------------------------------------------------------*/ +static char *hdbTypeToText(int type){ + return hdbTypes[type+1]; +} +/*--------------------------------------------------------------------------*/ +static int MakeHdbNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + int type = 0, i, length = 0, priv = -1; + char *name = NULL; + pHdb parent = NULL; + pHdb child = NULL; + char buffer[512], buffer2[512]; + hdbValue val; + + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + + if(argc < 4) { + SCWrite(pCon,"ERROR: not enough arguments to MakeHdbNode",eError); + return 0; + } + + /* + * convert privilege + */ + priv = decodeSICSPriv(argv[2]); + /* + * convert datatype + */ + strtolower(argv[3]); + type = convertHdbType(argv[3]); + if(type > HIPFLOATVARAR){ + SCWrite(pCon, + "ERROR: invalid type requested: none, int, float, text, intar, floatar, intvarar, floatvarar supported", + eError); + return 0; + } + if(type >= HIPINTAR){ + if( argc < 5){ + SCWrite(pCon,"ERROR: array length missing for array data type", + eError); + return 0; + } else { + length = atoi(argv[3]); + } + } + + parent = FindHdbParent(NULL, argv[1], &name, pCon); + if (parent == NULL) { + return 0; /* error messages written inside FindHdbParent */ + } + if(type != HIPNONE){ + val = makeHdbValue(type,length); + child = MakeSICSHdbPar(name, priv, val); + ReleaseHdbValue(&val); + } else { + child = MakeHipadabaNode(name,type,length); + } + if(child == NULL){ + SCWrite(pCon,"ERROR: out of memory creating node",eError); + return 0; + } + + AddHipadabaChild(parent,child,pCon); + SCSendOK(pCon); + return 1; +} +/*--------------------------------------------------------------------------*/ +static int MakeHdbScriptNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + int type = 0, i, length = 0; + char *name = NULL; + pHdb parent = NULL; + pHdb child = NULL; + pHdb current = NULL; + char *urgv[] = {"5", NULL}; + char driver[] = {"hdb"}; + char buffer[512], buffer2[512]; + + + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + + if(argc < 5) { + SCWrite(pCon,"ERROR: not enough arguments to MakeHdbNode",eError); + return 0; + } + + /* + * convert datatype + */ + strtolower(argv[4]); + type = convertHdbType(argv[4]); + if(type >= 7){ + SCWrite(pCon, + "ERROR: invalid type requested: none, int, float, text, intar, floatar, intvarar, floatvarar supported", + eError); + return 0; + } + if(type > 2){ + if( argc < 6){ + SCWrite(pCon,"ERROR: array length missing for array data type", + eError); + return 0; + } else { + length = atoi(argv[5]); + } + } + + parent = FindHdbParent(NULL, argv[1], &name, pCon); + if (parent == NULL) { + return 0; /* error messages written inside FindHdbParent */ + } + child = MakeSICSScriptPar(name, argv[3], argv[2], + makeHdbValue(type,length)); + if(child == NULL){ + SCWrite(pCon,"ERROR: out of memory creating node",eError); + return 0; + } + + AddHipadabaChild(parent,child,pCon); + /* + * have it polled automatically + */ + addPollObject(poller,pCon, GetHipadabaPath(child),driver,1,urgv); + + SCSendOK(pCon); + return 1; +} +/*------------------------------------------------------------------------------*/ +static int isNodeProtected(pHdb node){ + pHdb current = NULL; + + if(node->protected == 1){ + return 1; + } + current = node->child; + while(current != NULL){ + if(isNodeProtected(current)){ + return 1; + } + current = current->next; + } + + return 0; +} +/*-----------------------------------------------------------------------------------------*/ +static int DeleteHdbNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb killNode = NULL; + + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + + if(argc < 2){ + SCWrite(pCon,"ERROR: need path to node to delete",eError); + return 0; + } + killNode = FindHdbNode(NULL, argv[1], pCon); + if(killNode == NULL){ + SCWrite(pCon,"ERROR: node to delete not found",eError); + return 0; + } + if(isNodeProtected(killNode)){ + SCWrite(pCon,"ERROR: this node or one of its children is protected", + eError); + return 0; + } + + RemoveSICSPar(killNode, pCon); + SCSendOK(pCon); + return 1; +} +/*---------------------------------------------------------------------------*/ +static int SetHdbNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb targetNode = NULL; + hdbValue newValue; + pDynString parData = NULL; + char error[512]; + int i, status; + + if(!SCMatchRights(pCon,usUser)){ + return 0; + } + + if(argc < 3) { + SCWrite(pCon,"ERROR: insufficient number of arguments to SetHdbNode", + eError); + return 0; + } + + + targetNode = FindHdbNode(NULL,argv[1],pCon); + if(targetNode == NULL){ + return 0; + } + if(!cloneHdbValue(&targetNode->value,&newValue)){ + SCWrite(pCon,"ERROR: out of memory cloning node", + eError); + return 0; + } + parData = CreateDynString(64,64); + if(parData == NULL){ + SCWrite(pCon,"ERROR: out of memory reading parameter",eError); + return 0; + } + DynStringConcat(parData, argv[2]); + for(i = 3; i < argc; i++){ + DynStringConcat(parData," "); + DynStringConcat(parData, argv[i]); + } + strcpy(error,"ERROR: "); + if(!readHdbValue(&newValue, GetCharArray(parData), + error+7,512-7)){ + SCWrite(pCon,error, eError); + return 0; + } + DeleteDynString(parData); + status = SetHipadabaPar(targetNode,newValue,pCon); + ReleaseHdbValue(&newValue); + if(status == 1){ + /* SCSendOK(pCon); sending ok has to be done by the callback. */ + } + return status; +} +/*---------------------------------------------------------------------------*/ +static int UpdateHdbNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb targetNode = NULL; + hdbValue newValue; + pDynString parData = NULL; + char error[512]; + int i, status; + + /* + if(!SCMatchRights(pCon,usUser)){ + return 0; + } + */ + + if(argc < 2) { + SCWrite(pCon,"ERROR: insufficient number of arguments to UpdateHdbNode", + eError); + return 0; + } + + targetNode = FindHdbNode(NULL,argv[1],pCon); + if(targetNode == NULL){ + return 0; + } + if(argc > 2){ + if(!cloneHdbValue(&targetNode->value,&newValue)){ + SCWrite(pCon,"ERROR: out of memory cloning node", + eError); + return 0; + } + parData = CreateDynString(64,64); + if(parData == NULL){ + SCWrite(pCon,"ERROR: out of memory reading parameter",eError); + return 0; + } + for(i = 2; i < argc; i++){ + DynStringConcat(parData," "); + DynStringConcat(parData, argv[i]); + } + strcpy(error,"ERROR: "); + if(!readHdbValue(&newValue, GetCharArray(parData), + error+7,512-7)){ + SCWrite(pCon,error, eError); + return 0; + } + DeleteDynString(parData); + } else { + memset(&newValue,0,sizeof(hdbValue)); + GetHipadabaPar(targetNode,&newValue,pCon); + } + status = UpdateHipadabaPar(targetNode,newValue,pCon); + ReleaseHdbValue(&newValue); + if(status == 1){ + SCSendOK(pCon); + } + return status; +} +/*-----------------------------------------------------------------------------*/ +static int ZipGetHdbNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb targetNode = NULL; + char error[512], oriPath[512]; + int status; + hdbValue newValue; + + if(argc < 2) { + SCWrite(pCon,"ERROR: need path to node",eError); + return 0; + } + + strncpy(oriPath,argv[1], 511); + targetNode = FindHdbNode(NULL,argv[1],pCon); + if(targetNode == NULL){ + return 0; + } + memset(&newValue,0,sizeof(hdbValue)); + GetHipadabaPar(targetNode, &newValue, pCon); + ReleaseHdbValue(&newValue); + return sendZippedNodeData(targetNode,pCon); +} +/*---------------------------------------------------------------------------*/ +static int GetHdbNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb targetNode = NULL; + hdbValue newValue; + pDynString parData = NULL, result = NULL; + char error[512], oriPath[512];; + int i, status; + Protocol protocol = normal_protocol; + int outCode; + char value[80]; + + /* + if(strstr(argv[1],"values") != NULL){ + printf("Found!!\n"); + } + */ + + if(argc < 2) { + SCWrite(pCon,"ERROR: need path to node to print",eError); + return 0; + } + + strncpy(oriPath,argv[1], 511); + targetNode = FindHdbNode(NULL,argv[1],pCon); + if(targetNode == NULL){ + return 0; + } + /* + * if transfer = zip, redirect to zip + */ + if(GetHdbProperty(targetNode,"transfer", value,80) == 1){ + if(strstr(value,"zip") != NULL){ + return ZipGetHdbNode(pCon,pSics,pData,argc,argv); + } + } + memset(&newValue,0,sizeof(hdbValue)); + GetHipadabaPar(targetNode, &newValue, pCon); + parData = formatValue(newValue, targetNode); + if(parData == NULL){ + SCWrite(pCon,"ERROR: out of memory formatting data",eError); + return 0; + } + if ((protocol = isJSON(pCon)) == 1) + outCode = eHdbEvent; + else + outCode = eEvent; + + result = CreateDynString(128,128); + formatNameValue(protocol, oriPath, GetCharArray(parData), result, newValue.dataType); + SCWrite(pCon,GetCharArray(result),outCode); + DeleteDynString(parData); + DeleteDynString(result); + ReleaseHdbValue(&newValue); + + return 1; +} +/*---------------------------------------------------------------------------*/ +static int GetHdbVal(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb targetNode = NULL; + hdbValue newValue; + pDynString parData = NULL; + char error[512], oriPath[512]; + int i, status, protocol, outCode; + char value[80]; + + if(argc < 2) { + SCWrite(pCon,"ERROR: need path to node to print",eError); + return 0; + } + + strncpy(oriPath,argv[1], 511); + targetNode = FindHdbNode(NULL,argv[1],pCon); + if(targetNode == NULL){ + return 0; + } + memset(&newValue,0,sizeof(hdbValue)); + GetHipadabaPar(targetNode, &newValue, pCon); + parData = formatValue(newValue, targetNode); + if(parData == NULL){ + SCWrite(pCon,"ERROR: out of memory formatting data",eError); + return 0; + } else { + if ((protocol = isJSON(pCon)) == 1) + outCode = eHdbEvent; + else + outCode = eEvent; + SCWrite(pCon,GetCharArray(parData), outCode); + DeleteDynString(parData); + ReleaseHdbValue(&newValue); + return 1; + } + return 1; +} +/*--------------------------------------------------------------------------*/ +static int countChildren(pHdb node){ + pHdb current = NULL; + int count = 0; + + current = node->child; + while(current != NULL){ + count++; + current = current->next; + } + return count; +} +/*---------------------------------------------------------------------------*/ +static int HdbNodeInfo(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb targetNode = NULL; + char error[512], oriPath[512], info[512]; + int i, status, length; + + if(argc < 2) { + SCWrite(pCon,"ERROR: need path to node to get info",eError); + return 0; + } + + strncpy(oriPath,argv[1], 511); + targetNode = FindHdbNode(NULL,argv[1],pCon); + if(targetNode == NULL){ + return 0; + } + length = targetNode->value.arrayLength; + if(length == 0){ + length = 1; + } + snprintf(info,511,"%s,%d,%d",hdbTypeToText(targetNode->value.dataType), + countChildren(targetNode), length); + SCWrite(pCon,info,eValue); + + return 1; +} +/*---------------------------------------------------------------------------*/ +static int HdbNodeVal(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb targetNode = NULL; + hdbValue newValue; + pDynString parData = NULL; + char error[512]; + int i, status; + + if(argc < 2) { + SCWrite(pCon,"ERROR: need path to node to print",eError); + return 0; + } + + targetNode = FindHdbNode(NULL,argv[1],pCon); + if(targetNode == NULL){ + return 0; + } + memset(&newValue,0,sizeof(hdbValue)); + GetHipadabaPar(targetNode, &newValue, pCon); + parData = formatValue(newValue, targetNode); + if(parData == NULL){ + SCWrite(pCon,"ERROR: out of memory formatting data",eError); + return 0; + } + SCWrite(pCon,GetCharArray(parData),eHdbValue); + DeleteDynString(parData); + ReleaseHdbValue(&newValue); + + return 1; +} + +/*---------------------------------------------------------------------------*/ +int isSICSHdbRO(pHdb node){ + pHdbCallback current = NULL; + + current = node->callBackChain; + while(current != NULL){ + if(current->userCallback == SICSReadOnlyCallback) { + return 1; + } + current = current->next; + } + return 0; +} +/*---------------------------------------------------------------------------*/ +static pDynString formatPlainList(pHdb node){ + pHdb current; + pDynString result = NULL; + + result = CreateDynString(128,128); + if(result == NULL){ + return NULL; + } + + current = node->child; + while(current != NULL){ + DynStringConcat(result,current->name); + DynStringConcat(result,"\n"); + current = current->next; + } + return result; +} +/*---------------------------------------------------------------------------*/ +static pDynString formatJSONList(pHdb node){ + pHdb current; + pDynString result = NULL; + pDynString data = NULL; + + if (node->child == NULL) return NULL; + result = CreateDynString(128,128); + if(result == NULL){ + return NULL; + } + + if(node->child->value.dataType == HIPNONE) + DynStringCopy(result,"["); + else + DynStringCopy(result,"{"); + + current = node->child; + while(current != NULL){ + DynStringConcat(result,"\""); + DynStringConcat(result,current->name); + DynStringConcat(result,"\""); + if(current->value.dataType != HIPNONE){ + data = formatValue(current->value, current); + if(data != NULL){ + DynStringConcat(result,": "); + DynStringConcat(result,GetCharArray(data)); + DeleteDynString(data); + } + } + if (current->next != NULL) DynStringConcat(result,", "); + current = current->next; + } + + if(node->child->value.dataType == HIPNONE) + DynStringConcat(result,"]"); + else + DynStringConcat(result,"}"); + + return result; +} +/*---------------------------------------------------------------------------*/ +static pDynString formatListWithVal(pHdb node){ + pHdb current; + pDynString result = NULL; + pDynString data = NULL; + + result = CreateDynString(128,128); + if(result == NULL){ + return NULL; + } + + current = node->child; + while(current != NULL){ + if(current->value.dataType != HIPNONE){ + DynStringConcat(result,current->name); + data = formatValue(current->value, current); + if(data != NULL){ + DynStringConcat(result," = "); + DynStringConcat(result,GetCharArray(data)); + DeleteDynString(data); + } + DynStringConcat(result,"\n"); + } + current = current->next; + } + return result; +} +/*---------------------------------------------------------------------------*/ +static pDynString formatClientList(pHdb node){ + pHdb current; + pDynString result = NULL; + int length; + int i; + char number[50]; + + result = CreateDynString(128,128); + if(result == NULL){ + return NULL; + } + + current = node->child; + while(current != NULL){ + DynStringConcat(result,current->name); + DynStringConcat(result,","); + DynStringConcat(result,hdbTypeToText(current->value.dataType)); + DynStringConcat(result,","); + snprintf(number,50,"%d",countChildren(current)); + DynStringConcat(result,number); + DynStringConcat(result,","); + if(current->value.dataType >= 3){ + length = current->value.arrayLength; + } else { + length = 1; + } + snprintf(number,50,"%d",length); + DynStringConcat(result,number); + DynStringConcat(result,","); + switch(current->value.dataType){ + case HIPNONE: + break; + case HIPINT: + snprintf(number,50,"%d",current->value.v.intValue); + DynStringConcat(result,number); + break; + case HIPFLOAT: + snprintf(number,50,"%lg",current->value.v.doubleValue); + DynStringConcat(result,number); + break; + case HIPTEXT: + DynStringConcat(result,current->value.v.text); + break; + case HIPINTAR: + case HIPINTVARAR: + for(i = 0; i < length; i++){ + snprintf(number,50,"%d",current->value.v.intArray[i]); + DynStringConcat(result,number); + if(i > length -1){ + DynStringConcat(result,","); + } + } + break; + case HIPFLOATAR: + case HIPFLOATVARAR: + for(i = 0; i < length; i++){ + snprintf(number,50,"%lf",current->value.v.floatArray[i]); + DynStringConcat(result,number); + if(i > length -1){ + DynStringConcat(result,","); + } + } + break; + } + DynStringConcat(result,"\n"); + current = current->next; + } + return result; +} +/*---------------------------------------------------------------------------*/ +static int ListHdbNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb node = NULL; + int pathArg = 1; + pDynString listData = NULL; + Protocol protocol = normal_protocol; + int outCode; + + if(argc < 2) { + SCWrite(pCon,"ERROR: need path to node to print",eError); + return 0; + } + + if(argv[1][0] == '-'){ + pathArg = 2; + if(argc < 3){ + SCWrite(pCon,"ERROR: need path to node to print",eError); + return 0; + } + } + + node = FindHdbNode(NULL,argv[pathArg],pCon); + if(node == NULL){ + return 0; + } + + if(pathArg == 2) { + strtolower(argv[1]); + } + if(strcmp(argv[1],"-val") == 0){ + listData = formatListWithVal(node); + } else if(strcmp(argv[1],"-cli") == 0){ + listData = formatClientList(node); + } else { + if ((protocol = isJSON(pCon)) == 1) { + listData = formatJSONList(node); + outCode = eHdbEvent; + } else { + listData = formatPlainList(node); + outCode = eEvent; + } + } + if(listData == NULL){ + SCWrite(pCon,"ERROR: failed to format list", + eError); + return 0; + } + if( (strcmp(argv[1],"-val") == 0) || (strcmp(argv[1],"-cli") == 0) ){ + SCWrite(pCon,GetCharArray(listData),eValue); + } else { + SCWrite(pCon,GetCharArray(listData),outCode); + } + DeleteDynString(listData); + return 1; +} +/*---------------------------------------------------------------------------*/ +static int AutoNotifyHdbNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb node = NULL; + int id, status; + + if(argc < 3) { + SCWrite(pCon,"ERROR: need path and id in order to add notify", + eError); + return 0; + } + + node = FindHdbNode(NULL,argv[1],pCon); + if(node == NULL){ + return 0; + } + + id = atoi(argv[2]); + + status = InstallSICSNotify(node, pCon, id, 1); + if(status == 1){ + SCSendOK(pCon); + } + + return status; +} +/*---------------------------------------------------------------------------*/ +static int RemoveHdbCallback(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + int id; + hdbIDMessage m; + + if(argc < 2) { + SCWrite(pCon,"ERROR: need callback id to remove", + eError); + return 0; + } + id = atoi(argv[1]); + m.type = killID; + m.ID = id; + RecurseCallbackChains(root,(pHdbMessage)&m); + SCSendOK(pCon); + return 1; +} +/*---------------------------------------------------------------------------*/ +static int LinkHdbNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb node = NULL; + char buffer[256]; + pObjectDescriptor pDes = NULL; + + if(argc < 3) { + SCWrite(pCon,"ERROR: need path and object name to link", + eError); + return 0; + } + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + + node = GetHipadabaNode(root,argv[1]); + if(node == NULL){ + snprintf(buffer,255,"ERROR: path %s NOT found!", argv[1]); + SCWrite(pCon,buffer,eError); + return 0; + } + + pDes = FindCommandDescriptor(pSics,argv[2]); + if(pDes == NULL){ + snprintf(buffer,255,"ERROR: failed to find object %s", argv[2]); + SCWrite(pCon,buffer,eError); + return 0; + } + if(pDes->parNode == NULL){ + snprintf(buffer,255, + "ERROR: Object %s does not use Hipadaba natively and thus cannot be linked", + argv[2]); + SCWrite(pCon,buffer,eError); + return 0; + } + + if(pDes->parNode->mama != NULL){ + snprintf(buffer,255, + "ERROR: Object %s is already linked somewhere else", + argv[2]); + SCWrite(pCon,buffer,eError); + return 0; + } + + AddHipadabaChild(node,pDes->parNode,pCon); + + if(argc > 3){ + if(pDes->parNode->name != NULL){ + free(pDes->parNode->name); + } + pDes->parNode->name = strdup(argv[3]); + } + + SCSendOK(pCon); + return 1; +} +/*-------------------------------------------------------------------------*/ +static hdbCallbackReturn ChainCallback(pHdb node, void *userData, + pHdbMessage message){ + pHdb slave = (pHdb)userData; + hdbValue vv, old; + pHdbDataMessage mm = NULL; + + if((mm = GetHdbUpdateMessage(message)) == NULL){ + return hdbContinue; + } + + if(slave != NULL){ + memset(&vv,0,sizeof(hdbValue)); + GetHipadabaPar(slave,&vv,mm->callData); + UpdateHipadabaPar(slave, vv, mm->callData); + ReleaseHdbValue(&vv); + } + return hdbContinue; +} +/*--------------------------------------------------------------------------*/ +static int ChainHdbNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb master = NULL, slave = NULL; + char buffer[512]; + pHdbCallback kalle = NULL; + + if(argc < 3) { + SCWrite(pCon,"ERROR: insufficent number of arguments to hchain", + eError); + } + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + + slave = GetHipadabaNode(root,argv[1]); + if(slave == NULL){ + snprintf(buffer,511,"ERROR: slave %s not found",argv[1]); + SCWrite(pCon,buffer,eError); + return 0; + } + + master = GetHipadabaNode(root,argv[2]); + if(master == NULL){ + snprintf(buffer,511,"ERROR: master %s not found",argv[1]); + SCWrite(pCon,buffer,eError); + return 0; + } + + kalle = MakeHipadabaCallback(ChainCallback,slave, NULL); + if(kalle == NULL){ + SCWrite(pCon,"ERROR: out of memory creating callback",eError); + return 0; + } + AppendHipadabaCallback(master,kalle); + SCSendOK(pCon); + return 1; +} +/* -------------------------------------------------------------------------- + * This is actually SCInvoke but without advancing the context. I think this + * is only of local use. It makes sure that commands executed as Hipadaba + * commands get logged properly. + */ + static int HDBInvoke(SConnection *self, SicsInterp *pInter, char *pCommand) + { + int iRet; + long lLen; + const char *pResult = NULL; + char *pBuffer = NULL, *pFile = NULL; + char pBueffel[80]; + int i, iSpace; + + assert(pInter); + + /* print command to log files */ + for( i = 0; i < self->iFiles; i++) + { + if(self->pFiles[i]) + { + fprintf(self->pFiles[i],"SICS>> %s\n",pCommand); + } + } + + /* print to command log if user or manager */ + if(SCGetRights(self) <= usUser) + { + if(self->pSock != NULL) + { + sprintf(pBueffel,"sock %d>>",self->pSock->sockid); + } + } + + /* invoke */ + self->inUse++; + self->eInterrupt = eContinue; + /* + get first word of command + */ + iRet = InterpExecute(pInter,self,pCommand); + + StatusFileTask(NULL); /* save changed parameters */ + + self->inUse--; + return iRet; + } +/*---------------------------------------------------------------------------*/ +static hdbCallbackReturn CommandSetCallback(pHdb node, void *userData, + pHdbMessage message){ + SConnection *pCon = NULL; + pDynString cmd = NULL, par = NULL; + pHdb current = NULL; + int status; + pHdbDataMessage mm = NULL; + hdbValue v; + + if((mm = GetHdbSetMessage(message)) == NULL){ + return hdbContinue; + } + pCon = (SConnection *)pCon; + v = *(mm->v); + + if(pCon == NULL){ + printf("Cannot invoke command without connection\n"); + return hdbAbort; + } + + if(v.dataType == HIPTEXT){ + if(strstr(v.v.text,"start") != NULL) { + cmd = CreateDynString(64,64); + if(cmd == 0){ + SCWrite(pCon,"ERROR: out of memory in CommandSetCallback",eError); + return 0; + } + DynStringCopy(cmd, node->value.v.text); + DynStringConcat(cmd," "); + current = node->child; + while(current != NULL){ + par = formatValue(current->value, current); + if(par != NULL){ + DynStringConcat(cmd, GetCharArray(par)); + DynStringConcat(cmd," "); + DeleteDynString(par); + } + current = current->next; + } + status = HDBInvoke(pCon,pServ->pSics, GetCharArray(cmd)); + DeleteDynString(cmd); + if(status == 1){ + return hdbContinue; + } else { + return hdbAbort; + } + } else { + SCWrite(pCon,"ERROR: this node only understands start as value",eError); + return hdbAbort; + } + } + return hdbContinue; +} +/*---------------------------------------------------------------------------*/ +static hdbCallbackReturn CommandGetCallback(pHdb node, void *userData, + pHdbMessage message){ + pHdbDataMessage mm = NULL; + + if((mm = GetHdbGetMessage(message)) == NULL){ + return hdbContinue; + } + + hdbValue v2 = MakeHdbText("Nothing to get"); + copyHdbValue(&v2, mm->v); /* MakeHdbText makes no strdup ! */ + return hdbContinue; +} +/*--------------------------------------------------------------------------*/ +static int SicsCommandNode(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + char *name = NULL; + pHdbCallback kalle = NULL; + pHdb parent = NULL, node = NULL; + + if(argc < 3) { + SCWrite(pCon,"ERROR: insufficent number of arguments to hcommand", + eError); + } + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + + parent = FindHdbParent(NULL, argv[1], &name, pCon); + if(parent == NULL){ + return 0; /* error message already written */ + } + node = MakeHipadabaNode(name, HIPTEXT, 1); + if(node == NULL){ + SCWrite(pCon,"ERROR: out of memory in hcommand",eError); + return 0; + } + node->value.v.text = strdup(argv[2]); + node->value.arrayLength = strlen(argv[2]); + SetHdbProperty(node,"sicscommand", argv[2]); + + kalle = MakeHipadabaCallback(CommandSetCallback,NULL, NULL); + if(kalle == NULL){ + SCWrite(pCon,"ERROR: out of memory in hcommand",eError); + return 0; + } + AppendHipadabaCallback(node,kalle); + + kalle = MakeHipadabaCallback(CommandGetCallback,NULL, NULL); + if(kalle == NULL){ + SCWrite(pCon,"ERROR: out of memory in hcommand",eError); + return 0; + } + AppendHipadabaCallback(node,kalle); + + AddHipadabaChild(parent,node,pCon); + + SCSendOK(pCon); + return 1; +} +/*======================= Property Functions ================================*/ +static int SetSICSHdbProperty(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb targetNode = NULL; + char buffer[512]; + char *val; + + if(argc < 4) { + SCWrite(pCon,"ERROR: need path key value as parameters",eError); + return 0; + } + targetNode = FindHdbNode(NULL,argv[1],pCon); + if(targetNode == NULL){ + SCWrite(pCon,"ERROR: node not found",eError); + return 0; + } + val = Arg2Tcl(argc-3, &argv[3], buffer, sizeof buffer); + if (val) { + SetHdbProperty(targetNode,argv[2], val); + if (val != buffer) free(val); + } + SCSendOK(pCon); + return 1; + } +/*-------------------------------------------------------------------------------*/ +static int DelSICSHdbProperty(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb targetNode = NULL; + + if(argc < 3) { + SCWrite(pCon,"ERROR: need path key as parameters",eError); + return 0; + } + targetNode = FindHdbNode(NULL,argv[1],pCon); + if(targetNode == NULL){ + SCWrite(pCon,"ERROR: node not found",eError); + return 0; + } + SetHdbProperty(targetNode,argv[2], NULL); + SCSendOK(pCon); + return 1; + } +/*--------------------------------------------------------------------------*/ +static int GetSICSHdbProperty(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb targetNode = NULL; + char buffer[512]; + int status; + + if(argc < 3) { + SCWrite(pCon,"ERROR: need path key as parameters",eError); + return 0; + } + targetNode = FindHdbNode(NULL,argv[1],pCon); + if(targetNode == NULL){ + SCWrite(pCon,"ERROR: node not found",eValue); + return 0; + } + status = GetHdbProperty(targetNode,argv[2],buffer,511); + if(status != 1){ + SCPrintf(pCon,eValue,"ERROR: property %s not found", argv[2]); + return 0; + } + SCPrintf(pCon,eValue,"%s.%s = %s", argv[1], argv[2], buffer); + return 1; + } + /*--------------------------------------------------------------------------*/ +static int GetSICSHdbPropertyVal(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb targetNode = NULL; + char buffer[512]; + int status; + + if(argc < 3) { + SCWrite(pCon,"ERROR: need path key as parameters",eError); + return 0; + } + targetNode = FindHdbNode(NULL,argv[1],pCon); + if(targetNode == NULL){ + SCWrite(pCon,"ERROR: node not found",eValue); + return 0; + } + status = GetHdbProperty(targetNode,argv[2],buffer,511); + if(status != 1){ + SCWrite(pCon,"ERROR: attribute not found",eValue); + return 0; + } + SCPrintf(pCon,eValue,"%s", buffer); + return 1; + } + /*--------------------------------------------------------------------------*/ +static int ListSICSHdbProperty(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb targetNode = NULL; + char buffer[512]; + const char *pKey = NULL; + pDynString data = NULL; + + if(argc < 2) { + SCWrite(pCon,"ERROR: need path as parameter",eError); + return 0; + } + targetNode = FindHdbNode(NULL,argv[1],pCon); + if(targetNode == NULL){ + SCWrite(pCon,"ERROR: node not found",eError); + return 0; + } + data = CreateDynString(64,64); + if(data == NULL){ + SCWrite(pCon,"ERROR: out of memory in ListSICSHdbProperty",eError); + return 0; + } + InitHdbPropertySearch(targetNode); + while((pKey = GetNextHdbProperty(targetNode, buffer, 511)) != NULL){ + DynStringConcat(data,(char *)pKey); + DynStringConcat(data,"="); + DynStringConcat(data,buffer); + DynStringConcat(data,"\n"); + } + SCWrite(pCon,GetCharArray(data), eValue); + DeleteDynString(data); + return 1; + } +/*---------------------------------------------------------------------------*/ +static pHdb matchHdbProp(pHdb root, char *propname, char *buffer){ + char value[1024]; + pHdb current = NULL, search; + + memset(value,0,1024); + if(GetHdbProperty(root,propname,value,1023) == 1){ + if(strstr(buffer,value) != NULL){ + return root; + } + } + current = root->child; + while(current != NULL){ + search = matchHdbProp(current,propname,buffer); + if(search != NULL){ + return search; + } + current = current->next; + } + + return NULL; +} +/*---------------------------------------------------------------------------*/ +static int MatchHdbProperty(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pHdb root = NULL; + pHdb foundNode = NULL; + char buffer[1024], *path = NULL; + + if(argc < 4){ + SCWrite(pCon,"ERROR: need root, property name and target string for search", + eError); + return 0; + } + memset(buffer,0,1024); + Arg2Text(argc-3,&argv[3],buffer,1023); + root = GetHipadabaNode(GetHipadabaRoot(), argv[1]); + if(root == NULL){ + SCWrite(pCon,"ERROR: start node for search not found",eError); + return 0; + } + + strtolower(argv[2]); + strtolower(buffer); + foundNode = matchHdbProp(root,argv[2],buffer); + + if(foundNode == NULL){ + SCWrite(pCon,"NONE", eValue); + } else { + path = GetHipadabaPath(foundNode); + SCWrite(pCon,path,eValue); + free(path); + } + return 1; +} +/*======================= Factory Functions =================================*/ +void killSICSHipadaba(){ + if(root != NULL){ + DeleteHipadabaNode(root,NULL); + } + root = NULL; +} +/*---------------------------------------------------------------------------*/ +extern int HdbNodeFactory(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); /* from sicshdbfactory.c */ +/*---------------------------------------------------------------------------*/ +int InstallSICSHipadaba(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + + root = MakeHipadabaNode("/",HIPNONE,0); + AddCommand(pSics,"hmake", MakeHdbNode, NULL, NULL); + AddCommand(pSics,"hfactory", HdbNodeFactory, NULL, NULL); + AddCommand(pSics,"hmakescript", MakeHdbScriptNode, NULL, NULL); + AddCommand(pSics,"hattach", SICSHdbAdapter, NULL, NULL); + AddCommand(pSics,"hsubsamplehm", HdbSubSample, NULL, NULL); + AddCommand(pSics,"hdel", DeleteHdbNode, NULL, NULL); + AddCommand(pSics,"hset", SetHdbNode, NULL, NULL); + AddCommand(pSics,"hupdate", UpdateHdbNode, NULL, NULL); + AddCommand(pSics,"hget", GetHdbNode, NULL, NULL); + AddCommand(pSics,"hval", GetHdbVal, NULL, NULL); + AddCommand(pSics,"hzipget",ZipGetHdbNode, NULL, NULL); + AddCommand(pSics,"hlist", ListHdbNode, NULL, NULL); + AddCommand(pSics,"hnotify", AutoNotifyHdbNode, NULL, NULL); + AddCommand(pSics,"hdelcb", RemoveHdbCallback, NULL, NULL); + AddCommand(pSics,"hlink", LinkHdbNode, NULL, NULL); + AddCommand(pSics,"hinfo", HdbNodeInfo, NULL, NULL); + AddCommand(pSics,"hval", HdbNodeVal, NULL, NULL); + AddCommand(pSics,"hchain", ChainHdbNode, NULL, NULL); + AddCommand(pSics,"hcommand",SicsCommandNode, NULL, NULL); + AddCommand(pSics,"hsetprop",SetSICSHdbProperty, NULL, NULL); + AddCommand(pSics,"hdelprop",DelSICSHdbProperty, NULL, NULL); + AddCommand(pSics,"hgetprop",GetSICSHdbProperty, NULL, NULL); + AddCommand(pSics,"hgetpropval",GetSICSHdbPropertyVal, NULL, NULL); + AddCommand(pSics,"hmatchprop",MatchHdbProperty, NULL, NULL); + AddCommand(pSics,"hlistprop",ListSICSHdbProperty, NULL, NULL); + + InstallSICSPoll(pCon,pSics,pData,argc,argv); + poller = (pSicsPoll)FindCommandData(pSics,"sicspoll","SicsPoll"); + + return 1; +} diff --git a/sicshipadaba.h b/sicshipadaba.h new file mode 100644 index 00000000..e998a1e3 --- /dev/null +++ b/sicshipadaba.h @@ -0,0 +1,431 @@ +/** @file + * This is a set of helper functions for SICS to work with the hierarchical parameter + * database hipadaba. In SICS, the calldata associated with any callback will always + * be the connection object. + * + * copyright: GPL + * + * Mark Koennecke, June 2006 + */ +#ifndef SICSHIPADABA_H_ +#define SICSHIPADABA_H_ +#include +#include +#include +#include +/*======================== callback error codes ===============================*/ +#define SICSCBRO -607 +#define SICSCBPERM -608 +#define SICSCBRANGE -609 +#define SICSCBBADFIXED -610 +#define SICSNOPAR -611 +/*======================== SICS Messages ======================================*/ +typedef struct { + char *type; + int ID; +}hdbIDMessage, *pHdbIDMessage; +/*------------------------------------------------------------------------------*/ +pHdbIDMessage GetKillIDMessage(pHdbMessage message); +pHdbIDMessage GetKillInternalIDMessage(pHdbMessage message); +/*-----------------------------------------------------------------------------*/ +typedef struct { + char *type; + void *pPtr; +}hdbPtrMessage, *pHdbPtrMessage; +/*-----------------------------------------------------------------------------*/ +pHdbPtrMessage GetKillPtrMessage(pHdbMessage message); +/*======================== data structure for automatic parameter update =======*/ +typedef struct { + SConnection *pCon; + int updateList; + int iEnd; +}hdbUpdateTask, *pHdbUpdateTask; +/*======================== common callbacks =====================================*/ +/** + * make a ReadOnly callback + * @return a callback which disallows setting of a parameter. + */ +pHdbCallback MakeReadOnlyCallback(); +/** + * make a callback which checks permissions. To be used on write + * @param priv The privilege to check against + * @return a suitably initialized callback structure for + * checking permissions. + */ +pHdbCallback MakeCheckPermissionCallback(int priv); +/** + * make a callback which directly updates a + * paramter after setting. Useful for program parameters. + * @return a suitably initialized callback structure setting + * program parameters + */ +pHdbCallback MakeSetUpdateCallback(); +/** + * Remove a SetUpdate callback. This is useful for chaning the + * behaviour of a node created with the hmake command + * @param node the node + */ +void RemoveSetUpdateCallback(pHdb node); +/** + * make a callback which starts a parameter driving. + * @param sicsObject The SICS object to drive. + * @return a suitably initialized callback structure for + * starting a parameter driving. + */ +pHdbCallback MakeSICSDriveCallback(void *sicsObject); +/** + * make a callback which reads a SICS drivable object + * @param sicsObject The SICS drivable object to read. + * @return a suitably initialized callback structure for + * reading a drivable parameter + */ +pHdbCallback MakeSICSReadDriveCallback(void *sicsObject); +/** + * make a callback which enables automatically + * notification of pCon on parameter updates. + * @param pCon The connection to notify. The notification + * is issued in the context of this connection. + * @param id An integer id which can later on be used to remove the + * callback. + * @return a suitably initialized callback structure for + * automatic notification. + */ +pHdbCallback MakeNotifyCallback(SConnection *pCon, int id); +/** + * make a callback for checking if a parameter is within a given + * range of integers + * @param min The minimum value of the range + * @param max The maximum value of the range + * @return a suitably configured callback or NULL + * when out of memory + */ +pHdbCallback MakeIntRangeCallback(int min, int max); +/** + * make a callback for checking if a parameter is one out + * of a series of permitted integers + * @param data An array of permitted integers + * @param length The length of the data array. + * @return a suitably configured callback or NULL + * when out of memory + */ +pHdbCallback MakeIntFixedCallback(int *data, int length); +/** + * make a callback for checking if a parameter is within a given + * range of floats + * @param min The minimum value of the range + * @param max The maximum value of the range + * @return a suitably configured callback or NULL + * when out of memory + */ +pHdbCallback MakeFloatRangeCallback(double min, double max); +/** + * make a callback which reads a memory address (perhaps in a + * data structure) which is a float value + * @param address The address of the parameter + * @return a suitable callback for reading this parameter. + */ +pHdbCallback MakeMemReadCallback(float *address); +/** + * make a callback which sets a memory address (perhaps in a + * data structure) which is a float value. It is assumed that + * this is a direct parameter, i.e, UpdateHipadabaPar is + * automatically called. + * @param address The address of the parameter + * @return a suitable callback for setting this parameter. + */ +pHdbCallback MakeMemSetCallback(float *address); +/** + * make a tree chnage callback + * @param pCon The connection to notfy on tree chnages + * @param id The ID of this callback + * @return a suitable callback for notififications about tree changes. + */ + pHdbCallback MakeTreeChangeCallback(SConnection *pCon, int id); + /** + * make a clalback to invoke a function node + */ + pHdbCallback MakeSICSFuncCallback(void *obj); + +/*======================== parameter creation ===================================*/ +/** + * make a simple SICS hdb parameter. Setting it will call update immediately. Use + * this for program parameters. + * @param name The name of the parameter + * @param priv The privilege required to change that parameter + * @param v The initial value and datatype of this parameter + * @return A new suitably configured Hdb parameter or NULL when out of memory. + */ +pHdb MakeSICSHdbPar(char *name, int priv, hdbValue v); +/** + * make a SICS hdb drivable parameter. Setting it will start the motor, + * virtual motor or environment parameter. This will call StartDevice + * eventually + * @param name The name of the parameter + * @param priv The privilege required to change that parameter + * @param sicsObject The object corresponding to this parameter. + * @param datatype The datatype of this variable + * @return A new suitably configured Hdb parameter or NULL when out of memory. + */ +pHdb MakeSICSHdbDriv(char *name, int priv,void *sicsObject, int datatype); +/** + * make SICS hdb variable which is connected to a memory location, perhaps in + * an objects data structure. + * @param name The name of the variable + * @param priv The privilege required to set this parameter + * @param address A pointer to the memory location of the variable. + */ +pHdb MakeSICSMemPar(char *name, int priv, float *address); +/** + * makes a SICS Hdb read only parameter. Setting such a parameter causes an error. + * @param name The name of the parameter + * @param v The initial value and datatype of this parameter + * @return A new suitably configured Hdb parameter or NULL when out of memory. + */ +pHdb MakeSICSROPar(char *name, hdbValue v); +/** + * make a SICS scriptable parameter. I.e. when this parameter is set or read, + * appropriate scripts are invoked. + * @param name The name of the parameter + * @param setScript The script to call when this parameter is being set + * @param readScript The script to call when this parameter is being read. + * @param v The initial value and datatype of this parameter + * @return A new suitably configured Hdb parameter or NULL when out of memory. + */ +pHdb MakeSICSScriptPar(char *name, char *setScript, char *readScript, hdbValue v); + +/** + * remove a SICS paramameter node and its children. In contrast to the + * normal DeletHipadabaNode, this function also takes care of + * clearing scipted nodes out of the update tasks watch list. + * @param node The node to delete + * @param callData User data for the tree change callback + */ +void RemoveSICSPar(pHdb node, void *callData); +/*=============== Add par functions =======================================*/ +/** + * add a new simple hdb parameter as child to node + * @param parent The parent node to add the new node to. + * @param name The name of the new node + * @param priv The privilege required to change that parameter + * @param v The initial value and datatype of this parameter + * @return the created node on success, NULL else + */ +pHdb AddSICSHdbPar(pHdb parent, char *name, int priv, hdbValue v); +/** + * add a new read only hdb parameter as child to node + * @param parent The parent node to add the new node to. + * @param name The name of the new node + * @param v The initial value and datatype of this parameter + * @return the created node on success, NULL else + */ +pHdb AddSICSHdbROPar(pHdb parent, char *name, hdbValue v); +/** + * Add a new hdb parameter as child to node. Updates are synced + * to the memory location data. This works for simple variables, fixed size + * arrays and fixed sized strings. This does not work for dynamically sized + * arrays or strings. + * @param parent The parent node to add the new node to. + * @param name The name of the new node + * @param priv The privilege required to change that parameter + * @param data The pointer to map this parameter to. This must be in + * dynamically allocated memory. + * @param datalength The length of the data area pointed to by data. + * @param type The data type of the parameter + * @param length The length of the type. Used for array types. + * @return the created node on success, NULL else + */ +pHdb AddSICSHdbMemPar(pHdb parent, char *name, int priv, + void *data, int datalength, int type, int length); +/*============== access support functions =================================*/ +/** Find the parent of a node to be created + * @param rootpath the root path (where to start). May be NULL for absolute paths + * @param relpath an absolute or relative path + * @param namePtr (output) a pointer to a name. Will be the last element of + * the path if the parent was found. If namePtr is NULL, the routine does + * the same as FindHdbNode(root, relpath, pCon) + * @param pCon a connection for writing the error messages (may be NULL) + * @return the parent node or NULL on failure + * + * An abolute path starts with a slash, else it is a relative path. + * If the node exists already, an error message is emitted (node exists already) + * and NULL is returned. + * + * Nodes anchored in the sics object list are also found when + * the path starts with "/sics/" + */ +pHdb FindHdbParent(char *rootpath, char *relpath, char **namePtr, SConnection *pCon); +/** FindHdbNode finds a node + * @param rootpath the root path (where to start). May be NULL for absolute paths. + * @param relpath an absolute or relative path + * @param pCon a connection for writing the error messages (may be NULL) + * @return the found node or NULL on failure + * + * An abolute path starts with a slash, else it is a relative path. + * If relpath if a single dot ('.') root is returned. + * + * Nodes anchored in the sics object list are also found when + * the path starts with "/sics/" + */ +pHdb FindHdbNode(char *rootpath, char *relpath, SConnection *pCon); +/** Get the absolute path of a node anchored in the + * Hipadaba root or in a sics object + * @param nodeArg the input node + * @param path the result + * @param pathlen length of the result + * @return 1 on success, 0 on failure + */ +int GetHdbPath(pHdb nodeArg, char *path, size_t pathlen); +/** Remove all Callbacks rooted in the main root _and_ in sics objects + * @param internalID the internalID to be looked for + */ +void RemoveSICSInternalCallback(void *internalID); +/** + * SICSHdbGetPar returns the value of a parameter. + * @param obj The object for which to get a parameter. + * @param pCon The optional connection object to use for reporting errors. + * @param path The path to the parameter. + * @param v the value + * @return 1 on success, a negative error code else. + */ +int SICSHdbGetPar(void *obj, SConnection *pCon, char *path, hdbValue *v); +/** + * SICSHdbUpdatePar updates the value of a parameter. + * @param obj The object for which to get a parameter. + * @param pCon The optional connection object to use for reporting errors. + * @param path The path to the parameter. + * @param v the value + * @return 1 on success, a negative error code else. + */ +int SICSHdbUpdatePar(void *obj, SConnection *pCon, char *path, hdbValue v); +/** + * SICSHdbSetPar sets the value of a parameter. + * @param obj The object for which to get a parameter. + * @param pCon The optional connection object to use for reporting errors. + * @param path The path to the parameter. + * @param v the value + * @return positive on success, a negative error code else. + */ +int SICSHdbSetPar(void *obj, SConnection *pCon, char *path, hdbValue v); +/** + * query function if a parameter is read only. + * @param node The ndoe to query + * @return 1 when RO, 0 else + */ +int isSICSHdbRO(pHdb node); +/*============= common SICS Interactions ===================================*/ +/** + * Install a SICS automatic notification callback on the node. This is + * a default callback using the current connection with its current + * context for notification. + * @param node The parameter on which to install the callback + * @param pCon The connection to which this callback is linked. + * @param id An int associated with this notification callback. A + * precaution for later removal. + * @param recurse a flag 0 or 1 which determines if callbacks are + * installed to all nodes recursively. + * @return 1 on success, 0 when out of memory. + */ +int InstallSICSNotify(pHdb node, SConnection *pCon, int id, int recurse); +/** + * remove all Callbacks associated with a given conenction + * @param root Where to start removing callbacks + * @param pCon The connection for which to remove callbacks + */ +void RemoveConnectionCallbacks(pHdb root, SConnection *pCon); +/** + * handles the common task of checking for, and processing a SICS parameter. + * @param root The node at which to search for parameters + * @param pCon The connection in whichs context the parameter is processed. + * @param printPrefix A prefix to prepend before printing this parameter. + * Will be ignored if NULL. + * @param argc number of arguments to process. + * @param argv The arguments to process. argv[0] should be the parameter + * name. + * @return -1 when argv[0] is no parameter, 0 on failure, 1 on success. + */ +int ProcessSICSHdbPar(pHdb root, SConnection *pCon, char *printPrefix, + int argc, char *argv[]); +/** + * print a listing of the parameters of node to pCon, using the + * specified prefix. + * @param node The node to print + * @param pCon The connection to print to + * @param prefix The prefix to use for printing + */ +void PrintSICSParList(pHdb node, SConnection *pCon, char *prefix); +/** + * save the content of the Hipadaba starting at node into a file. This can + * be used to save the configuration of an instrument. This routine is + * recursive. + * @param fd The file to write to + * @param node The node to print from + * @param prefix A prefix to use for printing. + */ +void SaveSICSHipadaba(FILE *fd, pHdb node, char *prefix); +/** + * A SICS task which scans a Hipadaba and reads and updates all parameters, + * one per invocation. TODO: how to distinguish between automatic pars which + * do not need this and pars which need this? Idea 1: make a root point at an + * artificial tree of parameters which need to be checked like this. + * @param pData The root to start scanning at. + * @return 0 when ends, 1 else + */ +int SICSHipadabaTask(void *pData); +void SICSHipadabaSignal(void *pData, int iSignal, void *pSigData); +/*================== value helpers ========================================*/ +/** + * format a Hdb Value into a string using SICS defaults. + * @param v The Hdb value to format + * @param node The Hdb node (for format property) + * @return a dynamic string holding the formatted data. NULL when + * out of memory + */ +pDynString formatValue(hdbValue v, pHdb node); +/** + * read values for a Hdb value from a string. + * @param v The hdbValue to read data into. Datatype and arraylength must + * already have been initialised before this call in order to allow for + * checks. Arrays should also have been allocated in the right size. + * @param data The string to parse and convert. + * @param error A string to copy failure reasons to + * @param errlen The length of the error string + * @return 0 on failure, 1 on success + */ +int readHdbValue(hdbValue *v, char *data, char *error, int errlen); +/** + * convert from test to a Hipadaba type + * @param text The type text + * @return The converted Hipadaba type + */ +int convertHdbType(char *text); +/** + * wrap a SICSOBJFunc function as an hdbValue + * @param func the function + * @return: A properly initialized hdbValue structure + */ +hdbValue MakeSICSFunc(SICSOBJFunc func); + +/*================= SICS Interpreter Interface ===========================*/ +/** + * InstallHipadaba installs the Hipadaba commands into the SICS interpreter. + * The actual command implementation is in sicshipadaba.c. + * @param pCon The connection object + * @param pSics The SICS interpreter + * @param pData The object data structure + * @param argc The number of arguments + * @param argv[] The text arguments + * @return 0 on filaure, 1 on success + */ +int InstallSICSHipadaba(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); +/** + * get the root of the SICS Hipadaba tree + * @return The root node of the hipdaba + */ +pHdb GetHipadabaRoot(); +/** + * kill the SICS hierarchical database + * Only to be called when shutting down the SICServer + */ +void killSICSHipadaba(void); +#endif /*SICSHIPADABA_H_*/ diff --git a/sicsobj.c b/sicsobj.c new file mode 100644 index 00000000..0eaabcdd --- /dev/null +++ b/sicsobj.c @@ -0,0 +1,354 @@ +/** + * This is the header file for the new (as of 2007) style SICS objects + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, July 2007 + */ +#include +#include +#include "assert.h" +#include "ifile.h" +#include "sicsobj.h" +#include "dynstring.h" +#include "macro.h" +#include "sicshipadaba.h" +#include "initializer.h" +#include "splitter.h" + +extern int decodeSICSPriv(char *txt); /* from access.c */ +/*--------------------------------------------------------------------------*/ +void DefaultKill(void *data){ + return; +} +/*---------------------------------------------------------------------------*/ +pSICSOBJ MakeSICSOBJv(char *name, char *class, int type, int priv){ + pSICSOBJ pNew = NULL; + hdbValue val; + + pNew = (pSICSOBJ)malloc(sizeof(SICSOBJ)); + if(pNew == NULL){ + return NULL; + } + memset(pNew,0,sizeof(SICSOBJ)); + pNew->pDes = CreateDescriptor(class); + if (type == HIPNONE) { + pNew->objectNode = MakeHipadabaNode(name, HIPNONE, 1); + } else { + val = makeHdbValue(type,0); + pNew->objectNode = MakeSICSHdbPar(name, priv, val); + ReleaseHdbValue(&val); + } + if(pNew->pDes == NULL || pNew->objectNode == NULL){ + free(pNew); + return(NULL); + } + pNew->pDes->parNode = pNew->objectNode; + pNew->KillPrivate = DefaultKill; + return pNew; +} +/*---------------------------------------------------------------------------*/ +pSICSOBJ MakeSICSOBJ(char *name, char *class){ + return MakeSICSOBJv(name, class, HIPNONE, 0); +} +/*---------------------------------------------------------------------------*/ +void KillSICSOBJ(void *data){ + pSICSOBJ self = (pSICSOBJ)data; + if(self == NULL){ + return; + } + if(self->KillPrivate != NULL && self->pPrivate != NULL){ + self->KillPrivate(self->pPrivate); + } + RemoveHdbNodeFromParent(self->objectNode, pServ->dummyCon); + if(self->pDes != NULL){ + DeleteDescriptor(self->pDes); /* kill descriptor including node */ + } + free(self); +} +/*===========================================================================*/ +static int assignPar(pHdb node, SConnection *pCon, char *data){ + char error[132], buffer[256]; + int status; + + status = readHdbValue(&node->value,data, error, 132); + if(status != 1){ + snprintf(buffer,255,"ERROR: error parsing %s: %s", + node->name, error); + SCWrite(pCon,buffer,eError); + return 0; + } + return 1; +} +/*---------------------------------------------------------------------------*/ +static int invokeOBJFunction(pSICSOBJ object, pHdb commandNode, SConnection *pCon, + int argc, char *argv[]){ + int status, i, count = 0; + pHdb currentPar = NULL; + SICSOBJFunc pFunc = NULL; + pHdb parArray[64]; + + /* + * assign parameters and fill parameter array for function at the same + * time. Be lenient about missing parameters: Then the old values will + * be used. + */ + for(i = 0, currentPar = commandNode->child; + i < argc && currentPar != NULL; + i++, currentPar = currentPar->next){ + if(argv[i] != NULL){ + status = assignPar(currentPar,pCon, argv[i]); + } + if(status != 1){ + return status; + } + parArray[i] = currentPar; + count++; + } + + pFunc = (SICSOBJFunc)commandNode->value.v.func; + if(pFunc == NULL){ + SCWrite(pCon,"ERROR: internal error, function not found",eError); + return 0; + } + status = pFunc(object, pCon, commandNode, parArray,count); + return status; +} +/*---------------------------------------------------------------------------*/ +static int ScriptObjFunc(pSICSOBJ obj, SConnection *pCon, pHdb commandNode, + pHdb par[], int nCount){ + int status, i; + Tcl_Interp *pTcl = NULL; + Tcl_DString com; + char value[256]; + pDynString val = NULL; + char *pPtr = NULL; + + memset(value,0,256); + GetHdbProperty(commandNode,"priv",value,256); + status = decodeSICSPriv(value); + if(!SCMatchRights(pCon,status)){ + return 0; + } + + if(GetHdbProperty(commandNode,"script",value,256) != 1){ + SCWrite(pCon,"ERROR: script property not configured on this node", + eError); + return 0; + } + + Tcl_DStringInit(&com); + Tcl_DStringAppend(&com,value,strlen(value)); + for(i = 0; i < nCount; i++){ + val = formatValue(par[i]->value, par[i]); + if(val != NULL){ + Tcl_DStringAppend(&com," ", 1); + pPtr = GetCharArray(val); + Tcl_DStringAppend(&com,pPtr,strlen(pPtr)); + DeleteDynString(val); + } + } + + MacroPush(pCon); + pTcl = InterpGetTcl(pServ->pSics); + status = Tcl_Eval(pTcl,Tcl_DStringValue(&com)); + Tcl_DStringFree(&com); + MacroPop(); + + if(status == TCL_OK){ + SCWrite(pCon,Tcl_GetStringResult(pTcl),eValue); + return 1; + } else { + SCWrite(pCon,Tcl_GetStringResult(pTcl),eError); + return 0; + } + return 1; +} +/*--------------------------------------------------------------------------*/ +static int MakeScriptFunc(pSICSOBJ self, SConnection *pCon, + int argc, char *argv[]){ + char path[512], *pPtr = NULL; + pHdb parent = NULL, node = NULL; + hdbValue func; + + if(argc < 5){ + SCWrite(pCon, + "ERROR: not enough arguments: obj makescriptfunc path script priv", + eError); + return 0; + } + + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + + + strncpy(path,argv[2],511); + pPtr = strrchr(path,'/'); + if(pPtr == NULL){ + /* no hierarchy */ + parent = self->objectNode; + node = MakeHipadabaNode(path,HIPFUNC,1); + } else { + /* hierarchy */ + *pPtr = '\0'; + parent = GetHipadabaNode(self->objectNode,path); + pPtr++; + node = MakeHipadabaNode(pPtr,HIPFUNC,1); + } + if(parent == NULL || node == NULL){ + SCWrite(pCon,"ERROR: root path error or out of memory",eError); + return 0; + } + node->value = MakeSICSFunc(ScriptObjFunc); + SetHdbProperty(node,"script",argv[3]); + SetHdbProperty(node,"priv",argv[4]); + AppendHipadabaCallback(node,MakeSICSFuncCallback(self)); + AddHipadabaChild(parent,node,pCon); + SCSendOK(pCon); + return 1; +} +/*---------------------------------------------------------------------------*/ +static int isNodePrintable(pHdb node){ + switch(node->value.dataType){ + case HIPNONE: + case HIPFUNC: + return 0; + default: + return 1; + } +} +/*---------------------------------------------------------------------------*/ +int InvokeSICSOBJ(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pSICSOBJ self = NULL; + int status; + pHdb parNode; + char buffer[132]; + hdbValue data; + pDynString parData; + + self = (pSICSOBJ)pData; + assert(self != NULL); + + if (argc == 1) { + parNode = self->objectNode; + if(parNode != NULL && isNodePrintable(parNode) ){ + status = GetHipadabaPar(parNode,&data,pCon); + if(status != 1){ + return 0; + } + parData = formatValue(data, parNode); + if(parData == NULL){ + SCWrite(pCon,"ERROR: failed to format data", eError); + return 0; + } + SCPrintf(pCon,eValue,"%s = %s", argv[0], GetCharArray(parData)); + DeleteDynString(parData); + return 1; + } else { + SCWrite(pCon,"ERROR: nothing to print", eError); + return 0; + } + } else { + parNode = GetHipadabaNode(self->objectNode,argv[1]); + } + if(parNode != NULL && parNode->value.dataType == HIPFUNC){ + status = invokeOBJFunction(self, parNode, pCon, argc-2, &argv[2]); + } else { + snprintf(buffer, sizeof buffer, "%s ", argv[0]); + status = ProcessSICSHdbPar(self->objectNode,pCon, buffer, + argc-1,&argv[1]); + } + if(status == -1){ + if(strcmp(argv[1],"makescriptfunc") == 0) { + return MakeScriptFunc(self,pCon,argc,argv); + } + SCPrintf(pCon, eError, "ERROR: %s %s not found", argv[0], argv[1]); + } + + return status; +} +/*---------------------------------------------------------------------------*/ +int InterInvokeSICSOBJ(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + int status; + char buffer[132]; + + status = InvokeSICSOBJ(pCon,pSics,pData,argc,argv); + if(status == -1){ + status = 0; + if(argc > 1){ + snprintf(buffer,131,"ERROR: no command or parameter found for key: %s", + argv[1]); + } else { + snprintf(buffer,131,"ERROR: no argument found"); + } + SCWrite(pCon,buffer,eError); + status = 0; + } + return status; +} +/*---------------------------------------------------------------------------*/ +pSICSOBJ SetupSICSOBJ(SConnection *pCon,SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pSICSOBJ pNew = NULL; + int status; + int type; + int priv; + + if(argc < 3){ + SCWrite(pCon,"ERROR: not enough arguments to InstallSICSOBJ",eError); + return NULL; + } + if (argc < 5) { + type = HIPNONE; + priv = usInternal; + } else { + /* convert privilege */ + priv = decodeSICSPriv(argv[3]); + /* convert datatype */ + strtolower(argv[4]); + type = convertHdbType(argv[4]); + if(type > HIPFLOAT){ + SCWrite(pCon, + "ERROR: invalid type requested: none, int, float supported", + eError); + return 0; + } + } + + pNew = MakeSICSOBJv(argv[1], argv[2], type, priv); + if(pNew == NULL){ + SCWrite(pCon,"ERROR: out of memory creating new SICS object",eError); + return NULL; + } + if (strcasecmp(argv[0],"DynSicsObj") == 0) { + /* make object dynamic by defining a descriptor command */ + SetDescriptorKey(pNew->pDes, "creationCommand", "0"); + } + + status = AddCommand(pSics, + argv[1], + InterInvokeSICSOBJ, + KillSICSOBJ, + pNew); + if(status != 1){ + KillSICSOBJ(pNew); + SCPrintf(pCon,eError,"ERROR: failed create duplicate command %s", argv[1]); + return NULL; + } + return pNew; + } +/*---------------------------------------------------------------------------*/ +int InstallSICSOBJ(SConnection *pCon,SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pSICSOBJ pNew = NULL; + + pNew = SetupSICSOBJ(pCon, pSics, pData, argc, argv); + if(pNew == NULL){ + return 0; + } else { + return 1; + } +} diff --git a/sicsobj.h b/sicsobj.h new file mode 100644 index 00000000..a095d52a --- /dev/null +++ b/sicsobj.h @@ -0,0 +1,54 @@ +/** + * This is the header file for the new (as of 2007) style SICS objects + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, July 2007 + */ +#ifndef SICSOBJ2_H_ +#define SICSOBJ2_H_ +#include +#include +/*====================================================================== + * Be careful when changing this data structure. It has to be compatible + * in its first fields with the SICS object descriptor as defined in + * obdes.h in order to achieve backwards compatibility with old style + * SICS objects. + * =====================================================================*/ +typedef struct { + pObjectDescriptor pDes; + pHdb objectNode; + void *pPrivate; + void (*KillPrivate)(void *pPrivate); +}SICSOBJ, *pSICSOBJ; +/*-----------------------------------------------------------------------*/ +typedef int (*SICSOBJFunc)(pSICSOBJ self, SConnection *pCon, + pHdb commandNode, pHdb par[], int nPar); +/*======================= Live & Death =================================*/ +pSICSOBJ MakeSICSOBJ(char *name, char *class); +pSICSOBJ MakeSICSOBJv(char *name, char *class, int type, int priv); +void KillSICSOBJ(void *data); +void DefaultKill(void *data); + + +/** + * This creates a new SICS object and installs it in the interpreter. It returns + * the newly created SICS object such that the caller can continue + * configuring it. + */ +pSICSOBJ SetupSICSOBJ(SConnection *pCon,SicsInterp *pSics, void *pData, + int argc, char *argv[]); +/*====================== Interpreter Interface =========================== + * InvokeSICSObj is special in that it returns -1 if it cannot handle + * the command. This leaves calling code the opportunity to process + * further commands.It returns 1 on success and 0 on failures though. + * ------------------------------------------------------------------------*/ +int InvokeSICSOBJ(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); +int InterInvokeSICSOBJ(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); +int InstallSICSOBJ(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + + +#endif /*SICSOBJ2_H_*/ diff --git a/sicspoll.c b/sicspoll.c new file mode 100644 index 00000000..fe798bbb --- /dev/null +++ b/sicspoll.c @@ -0,0 +1,363 @@ +/** + * This is a generalized polling module for SICS. With this module + * SICS variables can be polled regulary for updates. For different types of + * SICS variables different polling mechanisms are required. In order to cope with + * this requirement a polling interface and different drivers are defined in the + * sister module polldriv.h and polldriv.c. This module implements the interface + * to configure polling and the SICS task to run polling. + * + * Copyright: see COPYRIGHT + * + * Mark Koennecke, November-December 2006 + */ + +#include +#include +#include +#include + +#include "polldriv.h" +#include "splitter.h" +#include +#include "lld.h" + +/*================== data structure =====================================*/ +static SConnection *defCon = NULL; + +struct __SICSPOLL{ + pObjectDescriptor pDes; + int pollList; /* list with polled objects */ + int listDirty; /* a flag to set when the list has been modified. This will + cause the list polling task to go back to the start. */ + SConnection *pCon; /* connection to use for polling */ + int iEnd; /* flag ending this */ + int nPoll; /* how many to poll in one run */ + long taskID; +}; +/*-----------------------------------------------------------------------*/ +void killSicsPoll(void *data){ + pSicsPoll self = (pSicsPoll)data; + int status; + pPollDriv poll = NULL; + + self->iEnd = 1; + status = LLDnodePtr2First(self->pollList); + while(status != 0){ + poll = LLDnodePtr(self->pollList); + if(poll != NULL){ + deletePollDriv(poll); + } + status = LLDnodePtr2Next(self->pollList); + } + LLDdelete(self->pollList); + if(self->pDes) { + DeleteDescriptor(self->pDes); + } + free(self); + if(defCon != NULL){ + SCDeleteConnection(defCon); + } +} +/*----------------- list access -----------------------------------------*/ +static pPollDriv locateObject(int list, char *objectIdentifier){ + int status; + pPollDriv data = NULL; + + status = LLDnodePtr2First(list); + while(status != 0){ + data = (pPollDriv)LLDnodePtr(list); + if(data != NULL){ + if(strcmp(data->objectIdentifier,objectIdentifier) == 0){ + return data; + } + } + status = LLDnodePtr2Next(list); + } + return NULL; +} +/*===================== task function ==================================*/ +static int incrList(int list){ + int status; + + if(LLDcheck(list) == LIST_EMPTY){ + return 0; + } + status = LLDnodePtr2Next(list); + if(status == 0) { + status = LLDnodePtr2First(list); + } + return status; +} +/*---------------------------------------------------------------------------*/ +void SicsPollSignal(void *pData, int iSignal, void *pSigData){ + pSicsPoll self = NULL; + int *iInt; + + self = (pSicsPoll)pData; + + if(iSignal == SICSINT){ + iInt = (int *)pSigData; + if(*iInt == eEndServer){ + self->iEnd = 1; + } + } +} +/*----------------------------------------------------------------------*/ +static int PollTask(void *data){ + pSicsPoll self = (pSicsPoll) data; + pPollDriv poll = NULL; + int status, i; + time_t now = time(NULL); + + if(self == NULL || self->iEnd == 1){ + return 0; + } + if(LLDcheck(self->pollList) == LIST_EMPTY){ + return 1; + } + + /* + * increment list + */ + if(self->listDirty == 1){ + self->listDirty = 0; + status = LLDnodePtr2First(self->pollList); + } + + /* + * actually do poll + */ + for(i = 0; i < self->nPoll; i++){ + status = incrList(self->pollList); + poll = (pPollDriv)LLDnodePtr(self->pollList); + if(status != 0 && poll != NULL){ + if(poll->isDue(poll,now,self->pCon)){ + poll->poll(poll,self->pCon); + } + } + } + return 1; +} +/*==================== interface functions ==============================*/ +int removePollObject(pSicsPoll self, char *objectIdentifier){ + pPollDriv target = NULL; + + self->listDirty = 1; + target = locateObject(self->pollList, objectIdentifier); + if(target != NULL){ + LLDnodeDelete(self->pollList); + deletePollDriv(target); + return 1; + } else{ + return 0; + } +} +/*------------------------------------------------------------------------*/ +int addPollObject(SicsPoll *self, SConnection *pCon, char *objectIdentifier, + char *driver, int argc, char *argv[]){ + int status; + pPollDriv driv = NULL; + + driv = makePollDriver(pCon, driver,objectIdentifier, + argc,argv); + if(driv == NULL){ + return 0; + } + + LLDnodeAppend(self->pollList,&driv); + return 1; +} +/*-----------------------------------------------------------------------*/ +static void printPollList(pSicsPoll self, SConnection *pCon){ + int status; + pPollDriv driv = NULL; + char buffer[512]; + + status = LLDnodePtr2First(self->pollList); + while(status != 0){ + driv = (pPollDriv)LLDnodePtr(self->pollList); + if(driv != NULL){ + snprintf(buffer,512,"%60s %3d", + driv->objectIdentifier, driv->pollIntervall); + SCWrite(pCon,buffer,eValue); + } + status = LLDnodePtr2Next(self->pollList); + } +} +/*================== interpreter interface ===============================*/ +int SICSPollWrapper(SConnection *pCon,SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pSicsPoll self = (pSicsPoll)pData; + pPollDriv driv = NULL; + int status, iVal; + char buffer[512]; + pDynString txt = NULL; + + assert(self != NULL); + if(argc < 2){ + SCWrite(pCon,"ERROR: Not enough arguments",eError); + return 0; + } + + strtolower(argv[1]); + if(strcmp(argv[1],"del") == 0) { + if(argc < 3){ + SCWrite(pCon,"ERROR: Not enough arguments",eError); + return 0; + } + + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + + status = removePollObject(self,argv[2]); + if(status == 0) { + SCWrite(pCon,"ERROR: object to remove from poll list not found",eError); + return 0; + } else { + SCSendOK(pCon); + return 1; + } + } else if(strcmp(argv[1],"add") == 0) { + if(argc < 4){ + SCWrite(pCon,"ERROR: Not enough arguments",eError); + return 0; + } + + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + + driv = makePollDriver(pCon,argv[3], argv[2], + argc-4, &argv[4]); + if(driv != NULL){ + LLDnodeAppend(self->pollList,&driv); + SCSendOK(pCon); + return 1; + } else { + return 0; + } + } else if (strcmp(argv[1],"npoll") == 0) { + if(argc < 3) { + snprintf(buffer,512,"%s.%s = %d", argv[0], "npoll", self->nPoll); + } else { + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + status = sscanf(argv[2],"%d",&self->nPoll); + if(status != 1) { + snprintf(buffer,512,"ERROR: failed to convert %s to int",argv[2]); + SCWrite(pCon,buffer,eError); + return 0; + } else { + SCSendOK(pCon); + return 1; + } + } + } else if (strcmp(argv[1],"listen") == 0) { + self->pCon = pCon; + SCSendOK(pCon); + return 1; + } else if (strcmp(argv[1],"unlisten") == 0) { + self->pCon = defCon; + SCSendOK(pCon); + return 1; + } else if (strcmp(argv[1],"intervall") == 0){ + if(argc < 3){ + SCWrite(pCon,"ERROR: Not enough arguments",eError); + return 0; + } + + if(!SCMatchRights(pCon,usMugger)){ + return 0; + } + + driv = locateObject(self->pollList,argv[2]); + if(driv == NULL){ + SCWrite(pCon,"ERROR: object not in polling list",eError); + return 0; + } + if(argc > 3){ + status = sscanf(argv[3],"%d", &iVal); + if(status != 1){ + snprintf(buffer,511,"ERROR: failed to convert %s to int", + argv[3]); + SCWrite(pCon,buffer,eError); + return 0; + } + if(iVal < 0) { + SCWrite(pCon,"ERROR: new value for intervall out of range",eError); + return 0; + } + driv->pollIntervall = iVal; + SCSendOK(pCon); + return 1; + } else { + snprintf(buffer,511,"%s.intervall = %d",driv->objectIdentifier, + driv->pollIntervall); + SCWrite(pCon,buffer,eValue); + return 1; + } + } else if (strcmp(argv[1],"list") == 0) { + SCStartBuffering(pCon); + printPollList(self,pCon); + txt = SCEndBuffering(pCon); + if(txt != NULL){ + SCWrite(pCon,GetCharArray(txt),eValue); + } + return 1; + } else if (strcmp(argv[1],"poll") == 0) { + if(argc < 3){ + SCWrite(pCon,"ERROR: Not enough arguments",eError); + return 0; + } + + driv = locateObject(self->pollList,argv[2]); + if(driv == NULL){ + SCWrite(pCon,"ERROR: object not in polling list",eError); + return 0; + } + status = driv->poll(driv,pCon); + if(status != 1){ + SCWrite(pCon,"ERROR: polling object",eError); + return 0; + } + SCWrite(pCon,"Object polled OK",eError); + return 1; + } + return 1; +} +/*------------------------------------------------------------------------*/ +int InstallSICSPoll(SConnection *pCon,SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pSicsPoll pNew = NULL; + + pNew = malloc(sizeof(SicsPoll)); + if(pNew == NULL){ + return 0; + } + memset(pNew,0,sizeof(SicsPoll)); + + pNew->pDes = CreateDescriptor("SicsPoll"); + pNew->pollList = LLDcreate(sizeof(void *)); + defCon = SCCreateDummyConnection(pSics); + + if(pNew->pDes == NULL|| pNew->pollList < 0 || defCon == NULL){ + SCWrite(pCon,"ERROR: out of memory creating SicsPoll",eError); + return 0; + } + pNew->pCon = defCon; + pNew->nPoll = 3; + + TaskRegister(pServ->pTasker,PollTask,SicsPollSignal,NULL,pNew, 10); + + if(argc > 1){ + AddCommand(pSics,argv[1],SICSPollWrapper, + killSicsPoll,pNew); + } else { + AddCommand(pSics,"sicspoll",SICSPollWrapper, + killSicsPoll,pNew); + } + + return 1; +} diff --git a/sicspoll.h b/sicspoll.h new file mode 100644 index 00000000..3d307223 --- /dev/null +++ b/sicspoll.h @@ -0,0 +1,53 @@ +/** + * This is a generalized polling module for SICS. With this module + * SICS variables can be polled regulary for updates. For different types of + * SICS variables different polling mechanisms are required. In order to cope with + * this requirement a polling interface and different drivers are defined in the + * sister module polldriv.h and polldriv.c. This module implements the interface + * to configure polling and the SICS task to run polling. + * + * Copyright: see COPYRIGHT + * + * Mark Koennecke, November-December 2006 + */ +#ifndef SICSPOLL_H_ +#define SICSPOLL_H_ +/*=================== interpreter interface ================================*/ +/** + * the factory function + */ +int InstallSICSPoll(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); +/* + * the actual wrapper which allows to configure and query the polling + * module + */ +int SICSPollWrapper(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); +/*================== Internal Interface ===================================*/ +typedef struct __SICSPOLL SicsPoll, *pSicsPoll; +/** + * add an object to the list of pollable objects. + * @param self A pointer to a sicsPoll object managing the poll loop. + * @param pCon a connection to report errors to + * @param objectidentifier A string describing the object to poll. + * This parameter will be used by the poll driver to locate the + * pollable obejct. + * @param The driver to use for polling + * @param argc The number of additional parameters to pass to the + * poll driver + * @param argv[] The parameters to pass to the poll driver. + * @return 1 on success or a negative error code when things go wrong. + */ +int addPollObject(SicsPoll *self, SConnection *pCon, char *objectIdentifier, + char *driver, int argc, char *argv[]); +/** + * remove an object from the polling loop. + * @param self A pointer to a sicsPoll object managing the poll loop. + * @param objectIdentifier The identifier of the object to remove from + * the poll loop. + * @return 1 on success, a negative error code on failure. + */ +int removePollObject(SicsPoll *self, char *objectIdentifier); + +#endif /*SICSPOLL_H_*/ diff --git a/sicspoll.tc b/sicspoll.tc new file mode 100644 index 00000000..ea2eb738 --- /dev/null +++ b/sicspoll.tc @@ -0,0 +1,306 @@ +/** + * This is a generalized polling module for SICS. With this module + * SICS variables can be polled regulary for updates. For different types of + * SICS variables different polling mechanisms are required. In order to cope with + * this requirement a polling interface and different drivers are defined in the + * sister module polldriv.h and polldriv.c. This module implements the interface + * to configure polling and the SICS task to run polling. + * + * Copyright: see COPYRIGHT + * + * Mark Koennecke, November-December 2006 + */ +<%! source sicstemplates.tcl %> +<% stdIncludes %> +#include "polldriv.h" +#include "splitter.h" +#include +#include "lld.h" + +/*================== data structure =====================================*/ +static SConnection *defCon = NULL; + +struct __SICSPOLL{ + pObjectDescriptor pDes; + int pollList; /* list with polled objects */ + int listDirty; /* a flag to set when the list has been modified. This will + cause the list polling task to go back to the start. */ + SConnection *pCon; /* connection to use for polling */ + int iEnd; /* flag ending this */ + int nPoll; /* how many to poll in one run */ + long taskID; +}; +/*-----------------------------------------------------------------------*/ +void killSicsPoll(void *data){ + pSicsPoll self = (pSicsPoll)data; + int status; + pPollDriv poll = NULL; + + self->iEnd = 1; + status = LLDnodePtr2First(self->pollList); + while(status != 0){ + poll = LLDnodePtr(self->pollList); + if(poll != NULL){ + deletePollDriv(poll); + } + status = LLDnodePtr2Next(self->pollList); + } + LLDdelete(self->pollList); + free(self); + if(defCon != NULL){ + SCDeleteConnection(defCon); + } +} +/*----------------- list access -----------------------------------------*/ +static pPollDriv locateObject(int list, char *objectIdentifier){ + int status; + pPollDriv data = NULL; + + status = LLDnodePtr2First(list); + while(status != 0){ + data = (pPollDriv)LLDnodePtr(list); + if(data != NULL){ + if(strcmp(data->objectIdentifier,objectIdentifier) == 0){ + return data; + } + } + status = LLDnodePtr2Next(list); + } + return NULL; +} +/*===================== task function ==================================*/ +static int incrList(int list){ + int status; + + if(LLDcheck(list) == LIST_EMPTY){ + return 0; + } + status = LLDnodePtr2Next(list); + if(status == 0) { + status = LLDnodePtr2First(list); + } + return status; +} +/*---------------------------------------------------------------------------*/ +void SicsPollSignal(void *pData, int iSignal, void *pSigData){ + pSicsPoll self = NULL; + int *iInt; + + self = (pSicsPoll)pData; + + if(iSignal == SICSINT){ + iInt = (int *)pSigData; + if(*iInt == eEndServer){ + self->iEnd = 1; + } + } +} +/*----------------------------------------------------------------------*/ +static int PollTask(void *data){ + pSicsPoll self = (pSicsPoll) data; + pPollDriv poll = NULL; + int status, i; + time_t now = time(NULL); + + if(self == NULL || self->iEnd == 1){ + return 0; + } + if(LLDcheck(self->pollList) == LIST_EMPTY){ + return 1; + } + + /* + * increment list + */ + if(self->listDirty == 1){ + self->listDirty = 0; + status = LLDnodePtr2First(self->pollList); + } + + /* + * actually do poll + */ + for(i = 0; i < self->nPoll; i++){ + status = incrList(self->pollList); + poll = (pPollDriv)LLDnodePtr(self->pollList); + if(status != 0 && poll != NULL){ + if(poll->isDue(poll,now,self->pCon)){ + poll->poll(poll,self->pCon); + } + } + } + return 1; +} +/*==================== interface functions ==============================*/ +int removePollObject(pSicsPoll self, char *objectIdentifier){ + pPollDriv target = NULL; + + self->listDirty = 1; + target = locateObject(self->pollList, objectIdentifier); + if(target != NULL){ + LLDnodeDelete(self->pollList); + deletePollDriv(target); + return 1; + } else{ + return 0; + } +} +/*------------------------------------------------------------------------*/ +int addPollObject(SicsPoll *self, SConnection *pCon, char *objectIdentifier, + char *driver, int argc, char *argv[]){ + int status; + pPollDriv driv = NULL; + + driv = makePollDriver(pCon, driver,objectIdentifier, + argc,argv); + if(driv == NULL){ + return 0; + } + + LLDnodeAppend(self->pollList,&driv); + return 1; +} +/*-----------------------------------------------------------------------*/ +static void printPollList(pSicsPoll self, SConnection *pCon){ + int status; + pPollDriv driv = NULL; + char buffer[512]; + + status = LLDnodePtr2First(self->pollList); + while(status != 0){ + driv = (pPollDriv)LLDnodePtr(self->pollList); + if(driv != NULL){ + snprintf(buffer,512,"%60s %3d", + driv->objectIdentifier, driv->pollIntervall); + SCWrite(pCon,buffer,eValue); + } + status = LLDnodePtr2Next(self->pollList); + } +} +/*================== interpreter interface ===============================*/ +<%makeSicsFunc SICSPollWrapper%>{ + pSicsPoll self = (pSicsPoll)pData; + pPollDriv driv = NULL; + int status, iVal; + char buffer[512]; + pDynString txt = NULL; + + assert(self != NULL); + <%testNoPar 2 5%> + strtolower(argv[1]); + if(strcmp(argv[1],"del") == 0) { + <%testNoPar 3 9%> + <%testPriv usMugger 9%> + status = removePollObject(self,argv[2]); + if(status == 0) { + SCWrite(pCon,"ERROR: object to remove from poll list not found",eError); + return 0; + } else { + SCSendOK(pCon); + return 1; + } + } else if(strcmp(argv[1],"add") == 0) { + <%testNoPar 4 9%> + <%testPriv usMugger 9%> + driv = makePollDriver(pCon,argv[3], argv[2], + argc-3, &argv[4]); + if(driv != NULL){ + LLDnodeAppend(self->pollList,&driv); + SCSendOK(pCon); + return 1; + } else { + return 0; + } + } else if (strcmp(argv[1],"npoll") == 0) { + <%# sicsPar name, c-name nargs priv type indent + sicsPar npoll self->nPoll 3 usMugger int 9%> + } else if (strcmp(argv[1],"listen") == 0) { + self->pCon = pCon; + SCSendOK(pCon); + return 1; + } else if (strcmp(argv[1],"unlisten") == 0) { + self->pCon = defCon; + SCSendOK(pCon); + return 1; + } else if (strcmp(argv[1],"intervall") == 0){ + <%testNoPar 3 9%> + <%testPriv usMugger 9%> + driv = locateObject(self->pollList,argv[2]); + if(driv == NULL){ + SCWrite(pCon,"ERROR: object not in polling list",eError); + return 0; + } + if(argc > 3){ + status = sscanf(argv[3],"%d", &iVal); + if(status != 1){ + snprintf(buffer,511,"ERROR: failed to convert %s to int", + argv[3]); + SCWrite(pCon,buffer,eError); + return 0; + } + if(iVal < 0) { + SCWrite(pCon,"ERROR: new value for intervall out of range",eError); + return 0; + } + driv->pollIntervall = iVal; + SCSendOK(pCon); + return 1; + } else { + snprintf(buffer,511,"%s.intervall = %d",driv->objectIdentifier, + driv->pollIntervall); + SCWrite(pCon,buffer,eValue); + return 1; + } + } else if (strcmp(argv[1],"list") == 0) { + SCStartBuffering(pCon); + printPollList(self,pCon); + txt = SCEndBuffering(pCon); + if(txt != NULL){ + SCWrite(pCon,GetCharArray(txt),eValue); + } + return 1; + } else if (strcmp(argv[1],"poll") == 0) { + <%testNoPar 3 9%> + driv = locateObject(self->pollList,argv[2]); + if(driv == NULL){ + SCWrite(pCon,"ERROR: object not in polling list",eError); + return 0; + } + status = driv->poll(driv,pCon); + if(status != 1){ + SCWrite(pCon,"ERROR: polling object",eError); + return 0; + } + SCWrite(pCon,"Object polled OK",eError); + return 1; + } + return 1; +} +/*------------------------------------------------------------------------*/ +<%makeSicsFunc InstallSICSPoll%>{ + pSicsPoll pNew = NULL; + + <%newStrucRet SicsPoll 5 0%> + pNew->pDes = CreateDescriptor("SicsPoll"); + pNew->pollList = LLDcreate(sizeof(void *)); + defCon = SCCreateDummyConnection(pSics); + + if(pNew->pDes == NULL|| pNew->pollList < 0 || defCon == NULL){ + SCWrite(pCon,"ERROR: out of memory creating SicsPoll",eError); + return 0; + } + pNew->pCon = defCon; + pNew->nPoll = 3; + + TaskRegister(pServ->pTasker,PollTask,SicsPollSignal,NULL,pNew, 10); + + if(argc > 1){ + AddCommand(pSics,argv[1],SICSPollWrapper, + killSicsPoll,pNew); + } else { + AddCommand(pSics,"sicspoll",SICSPollWrapper, + killSicsPoll,pNew); + } + + return 1; +} diff --git a/sicsstat.tcl b/sicsstat.tcl index 3ebfc9d2..c4461394 100644 --- a/sicsstat.tcl +++ b/sicsstat.tcl @@ -1,7 +1,60 @@ +exe batchpath ./ +exe syspath ./ +sample +sample setAccess 2 +title +title setAccess 2 +user +user setAccess 2 +email unknown +email setAccess 2 +address unknown +address setAccess 2 +fax unknown +fax setAccess 2 +phone unknown +phone setAccess 2 +# Motor som +som sign 1.000000 +som SoftZero 0.000000 +som SoftLowerLim -180.000000 +som SoftUpperLim 180.000000 +som Fixed -1.000000 +som InterruptMode 0.000000 +som precision 0.010000 +som ignorefault 0.000000 +som AccessCode 2.000000 +som failafter 3.000000 +som maxretry 3.000000 +som movecount 10.000000 +# Motor stt +stt sign 1.000000 +stt SoftZero 0.000000 +stt SoftLowerLim 0.000000 +stt SoftUpperLim 120.000000 +stt Fixed -1.000000 +stt InterruptMode 0.000000 +stt precision 0.010000 +stt ignorefault 0.000000 +stt AccessCode 2.000000 +stt failafter 3.000000 +stt maxretry 3.000000 +stt movecount 10.000000 +# Motor dth +dth sign 1.000000 +dth SoftZero 0.000000 +dth SoftLowerLim 0.000000 +dth SoftUpperLim 200.000000 +dth Fixed -1.000000 +dth InterruptMode 0.000000 +dth precision 0.010000 +dth ignorefault 0.000000 +dth AccessCode 2.000000 +dth failafter 3.000000 +dth maxretry 3.000000 +dth movecount 10.000000 # Counter counter -counter SetPreset 10.000000 +counter SetPreset 1000.000000 counter SetMode Timer hm CountMode timer -hm preset 10.000000 -hm genbin 0.000000 10.000000 10000 -hm init +hm preset 3.000000 diff --git a/sicstemplates.tcl b/sicstemplates.tcl new file mode 100644 index 00000000..a5159572 --- /dev/null +++ b/sicstemplates.tcl @@ -0,0 +1,107 @@ +#---------------------------------------------------------------------------- +# This file contaisn template generation code for SICS programming +# +# copyright: see file COPYRIGHT +# +# Mark Koennecke, December 2006 +#---------------------------------------------------------------------------- +proc stdIncludes {} { + append txt "#include \n" + append txt "#include \n" + append txt "#include \n" + append txt "#include \n" +} +#--------------------------------------------------------------------------- +proc makeSicsFunc {name} { + append txt "int ${name}(SConnection *pCon,SicsInterp *pSics, void *pData,\n" + append txt " int argc, char *argv\[\])" + return $txt +} +#---------------------------------------------------------------------------- +proc newStruc {name indent} { + set pre [string repeat " " $indent] + append txt "pNew = malloc(sizeof($name));\n" + append txt $pre "if(pNew == NULL){\n" + append txt $pre " return NULL;\n" + append txt $pre "}\n" + append txt $pre "memset(pNew,0,sizeof($name));\n" + return $txt +} +#---------------------------------------------------------------------------- +proc newStrucRet {name indent retval} { + set pre [string repeat " " $indent] + append txt "pNew = malloc(sizeof($name));\n" + append txt $pre "if(pNew == NULL){\n" + append txt $pre " return $retval;\n" + append txt $pre "}\n" + append txt $pre "memset(pNew,0,sizeof($name));\n" + return $txt +} +#----------------------------------------------------------------------------- +proc testNoPar {noPar indent} { + set pre [string repeat " " $indent] + append txt "if(argc < $noPar){\n" + append txt $pre " SCWrite(pCon,\"ERROR: Not enough arguments\",eError);\n" + append txt $pre " return 0;\n" + append txt $pre "}\n" + return $txt +} +#------------------------------------------------------------------------------- +proc testPriv {priv indent} { + set pre [string repeat " " $indent] + append txt "if(!SCMatchRights(pCon,$priv)){\n" + append txt $pre " return 0;\n" + append txt $pre "}\n" + return $txt +} +#-------------------------------------------------------------------------------- +proc sicsPar {parName parCName noPar priv type indent} { + set pre [string repeat " " $indent] + append txt "if(argc < $noPar) {\n" + switch $type { + int { + append txt $pre + append txt " snprintf(buffer,512,\"%s.%s = %d\", argv\[0\], \"$parName\", $parCName);\n" + } + float { + append txt $pre + append txt " snprintf(buffer,512,\"%s.%s = %f\", argv\[0\], \"$parName\", $parCName);\n" + } + text { + append txt $pre + append txt " snprintf(buffer,512,\"%s.%s = %s\", argv\[0\], \"$parName\", $parCName);\n" + } + default { + error "$type is unknown" + } + } + append txt $pre "} else {\n" + append txt $pre " " [testPriv $priv [expr $indent + 4]] + set n [expr $noPar -1] + switch $type { + int { + append txt $pre " status = sscanf(argv\[$n\],\"%d\",&$parCName);\n" + } + float { + append txt $pre " status = sscanf(argv\[$n\],\"%f\",&$parCName);\n" + } + text { + append txt $pre " if($parCName != NULL){\n" + append txt $pre " free($parCName);\n" + append txt $pre " }\n" + append txt $pre " $parCName = strdup(argv\[$n\]);\n" + append txt $pre " status = 1;\n" + } + } + append txt $pre " if(status != 1) {\n" + append txt $pre " snprintf(buffer,512," + append txt "\"ERROR: failed to convert %s to $type\",argv\[$n\]);\n" + append txt $pre " SCWrite(pCon,buffer,eError);\n" + append txt $pre " return 0;\n" + append txt $pre " } else {\n" + append txt $pre " SCSendOK(pCon);\n" + append txt $pre " return 1;\n" + append txt $pre " }\n" + append txt $pre "}" + return $txt +} diff --git a/sicvar.c b/sicvar.c index 4d351a38..5b8af4c9 100644 --- a/sicvar.c +++ b/sicvar.c @@ -136,14 +136,7 @@ "float", NULL }; - static char *cAccess[] = { - "internal", - "mugger", - "user", - "spy", - NULL - }; - + int VarFactory(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) { @@ -196,30 +189,8 @@ } /* argv[3] must be the access code, check that now */ - i = 0; - while(cAccess[i] != NULL) - { - if(strcmp(argv[3],cAccess[i]) == 0) - { - break; - } - i++; - } - switch(i) - { - case 0: - iCode = usInternal; - break; - case 1: - iCode = usMugger; - break; - case 2: - iCode = usUser; - break; - case 3: - iCode = usSpy; - break; - default: + i = decodeSICSPriv(argv[3]); + if(i < 0){ sprintf(pBueffel," %s access code %s not recognized", argv[1], argv[3]); SCWrite(pCon,pBueffel,eError); @@ -227,7 +198,7 @@ } /* now we can actually install the variable */ - pRes = VarCreate(iCode,eType,argv[1]); + pRes = VarCreate(i,eType,argv[1]); if(!pRes) { sprintf(pBueffel,"Memory Error creating variable %s", argv[1]); diff --git a/simcter.c b/simcter.c index 1b501e67..821cc0a4 100644 --- a/simcter.c +++ b/simcter.c @@ -259,7 +259,7 @@ static float FAILRATE; { for(i = 0; i < MAXCOUNT; i++) { - self->lCounts[i] = (long)rand(); + self->lCounts[i] = (long)(SimRandom()*100); } self->lCounts[1] = self->fPreset; return OKOK; @@ -272,7 +272,7 @@ static float FAILRATE; for(i = 0; i < MAXCOUNT; i++) { - self->lCounts[i] = (long)rand(); + self->lCounts[i] = (long)(SimRandom()*100); } self->lCounts[1] = self->fPreset; return OKOK; @@ -337,7 +337,7 @@ static float FAILRATE; pData->lEnd = 0; pData->iPause = 0; pRes->pData = (void *)pData; - pRes->iNoOfMonitors = 2; + pRes->iNoOfMonitors = 8; pRes->fTime = 0; /* assign functions */ diff --git a/sinfox.c b/sinfox.c index 75cfc21e..e4c6b449 100644 --- a/sinfox.c +++ b/sinfox.c @@ -439,7 +439,7 @@ static int SinfoxList(pSinfox pSin, SicsInterp* pSics, SConnection *pCon, iRet = EnumChoice(pIFaces,iNumIFaces,objName); if(-1 < iRet) { - if((0==strcmp(keyName,"device")) || (0==strcmp(keyName,NULL))) + if((0==strcmp(keyName,"device")) || keyName == NULL ) { /* format as sinfox list server interface interfaceName */ setOKE(objName,keyName,eltName,"server","interface",objName); } else if(0==strcmp(keyName,"command")) diff --git a/sllinux_def b/sllinux_def index ea98bcdb..fdd1cc2a 100644 --- a/sllinux_def +++ b/sllinux_def @@ -6,7 +6,8 @@ #DFORTIFY= -DFORTIFY #FORTIFYOBJ= fortify.o strdup.o +DFORTIFY= -pg MFLAGS=-f makefile_linux$(DUMMY) -HDFROOT=/afs/psi.ch/project/sinq/sl-linux +HDFROOT=/afs/psi.ch/project/sinq/sl5 diff --git a/splitter.c b/splitter.c index 7d67de02..09543881 100644 --- a/splitter.c +++ b/splitter.c @@ -114,7 +114,7 @@ typedef enum _CharType {eSpace, eNum,eeText,eQuote} CharType; { TokenList *pList = NULL; TokenList *pCurrent; - char pBueffel[132]; + char pBueffel[256]; char *pChar; CharType eWhat; int i, n; @@ -141,7 +141,7 @@ typedef enum _CharType {eSpace, eNum,eeText,eQuote} CharType; { i = 0; pChar++; - while( (isEnd(*pChar) != 2) && (CheckSpecial(pChar) != eQuote)) + while( (isEnd(*pChar) != 2) && (CheckSpecial(pChar) != eQuote) && i < 250) { if (*pChar == '\\') { pBueffel[i] = Tcl_Backslash(pChar, &n); @@ -400,12 +400,18 @@ typedef enum _CharType {eSpace, eNum,eeText,eQuote} CharType; return 1; } /*--------------------------------------------------------------------------*/ -char *Arg2Tcl(int argc, char *argv[], char *buffer, int buffersize) { - int i, l, firstArgToQuote, quote; +char *Arg2Tcl0(int argc, char *argv[], char *buffer, int buffersize, char *prepend) { + int i, l, firstArgToQuote, quote, prependlen; char ch; char *res, *arg; - l = 0; + if (prepend) { + prependlen = strlen(prepend); + l = prependlen + 1; + } else { + prependlen = 0; + l = 0; + } firstArgToQuote = argc; quote = 0; for (i=0; i= firstArgToQuote) *res++ = '"'; arg = argv[i]; @@ -464,6 +475,33 @@ char *Arg2Tcl(int argc, char *argv[], char *buffer, int buffersize) { *res='\0'; return buffer; } +/*--------------------------------------------------------------------------*/ +char *Arg2Tcl(int argc, char *argv[], char *buffer, int buffersize) { + return Arg2Tcl0(argc, argv, buffer, buffersize, NULL); +} +/*----------------------------------------------------------------------------*/ +char *sicsNextNumber(char *pStart, char pNumber[80]){ + int charCount = 0; + pNumber[0] = '\0'; + + /* advance to first digit */ + while(isspace(*pStart) && *pStart != '\0'){ + pStart++; + } + if(*pStart == '\0'){ + return NULL; + } + + /* copy */ + while(!isspace(*pStart) && *pStart != '\0' && charCount < 78){ + pNumber[charCount] = *pStart; + pStart++; + charCount++; + } + pNumber[charCount] = '\0'; + return pStart; +} + /*============================================================================ Testprogram, can be activated by defining MAIN diff --git a/splitter.h b/splitter.h index a5e6fbbf..84849d38 100644 --- a/splitter.h +++ b/splitter.h @@ -87,5 +87,16 @@ typedef struct _TokenEntry { if (result != NULL && result != buffer) free(result); !*/ - + char *Arg2Tcl0(int argc, char *argv[], char *buffer, int buffersize, char *prepend); + /*! + This function is added for convenience, and acts similar to Arg2Tcl. + If prepend is not NULL, its contents appear untreated before the args. + A space is used as separator. + !*/ + char *sicsNextNumber(char *pStart, char pNumber[80]); + /*! + This function reads the next number from the string in pStart. + The number is put into pNumber, a pointer to the string after + the number is returned or NULL whne the string is exhausted. + !*/ #endif diff --git a/statemon.c b/statemon.c new file mode 100644 index 00000000..8141e5bf --- /dev/null +++ b/statemon.c @@ -0,0 +1,341 @@ +/** + * This is a state monitor. It collects all the start and stop messages + * from the device executor and from scan and batch commands. Clients can + * listen to this in order to figure out what is actually going on in a + * given SICS installation. This might in the end supersede the status code + * managment in status.c + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, January 2007 + */ +#include +#include "exeman.h" +#include "scan.h" +#include "scan.i" +#include "stptok.h" +#include "statemon.h" +#include "sicshipadaba.h" +/*==========================================================================*/ +typedef struct __STATEMON { + pObjectDescriptor pDes; + pICallBack pCall; +}StateMon; +/*============================ Callbacks =================================*/ + static int DevexecCallback(int iEvent, void *text, void *pData, + commandContext cc){ + char pDevice[132]; + int eventCode; + pStateMon self = (pStateMon)pData; + + memset(pDevice,0,132); + if(iEvent == DRIVSTAT){ + stptok(text,pDevice,131," "); + if(strstr(text,"started") != NULL){ + eventCode = STSTART; + } else if(strstr(text,"finished") != NULL) { + eventCode = STEND; + } else { + printf("Unrecognized event text from devexec in statemon.c: %s\n", + (char *)text); + return 0; + } + if(self != NULL){ + InvokeCallBack(self->pCall,eventCode,pDevice); + } + } + return 1; + } +/*---------------------------------------------------------------------------*/ +static int StateMonScanInterest(int iEvent, void *pEventData, void *pUser, + commandContext cc){ + pScanData pScan = NULL; + pStateMon self = (pStateMon)pUser; + + pScan = (pScanData)pEventData; + + if(pScan == NULL || self == NULL){ + printf("Bad StateMonScanInterst in statemon\n"); + return 0; + } + + if(iEvent == SCANSTART){ + InvokeCallBack(self->pCall,STSTART,pScan->objectName); + return 1; + }else if(iEvent == SCANEND){ + InvokeCallBack(self->pCall,STEND,pScan->objectName); + return 1; + } + return 1; +} +/*--------------------------------------------------------------------------*/ +static int ExeCallback(int iEvent, void *pEvent, void *pUser, + commandContext cc){ + pStateMon self = (pStateMon)pUser; + char *name = (char *)pEvent; + char pBueffel[131]; + + if(self == NULL || name == NULL){ + printf("Bad ExeCallback in statemon\n"); + return 0; + } + + if(iEvent == BATCHSTART){ + snprintf(pBueffel,131,"exe %s",name); + InvokeCallBack(self->pCall,STSTART,pBueffel); + return 1; + } + if(iEvent == BATCHEND){ + snprintf(pBueffel,131,"exe %s",name); + InvokeCallBack(self->pCall,STEND,pBueffel); + return 1; + } + return 0; +} +/*=============== user callbacks ============================================*/ +static int StateInterest(int iEvent, void *pEvent, void *pUser, + commandContext cc){ + SConnection *pCon = (SConnection *)pUser; + char *device = (char *)pEvent; + char buffer[256]; + + if(pCon == NULL || device == NULL){ + printf("Bad StateInterest in statemon\n"); + return 0; + } + if(iEvent == STSTART){ + snprintf(buffer,255,"STARTED = %s", device); + SCWriteInContext(pCon,buffer,eWarning,cc); + } + if(iEvent == STEND){ + snprintf(buffer,255,"FINISH = %s", device); + SCWriteInContext(pCon,buffer,eWarning,cc); + } + return 1; +} +/*--------------------------------------------------------------------------*/ +static pHdb recurseInterestNode(pHdb current, char *pDevice){ + char pSicsdev[131], pAlias[132]; + pHdb result = NULL; + char *alias = NULL, *pPtr = NULL; + + memset(pSicsdev,0,132); + memset(pAlias,0,132); + if(current != NULL){ + if(GetHdbProperty(current,"sicsdev",pSicsdev,131) != 0){ + strtolower(pSicsdev); + if(strcmp(pSicsdev,pDevice) == 0){ + return current; + } + /* + * try to look for aliases, too + */ + alias = FindAliases(pServ->pSics,pSicsdev); + pPtr = alias; + if(pPtr != NULL){ + while((pPtr = stptok(pPtr,pAlias,131,",")) != NULL){ + if(strcmp(pAlias,pDevice) == 0){ + return current; + } + } + if(alias != NULL){ + free(alias); + } + } + } + current = current->child; + while(current != NULL){ + result = recurseInterestNode(current, pDevice); + if(result != NULL){ + return result; + } + current = current->next; + } + } + return NULL; +} +/*--------------------------------------------------------------------------*/ +static pHdb locateInterestNode(char *device){ + char pDevice[132], pSicsdev[132]; + pHdb current = NULL, result = NULL; + + memset(pDevice,0,132); + memset(pSicsdev,0,132); + + /* + * this is to strip off exes batch file name + */ + stptok(device,pDevice,131," "); + strtolower(pDevice); + + current = GetHipadabaRoot(); + return recurseInterestNode(current,pDevice); +} +/*--------------------------------------------------------------------------*/ +static int StateHdbInterest(int iEvent, void *pEvent, void *pUser, + commandContext cc){ + SConnection *pCon = (SConnection *)pUser; + char *device = (char *)pEvent, *path = NULL; + char buffer[1024]; + pHdb node = NULL; + + if(pCon == NULL || device == NULL){ + printf("Bad StateHdbInterest in statemon\n"); + return 0; + } + node = locateInterestNode(device); + if(node != NULL){ + path = GetHipadabaPath(node); + if(iEvent == STSTART){ + snprintf(buffer,1024,"%s STARTED", path); + SCWriteInContext(pCon,buffer,eWarning,cc); + } + if(iEvent == STEND){ + snprintf(buffer,1024,"%s FINISH", path); + SCWriteInContext(pCon,buffer,eWarning,cc); + } + if(path != NULL){ + free(path); + } + } + return 1; +} +/*====================== interpreter interface ==============================*/ +static void killStateMon(void *pData){ + pStateMon self = NULL; + + self = (pStateMon)pData; + if(self != NULL){ + if(self->pDes != NULL){ + DeleteDescriptor(self->pDes); + } + if(self->pCall != NULL){ + DeleteCallBackInterface(self->pCall); + } + free(self); + } +} +/*---------------------------------------------------------------------------*/ +int StateMonFactory(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + pStateMon pNew = NULL; + commandContext cc; + pICallBack target = NULL; + void *pPtr = NULL, *exe = NULL, *pDevexec = NULL; + + exe = FindCommandData(pSics,"exe", "ExeManager"); + pDevexec = FindCommandData(pSics,"stopexe","DeviceExecutor"); + if(exe == NULL || pDevexec == NULL){ + SCWrite(pCon, + "ERROR: both the device executor and the batch file module must be installed before initialising statemon", + eError); + return 0; + } + + /* + * generate data structures + */ + strcpy(cc.deviceID,"statemon"); + cc.transID = -120; + pNew = (pStateMon)malloc(sizeof(StateMon)); + if(pNew == NULL){ + SCWrite(pCon,"ERROR: out of memory creating StateMon",eError); + return 0; + } + memset(pNew,0,sizeof(StateMon)); + pNew->pDes = CreateDescriptor("statemon"); + pNew->pCall = CreateCallBackInterface(); + if(pNew->pDes == NULL || pNew->pCall == NULL){ + SCWrite(pCon,"ERROR: out of memory creating StateMon",eError); + return 0; + } + /* + * register callbacks + */ + target = GetCallbackInterface(pDevexec); + assert(target != NULL); + RegisterCallback(target,cc,DRIVSTAT,DevexecCallback,pNew,NULL); + target = GetCallbackInterface(exe); + assert(target != NULL); + RegisterCallback(target,cc,BATCHSTART,ExeCallback,pNew,NULL); + RegisterCallback(target,cc,BATCHEND,ExeCallback,pNew,NULL); + + if(argc > 1) { + pPtr = FindCommandData(pSics,argv[1],"ScanObject"); + if(pPtr == NULL){ + SCWrite(pCon,"ERROR: failked to locate scan object",eError); + } else { + target = GetCallbackInterface(pPtr); + assert(target != NULL); + RegisterCallback(target,cc,SCANSTART,StateMonScanInterest,pNew,NULL); + RegisterCallback(target,cc,SCANEND,StateMonScanInterest,pNew,NULL); + } + } + /* + * TODO: add kill functions + */ + AddCommand(pSics,"statemon",StateMonAction,killStateMon,pNew); + return 1; +} +/*---------------------------------------------------------------------------*/ +int StateMonAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]){ + long lID; + int i; + pStateMon self = NULL; + + self = (pStateMon)pData; + assert(self != NULL); + + if(argc < 2){ + SCWrite(pCon,"ERROR: not enough arguments to statemon",eError); + return 0; + } + strtolower(argv[1]); + if(strcmp(argv[1],"interest") == 0){ + lID = RegisterCallback(self->pCall, SCGetContext(pCon),STSTART, StateInterest, + pCon, NULL); + SCRegister(pCon,pSics, self->pCall,lID); + lID = RegisterCallback(self->pCall, SCGetContext(pCon),STEND, StateInterest, + pCon, NULL); + SCRegister(pCon,pSics, self->pCall,lID); + SCSendOK(pCon); + return 1; + } else if(strcmp(argv[1],"uninterest") == 0) { + for(i = 0; i < 2; i++){ + lID = SCgetCallbackID(pCon,self->pCall); + if(lID >= 0){ + RemoveCallback(self->pCall,lID); + SCUnregisterID(pCon,lID); + } + } + SCSendOK(pCon); + return 1; + } else if(strcmp(argv[1],"hdbinterest") == 0){ + lID = RegisterCallback(self->pCall, SCGetContext(pCon),STSTART, StateHdbInterest, + pCon, NULL); + SCRegister(pCon,pSics, self->pCall,lID); + lID = RegisterCallback(self->pCall, SCGetContext(pCon),STEND, StateHdbInterest, + pCon, NULL); + SCRegister(pCon,pSics, self->pCall,lID); + SCSendOK(pCon); + return 1; + } else if(strcmp(argv[1],"start") == 0) { + if(argc > 2){ + InvokeCallBack(self->pCall,STSTART,argv[2]); + SCSendOK(pCon); + return 1; + } + return 0; + } else if(strcmp(argv[1],"stop") == 0) { + if(argc > 2){ + InvokeCallBack(self->pCall,STEND,argv[2]); + SCSendOK(pCon); + return 1; + } + return 0; + } + + SCWrite(pCon,"ERROR: keyword not recognized",eError); + return 0; +} diff --git a/statemon.h b/statemon.h new file mode 100644 index 00000000..686cdcd9 --- /dev/null +++ b/statemon.h @@ -0,0 +1,21 @@ +/** + * This is a state monitor. It collects all the start and stop messages + * from the device executor and from scan and batch commands. Clients can + * listen to this in order to figure out what is actually going on in a + * given SICS installation. This might in the end supersede the status code + * managment in status.c + * + * copyright: see file COPYRIGHT + * + * Mark Koennecke, January 2007 + */ +#ifndef STATEMON_H_ +#define STATEMON_H_ +typedef struct __STATEMON *pStateMon; + +/*===================== The interpreter interface ===========================*/ +int StateMonFactory(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); +int StateMonAction(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); +#endif /*STATEMON_H_*/ diff --git a/statistics.c b/statistics.c new file mode 100644 index 00000000..cfbfefb9 --- /dev/null +++ b/statistics.c @@ -0,0 +1,167 @@ +#include +#include +#include "statistics.h" + +typedef struct timeval tv_t; + +struct Statistics { + tv_t tim; + tv_t last; + tv_t total; + long cnt; + char *name; + Statistics *next; +}; + +static Statistics *current = NULL; +static tv_t last, lastStat; +static Statistics *idle = NULL, *list = NULL; +static int init = 1; +/*-----------------------------------------------------------------------*/ +tv_t timeDif(tv_t t1, tv_t t2) { + tv_t result; + + result.tv_usec = t2.tv_usec - t1.tv_usec; + result.tv_sec = t2.tv_sec - t1.tv_sec; + if (result.tv_usec < 0) { + result.tv_usec += 1000000; + result.tv_sec --; + } + if (result.tv_sec < 0) { + result.tv_sec += 24*3600; + } + return result; +} +/*-----------------------------------------------------------------------*/ +void timeAdd(tv_t *t1, tv_t t2) { + t1->tv_usec += t2.tv_usec; + t1->tv_sec += t2.tv_sec + (t1->tv_usec / 1000000); + t1->tv_usec %= 1000000; +} +/*-----------------------------------------------------------------------*/ +double timeFloat(tv_t t) { + return t.tv_sec + 1e-6 * t.tv_usec; +} +/*-----------------------------------------------------------------------*/ +int StatisticsCommand(SConnection *con, SicsInterp *pSics, void *pData, + int argc, char *argv[]) { + Statistics *p; + tv_t now; + double dif, percent, full, dt, calls; + + gettimeofday(&now, 0); + dif = timeFloat(timeDif(lastStat, now)); + SCPrintf(con, eStatus, "calls/s time[%] full[%] mean[ms] command"); + SCPrintf(con, eStatus, "----------------------------------------------"); + for (p = list; p != NULL; p = p->next) { + if (dif > 0) { + calls = p->cnt / dif; + percent = timeFloat(p->tim) * 100 / dif; + full = timeFloat(p->total) * 100 / dif; + if (full > 0 || percent > 0) { + if (p->cnt > 0) { + dt = timeFloat(p->total) * 1000.0 / p->cnt; + } else { + dt = 0; + } + SCPrintf(con, eStatus, "%7.1f %7.1f %7.1f %8.2f %s", calls, + percent, full, dt, p->name); + } + } + p->cnt = 0; + p->tim.tv_sec = 0; + p->tim.tv_usec = 0; + p->total.tv_sec = 0; + p->total.tv_usec = 0; + } + SCPrintf(con, eStatus, "----------------------------------------------"); + SCPrintf(con, eStatus, "total time %.2f", dif); + lastStat = now; + return 1; +} +/*-----------------------------------------------------------------------*/ +Statistics *StatisticsNew(char *name) { + Statistics *new; + + if (init) { + gettimeofday(&lastStat, 0); + last = lastStat; + init = 0; + } + new = calloc(1,sizeof(*new)); + if (new) { + new->cnt = 0; + new->tim.tv_sec = 0; + new->tim.tv_usec = 0; + new->total.tv_sec = 0; + new->total.tv_usec = 0; + new->last.tv_sec = -1; + new->next = list; + new->name = strdup(name); + list = new; + } + return new; +} +/*-----------------------------------------------------------------------*/ +void StatisticsKill(Statistics *stat) { + Statistics *p, **last; + + /* find in list */ + last = &list; + for (p = list; p != NULL && p != stat; p = p->next) { + last = &p->next; + } + + /* remove from list */ + if (p == stat) { + *last = p->next; + } + + /* kill it */ + if (stat->name) { + free(stat->name); + stat->name = NULL; + } + free(stat); +} +/*-----------------------------------------------------------------------*/ +Statistics *StatisticsBegin(Statistics *stat) { + Statistics *res; + tv_t now; + + res = current; + gettimeofday(&now, 0); + if(current != NULL){ + timeAdd(¤t->tim, timeDif(last, now)); + } + last = now; + current = stat; + stat->last = now; + stat->cnt ++; + return res; +} +/*-----------------------------------------------------------------------*/ +void StatisticsEnd(Statistics *stat) { + tv_t now; + + gettimeofday(&now, 0); + timeAdd(¤t->tim, timeDif(last, now)); + last = now; + if(current != NULL){ + if (current->last.tv_sec >= 0) { + timeAdd(¤t->total, timeDif(current->last, now)); + } + current->last.tv_sec = -1; + } + current = stat; +} +/*-----------------------------------------------------------------------*/ +void StatisticsInit(void) { + if (idle == NULL) { + AddCmd("statistics", StatisticsCommand); + last = lastStat; + idle = StatisticsNew(""); + current = idle; + } +} + diff --git a/statistics.h b/statistics.h new file mode 100644 index 00000000..b3d5b599 --- /dev/null +++ b/statistics.h @@ -0,0 +1,36 @@ +/* statistics.h + + statistics on the time spend for commands/tasks + +*/ + +#ifndef STATISTICS_H +#define STATISTICS_H + +typedef struct Statistics Statistics; + +Statistics *StatisticsNew(char *name); +/* create a new Statistics item */ + +void StatisticsKill(Statistics *s); +/* kill item */ + +Statistics *StatisticsBegin(Statistics *s); +void StatisticsEnd(Statistics *s); +/* + Switch statistics to item s. + + Typical usage: + + old = StatisticsBegin(thisItem); + + ...do work related with thisItem... + + StatisticsEnd(old); +*/ + +void StatisticsInit(void); + +/* initialize module and activate statistics command */ + +#endif diff --git a/status.c b/status.c index 2aaaca2e..b88b60ca 100644 --- a/status.c +++ b/status.c @@ -9,6 +9,9 @@ Updated in order to prevent status floods Mark Koennecke, July 2004 + + Reworked restore to keep parameters from uninitialized devices + Mark Koennecke, November 2007 Copyright: @@ -48,7 +51,7 @@ #include "sics.h" #include "status.h" #include "interrupt.h" -#include "devexec.h" +#include "sicshipadaba.h" #undef VALUECHANGE #define VALUECHANGE 2 @@ -197,13 +200,29 @@ SCPopContext(pCon); return 1; } - +/*------------------- The CallBack function for interest ------------------*/ + static int StatusHDBCallback(int iEvent, void *pEvent, void *pUser, + commandContext cc) + { + pHdb node = NULL; + hdbValue v; + + assert(pUser); + + node = (pHdb)pUser; + v = MakeHdbText(strdup(pText[eCode])); + if(node != NULL && iEvent == VALUECHANGE){ + UpdateHipadabaPar(node,v,NULL); + } + return 1; + } /*-----------------------------------------------------------------------*/ int UserStatus(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) { char pBueffel[512]; long lID; + pHdb node = NULL; assert(pSics); assert(pCon); @@ -221,12 +240,33 @@ if(strcmp(argv[1],"interest") == 0) { lID = RegisterCallback(pCall, SCGetContext(pCon), - VALUECHANGE, StatusCallback, + VALUECHANGE, StatusCallback, pCon, NULL); SCRegister(pCon,pSics, pCall,lID); SCSendOK(pCon); return 1; } + else if(strcmp(argv[1],"hdbinterest") == 0) + { + if(argc > 2){ + node = GetHipadabaNode(GetHipadabaRoot(),argv[2]); + if(node != NULL){ + lID = RegisterCallback(pCall, SCGetContext(pCon), + VALUECHANGE, StatusHDBCallback, + node, NULL); + /* SCRegister(pCon,pSics, pCall,lID); */ + SCSendOK(pCon); + return 1; + } else { + SCWrite(pCon,"ERROR: Hipadaba node not found",eError); + return 0; + } + } + } else { + SCWrite(pCon,"ERROR: require node parameter to register status callback", + eError); + return 0; + } } /* else just print value */ @@ -249,6 +289,7 @@ SetStatus(eEager); SetInterrupt(eContinue); ClearExecutor(GetExecutor()); + SCsetMacro(pCon,0); return 1; } /* ===================== Control Connection Management ====================*/ @@ -304,106 +345,3 @@ pOwner->pSock = pCon->pSock; return 1; } -/*---------------------------------------------------------------------*/ -static int motorSave = 0; -/*-----------------------------------------------------------------------*/ - int BackupStatus(SConnection *pCon, SicsInterp *pSics, void *pData, - int argc, char *argv[]) - { - int iRet; - char pBueffel[512]; - char *pFile = NULL; - - assert(pSics); - assert(pCon); - - if(argc < 2) - { - pFile = IFindOption(pSICSOptions,"statusfile"); - if(pFile) - { - iRet = WriteSicsStatus(pSics,pFile,motorSave); - } - else - { - SCWrite(pCon,"ERROR: No filename given for backup, Aborted.", - eError); - return 0; - } - } - else - { - if(strcmp(argv[1],"motorSave") == 0) - { - if(motorSave== 1) - motorSave= 0; - else - motorSave= 1; - sprintf(pBueffel,"New Value of motorSave= %d\n",motorSave); - SCWrite(pCon,pBueffel,eValue); - return 1; - } - else - { - iRet = WriteSicsStatus(pSics,argv[1],motorSave); - } - } - - if(!iRet) - { - sprintf(pBueffel,"ERROR: could not open file %s\n", argv[1]); - SCWrite(pCon,pBueffel,eError); - return 0; - } - SCSendOK(pCon); - return 1; - } -/*-----------------------------------------------------------------------*/ - int RestoreStatus(SConnection *pCon, SicsInterp *pSics, void *pData, - int argc, char *argv[]) - { - char pBueffel[512]; - int iRights; - int iRet; - char *pFile = NULL; - writeFunc oldWrite; - - assert(pSics); - assert(pCon); - - if(argc < 2) - { - pFile = IFindOption(pSICSOptions,"statusfile"); - if(pFile) - { - sprintf(pBueffel,"FileEval %s",pFile); - } - else - { - SCWrite(pCon,"ERROR: No filename given for backup, Aborted.", - eError); - return 0; - } - } - else - { - sprintf(pBueffel,"FileEval %s",argv[1]); - } - - iRights = SCGetRights(pCon); - pCon->iUserRights = usInternal; - oldWrite = SCGetWriteFunc(pCon); - SCSetWriteFunc(pCon,SCNotWrite); - iRet = InterpExecute(pSics,pCon,pBueffel); - SCSetWriteFunc(pCon,oldWrite); - pCon->iUserRights = iRights; - /* - if we do not override parameterChange here, the backup file - would be overwritten after each restore... Not the right thing - to do! - */ - pCon->parameterChange = 0; - SCSendOK(pCon); - return iRet; - } - diff --git a/status.h b/status.h index 1d9dca09..f80774da 100644 --- a/status.h +++ b/status.h @@ -50,11 +50,4 @@ int ResetStatus(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]); - int BackupStatus(SConnection *pCon, SicsInterp *pSics, void *pData, - int argc, char *argv[]); - int RestoreStatus(SConnection *pCon, SicsInterp *pSics, void *pData, - int argc, char *argv[]); - - - #endif diff --git a/statusfile.c b/statusfile.c new file mode 100644 index 00000000..85371cbb --- /dev/null +++ b/statusfile.c @@ -0,0 +1,296 @@ +/*-------------------------------------------------------------------------- + + Handling of status files + + + Mark Koennecke, November 1996 + + Updated in order to prevent status floods + Mark Koennecke, July 2004 + + Reworked restore to keep parameters from uninitialized devices + Mark Koennecke, November 2007 + + Copyright: + + Labor fuer Neutronenstreuung + Paul Scherrer Institut + CH-5423 Villigen-PSI + + + The authors hereby grant permission to use, copy, modify, distribute, + and license this software and its documentation for any purpose, provided + that existing copyright notices are retained in all copies and that this + notice is included verbatim in any distributions. No written agreement, + license, or royalty fee is required for any of the authorized uses. + Modifications to this software may be copyrighted by their authors + and need not follow the licensing terms described here, provided that + the new terms are clearly indicated on the first page of each file where + they apply. + + IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY + FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY + DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE + IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR + MODIFICATIONS. +-----------------------------------------------------------------------------*/ +#include +#include +#include +#include "fortify.h" +#include +#include "sics.h" +#include "statusfile.h" +#include "lld_str.h" +#include "lld.h" +#include "exebuf.h" + +static int parameterChange = 0; +/*-----------------------------------------------------------------------*/ +int StatusFileTask(void *data) { + char *pFile = NULL; + + if (parameterChange) { + parameterChange = 0; + + assert(pServ->pSics); + pFile = IFindOption(pSICSOptions,"statusfile"); + if (pFile) { + WriteSicsStatus(pServ->pSics,pFile,0); + } + } + return 1; +} +/*---------------------------------------------------------------------*/ +static int motorSave = 0; +/*-----------------------------------------------------------------------*/ + int BackupStatus(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) + { + int iRet; + char pBueffel[512]; + char *pFile = NULL; + + assert(pSics); + assert(pCon); + + if(argc < 2) + { + pFile = IFindOption(pSICSOptions,"statusfile"); + if(pFile) + { + iRet = WriteSicsStatus(pSics,pFile,motorSave); + } + else + { + SCWrite(pCon,"ERROR: No filename given for backup, Aborted.", + eError); + return 0; + } + } + else + { + if(strcmp(argv[1],"motorSave") == 0) + { + if(motorSave== 1) + motorSave= 0; + else + motorSave= 1; + sprintf(pBueffel,"New Value of motorSave= %d\n",motorSave); + SCWrite(pCon,pBueffel,eValue); + return 1; + } + else + { + iRet = WriteSicsStatus(pSics,argv[1],motorSave); + } + } + + if(!iRet) + { + sprintf(pBueffel,"ERROR: could not open file %s\n", argv[1]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + SCSendOK(pCon); + return 1; + } +/*---------------------------------------------------------------------*/ +static int restoreOccurred = 0; +int hasRestored(){ + return restoreOccurred; +} +/*---------------------------------------------------------------------------*/ +typedef struct { + pObjectDescriptor pDes; + int errList; +}RestoreObj, *pRestoreObj; +/*---------------------------------------------------------------------------*/ +static void killRestore(void *data){ + pRestoreObj self = (pRestoreObj)data; + if(self == NULL){ + return; + } + if(self->errList >= 0){ + LLDdeleteBlob(self->errList); + } + if(self->pDes != NULL){ + DeleteDescriptor(self->pDes); + } + free(self); +} +/*--------------------------------------------------------------------------*/ +static int SaveRestore(void *obj, char *name, FILE *fd){ + int status; + char buffer[1024]; + + pRestoreObj self = (pRestoreObj)obj; + if(self == NULL){ + return 0; + } + fprintf(fd,"\n#--- BEGIN (commands producing errors on last restore)\n"); + status = LLDnodePtr2First(self->errList); + while(status == 1){ + LLDstringData(self->errList,buffer); + fprintf(fd,"%s", buffer); + status = LLDnodePtr2Next(self->errList); + } + fprintf(fd,"#--- END (commands producing errors on last restore)\n\n"); + return 1; +} +/*---------------------------------------------------------------------------*/ +int InstallBckRestore(SConnection *pCon, SicsInterp *pSics){ + pRestoreObj pNew = NULL; + + pNew = malloc(sizeof(RestoreObj)); + if(pNew == NULL){ + SCWrite(pCon,"ERROR: no memory to create restore object! This is SERIOUS!!!", + eError); + return 0; + } + pNew->pDes = CreateDescriptor("BckRestore"); + pNew->errList = LLDstringCreate(); + if(pNew->pDes == NULL || pNew->errList < 0){ + SCWrite(pCon,"ERROR: no memory to create restore object! This is SERIOUS!!!", + eError); + return 0; + } + pNew->pDes->SaveStatus = SaveRestore; + AddCommand(pSics,"Backup",BackupStatus,NULL,NULL); + AddCommand(pSics,"Restore",RestoreStatus,killRestore,pNew); + + return 1; +} +/*-----------------------------------------------------------------------*/ +static int listRestoreErr(pRestoreObj self, SConnection *pCon){ + char buffer[1024]; + int status; + pDynString data = NULL; + + SCStartBuffering(pCon); + status = LLDnodePtr2First(self->errList); + while(status == 1){ + LLDstringData(self->errList,buffer); + SCWrite(pCon,buffer,eValue); + status = LLDnodePtr2Next(self->errList); + } + data = SCEndBuffering(pCon); + if(data != NULL){ + SCWrite(pCon,GetCharArray(data),eValue); + } + return 1; +} +/*-----------------------------------------------------------------------*/ + int RestoreStatus(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) + { + char pBueffel[512]; + int iRights; + int iRet; + char *pFile = NULL; + writeFunc oldWrite; + pExeBuf buffi = NULL; + pRestoreObj self = (pRestoreObj)pData; + + assert(pSics); + assert(pCon); + assert(self != NULL); + + if(argc < 2) + { + pFile = IFindOption(pSICSOptions,"statusfile"); + if(pFile) + { + sprintf(pBueffel,"%s",pFile); + } + else + { + SCWrite(pCon,"ERROR: No filename given for backup, Aborted.", + eError); + return 0; + } + } + else + { + if(strcasecmp(argv[1],"listerr") == 0){ + return listRestoreErr(self,pCon); + } else if(strcasecmp(argv[1],"killerr") == 0){ + if(self->errList >= 0){ + LLDdeleteBlob(self->errList); + } + self->errList = LLDstringCreate(); + StatusFileDirty(); + SCSendOK(pCon); + return 1; + } else { + sprintf(pBueffel,"%s",argv[1]); + } + } + + buffi = exeBufCreate("restore"); + if(buffi == NULL){ + SCWrite(pCon,"ERROR: failed to allocate buffer for restore",eError); + return 0; + } + iRet = exeBufLoad(buffi,pBueffel); + if(iRet != 1){ + exeBufDelete(buffi); + SCWrite(pCon,"ERROR: failed open status file",eError); + return 0; + } + LLDdeleteBlob(self->errList); + self->errList = LLDstringCreate(); + iRights = SCGetRights(pCon); + pCon->iUserRights = usInternal; + oldWrite = SCGetWriteFunc(pCon); + SCSetWriteFunc(pCon,SCNotWrite); + iRet = exeBufProcessErrList(buffi,pSics,pCon,self->errList); + restoreOccurred = 1; + SCSetWriteFunc(pCon,oldWrite); + pCon->iUserRights = iRights; + exeBufDelete(buffi); + /* + if we do not override parameterChange here, the backup file + would be overwritten after each restore... Not the right thing + to do! + */ + parameterChange = 0; + SCSendOK(pCon); + return iRet; + } +/*-----------------------------------------------------------------------*/ +void StatusFileDirty(void) { + parameterChange = 1; +} +/*-----------------------------------------------------------------------*/ +void StatusFileInit(void) { + TaskRegister(pServ->pTasker, StatusFileTask, NULL, NULL, NULL, 0); +} + diff --git a/statusfile.h b/statusfile.h new file mode 100644 index 00000000..39976d8b --- /dev/null +++ b/statusfile.h @@ -0,0 +1,24 @@ +/*-------------------------------------------------------------------------- + Status files + + Mark Koennecke, November 1996 + copyright: see implementation file +----------------------------------------------------------------------------*/ +#ifndef STATUSFILE_H +#define STATUSFILE_H + +#include + + int InstallBckRestore(SConnection *pCon, SicsInterp *pSics); + int BackupStatus(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + + int RestoreStatus(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); + int hasRestored(); + + int StatusFileTask(void *data); /* saves status file if parameters have changed */ + + void StatusFileDirty(void); /* indicate that the status file has to be rewritten */ +#endif + diff --git a/stdscan.c b/stdscan.c index 8a25f6ff..2f1ae4d1 100644 --- a/stdscan.c +++ b/stdscan.c @@ -64,8 +64,240 @@ static char *fixExtension(char *filename) } return fixExtension(pRes); } +/*-------------------------------------------------------------------------*/ +void WriteTemplate(FILE *fd, FILE *temp, char *filename, pScanData pScan, + SConnection *pCon, SicsInterp *pSics) +{ + char pBuffer[512], pError[512], *pPtr, *pName; + CommandList *pCom = NULL; + pSicsVariable pVar = NULL; + pDummy pDum = NULL; + pIDrivable pDriv = NULL; + pMotor pMot = NULL; + pVarEntry pScanVar = NULL; + void *pVoid = NULL; + float fVal; + int iRet; + + /* loop through description file and act along the way */ + while(fgets(pBuffer,511,temp) != NULL) + { + pPtr = strstr(pBuffer,"!!VAR("); + if(pPtr) /* handle a Sics variable */ + { + /* extract the name */ + pName = pPtr + 6; /* first char of name */ + *pPtr = '\0'; /* this is the starter of the line */ + pPtr = pName; + while( (*pPtr != '\0') && (*pPtr != ')') ) + { + pPtr++; + } + *pPtr = '\0'; + /* find the variable */ + pCom = FindCommand(pSics,pName); + if(!pCom) + { + sprintf(pError,"ERROR: variable %s NOT found",pName); + SCWrite(pCon,pError,eError); + continue; + } + pVar = (pSicsVariable)pCom->pData; + if(!pVar) + { + sprintf(pError,"ERROR: variable %s NOT found",pName); + SCWrite(pCon,pError,eError); + continue; + } + switch(pVar->eType) + { + case veFloat: + sprintf(pError,"%f",pVar->fVal); + break; + case veInt: + sprintf(pError,"%d",pVar->iVal); + break; + case veText: + sprintf(pError,"%s",pVar->text); + break; + } + /* finally write */ + fprintf(fd,"%s %s\n",pBuffer,pError); + continue; + }/* end variable */ +/*------- Drivable */ + pPtr = strstr(pBuffer,"!!DRIV("); + if(pPtr) + { + /* extract the name */ + pName = pPtr + 7; /* first char of name */ + *pPtr = '\0'; /* this is the starter of the line */ + pPtr = pName; + while( (*pPtr != '\0') && (*pPtr != ')') ) + { + pPtr++; + } + *pPtr = '\0'; + /* find the variable */ + pCom = FindCommand(pSics,pName); + if(!pCom) + { + sprintf(pError,"ERROR: variable %s NOT found",pName); + SCWrite(pCon,pError,eError); + continue; + } + pDum = (pDummy)pCom->pData; + if(!pDum) + { + sprintf(pError,"ERROR: variable %s is NOT drivable",pName); + SCWrite(pCon,pError,eError); + continue; + } + pDriv = (pIDrivable)pDum->pDescriptor->GetInterface(pDum,DRIVEID); + if(!pDriv) + { + sprintf(pError,"ERROR: variable %s is NOT drivable",pName); + SCWrite(pCon,pError,eError); + continue; + } + fVal = pDriv->GetValue(pDum,pCon); + fprintf(fd,"%s %f\n",pBuffer,fVal); + continue; + } /* end drive */ +/*------- zero point */ + pPtr = strstr(pBuffer,"!!ZERO("); + if(pPtr) + { + /* extract the name */ + pName = pPtr + 7; /* first char of name */ + *pPtr = '\0'; /* this is the starter of the line */ + pPtr = pName; + while( (*pPtr != '\0') && (*pPtr != ')') ) + { + pPtr++; + } + *pPtr = '\0'; + /* find the motor */ + pMot = FindMotor(pSics,pName); + if(!pMot) + { + sprintf(pError,"ERROR: motor %s NOT found",pName); + SCWrite(pCon,pError,eError); + continue; + } + iRet = MotorGetPar(pMot,"softzero",&fVal); + if(!iRet) + { + SCWrite(pCon,"ERROR: failed to read zero point",eError); + continue; + } + fprintf(fd,"%s %f\n",pBuffer,fVal); + continue; + } /* end zero point */ +/*---------- scripted info */ + pPtr = strstr(pBuffer,"!!SCRIPT("); + if(pPtr) + { + /* extract the name */ + pName = pPtr + 9; /* first char of name */ + *pPtr = '\0'; /* this is the starter of the line */ + pPtr = pName; + while( (*pPtr != '\0') && (*pPtr != ')') ) + { + pPtr++; + } + *pPtr = '\0'; + if(Tcl_Eval(InterpGetTcl(pSics),pName) == TCL_OK) + { + fprintf(fd,"%s\n",Tcl_GetStringResult(InterpGetTcl(pSics))); + } + else + { + SCWrite(pCon,"ERROR: failed to execute Tcl command",eError); + strncpy(pBuffer, Tcl_GetStringResult(InterpGetTcl(pSics)), 511); + SCWrite(pCon,pBuffer,eError); + continue; + } + } +/* ------- date */ + pPtr = strstr(pBuffer,"!!DATE!!"); + if(pPtr) + { + *pPtr = '\0'; + SNXFormatTime(pError,511); + fprintf(fd,"%s %s\n",pBuffer,pError); + continue; + } +/*-------- filename */ + pPtr = strstr(pBuffer,"!!FILE!!"); + if(pPtr) + { + *pPtr = '\0'; + fprintf(fd,"%s %s\n",pBuffer,filename); + continue; + } +/*------------ scanzero */ + pPtr = strstr(pBuffer,"!!SCANZERO!!"); + if(pPtr && pScan != NULL) + { + *pPtr = '\0'; + /* write zero point of first scan variable if motor */ + DynarGet(pScan->pScanVar,0,&pVoid); + pScanVar = (pVarEntry)pVoid; + if(pScanVar) + { + pMot = NULL; + pMot = FindMotor(pSics,ScanVarName(pScanVar)); + if(pMot != NULL) + { + MotorGetPar(pMot,"softzero",&fVal); + fprintf(fd,"%s zero = %8.3f\n",ScanVarName(pScanVar), + fVal); + } + } + } +/* --------- plain text */ + fprintf(fd,"%s",pBuffer); + } /* end while */ +} /*---------------------------------------------------------------------------*/ int WriteHeader(pScanData self) + { + int i, iRet; + FILE *fd; + + assert(self->pSics); + assert(self->pCon); + + /* open data file */ + self->fd = fopen(self->pFile,"w"); + if(!self->fd) + { + SCWrite(self->pCon,"ERROR: cannot write data file",eError); + return 0; + } + + /* open header description file */ + fd = fopen(self->pHeaderFile,"r"); + if(!fd) + { + SCWrite(self->pCon,"ERROR: cannot open header description file",eError); + return 0; + } + + WriteTemplate(self->fd,fd,self->pFile, + self, self->pCon, self->pSics); + + /* remember position for seeking to it for writing data */ + self->lPos = ftell(self->fd); + + fclose(fd); + fclose(self->fd); + self->fd = NULL; + return 1; + } +/*---------------------------------------------------------------------------*/ + int WriteHeaderOld(pScanData self) { int i, iRet; FILE *fd; @@ -378,7 +610,7 @@ int prepareDataFile(pScanData self){ char pBueffel[512]; /* allocate a new data file */ - pPtr = ScanMakeFileName(self->pSics,self->pCon); + pPtr = ScanMakeFileName(pServ->pSics,self->pCon); if(!pPtr) { SCWrite(self->pCon, @@ -405,7 +637,6 @@ int prepareDataFile(pScanData self){ char pMessage[1024]; assert(self); - assert(self->iNP > 0); assert(self->pCon); /* check boundaries of scan variables and allocate storage */ @@ -534,8 +765,8 @@ int prepareDataFile(pScanData self){ pVarEntry pVar = NULL; void *pDings; int i, iRet, status; - char pStatus[512], pItem[20]; - char pHead[512]; + char pStatus[2024], pItem[20]; + char pHead[2024]; float fVal; CountEntry sCount; @@ -555,16 +786,8 @@ int prepareDataFile(pScanData self){ pVar = (pVarEntry)pDings; if(pVar) { - if(jochenFlag == 1 && - strcmp(pVar->pObject->pDescriptor->name, "Motor") == 0) - { - MotorGetSoftPosition((pMotor)pVar->pObject,self->pCon,&fVal); - } - else - { - fVal = pVar->pInter->GetValue(pVar->pObject,self->pCon); - } - AppendScanVar(pVar,fVal); + fVal = pVar->pInter->GetValue(pVar->pObject,self->pCon); + AppendScanVar(pVar,fVal); sprintf(pItem,"%-9.9s ",ScanVarName(pVar)); strcat(pHead,pItem); sprintf(pItem,"%-9.3f ",fVal); @@ -597,8 +820,10 @@ int prepareDataFile(pScanData self){ strcat(pStatus,pItem); /* write progress */ - strcat(pHead,"\n"); - strcat(pStatus,"\n"); + /* + strcat(pHead,"\r\n"); + strcat(pStatus,"\r\n"); + */ SCWrite(self->pCon,pHead,eWarning); SCWrite(self->pCon,pStatus,eWarning); @@ -609,13 +834,8 @@ int prepareDataFile(pScanData self){ { return CollectScanDataIntern(self,iPoint,0); } -/*--------------------------------------------------------------------------*/ - int CollectScanDataJochen(pScanData self, int iPoint) - { - return CollectScanDataIntern(self,iPoint,1); - } /*------------------------------------------------------------------------*/ - static int CollectSilent(pScanData self, int iPoint) + int CollectSilent(pScanData self, int iPoint) { pVarEntry pVar = NULL; void *pDings; @@ -635,15 +855,7 @@ int prepareDataFile(pScanData self){ pVar = (pVarEntry)pDings; if(pVar) { - if(jochenFlag == 1 && - strcmp(pVar->pObject->pDescriptor->name, "Motor") == 0) - { - MotorGetSoftPosition((pMotor)pVar->pObject,self->pCon,&fVal); - } - else - { - fVal = pVar->pInter->GetValue(pVar->pObject,self->pCon); - } + fVal = pVar->pInter->GetValue(pVar->pObject,self->pCon); AppendScanVar(pVar,fVal); } } @@ -908,7 +1120,14 @@ int StandardScanWrapper(SConnection *pCon, SicsInterp *pSics, void *pData, } strtolower(argv[1]); self = (pScanData)FindCommandData(pSics,argv[2],"ScanObject"); - assert(self); + if(self == NULL){ + SCWrite(pCon,"ERROR: scan object not found",eError); + return 0; + } + + if(self->pCon == NULL){ + self->pCon = pCon; + } if(strcmp(argv[1],"writeheader") == 0){ return WriteHeader(self); diff --git a/stdscan.h b/stdscan.h index 5ad5c223..d0030c9e 100644 --- a/stdscan.h +++ b/stdscan.h @@ -11,8 +11,15 @@ #ifndef SICSSTDSCAN #define SICSSTDSCAN + /** + * make a filename according to SICS rules for this scan + */ char *ScanMakeFileName(SicsInterp *pSics, SConnection *pCon); - + /* + * write the header bits from the template + */ + void WriteTemplate(FILE *fd, FILE *temp, char *filename, pScanData pScan, + SConnection *pCon, SicsInterp *pSics); /** * write the header of the scan file */ @@ -36,7 +43,9 @@ * second version of PrepareScan which does not check scan limits */ int NonCheckPrepare(pScanData self); - + /** + * prepare for a scan without complaining... + */ int SilentPrepare(pScanData self); /** * ScanDrive handles driving to the scan point iPoint. @@ -59,6 +68,7 @@ */ int CollectScanData(pScanData self, int iPoint); int CollectScanDataJochen(pScanData self, int iPoint); + int CollectSilent(pScanData self, int iPoint); /*===================================================================*/ /** * Script invocation for writing the scan header. diff --git a/stptok.c b/stptok.c index e7da5a51..7000e83b 100644 --- a/stptok.c +++ b/stptok.c @@ -1,64 +1,64 @@ -/* -** stptok() -- public domain by Ray Gardner, modified by Bob Stout -** -** You pass this function a string to parse, a buffer to receive the -** "token" that gets scanned, the length of the buffer, and a string of -** "break" characters that stop the scan. It will copy the string into -** the buffer up to any of the break characters, or until the buffer is -** full, and will always leave the buffer null-terminated. It will -** return a pointer to the first non-breaking character after the one -** that stopped the scan. -*/ - -#include -#include - -char *stptok(const char *s, char *tok, size_t toklen, char *brk) -{ - char *lim, *b; - - if (!*s) - return NULL; - - lim = tok + toklen - 1; - while ( *s && tok < lim ) - { - for ( b = brk; *b; b++ ) - { - if ( *s == *b ) - { - *tok = 0; - return (char *)(s+1); - } - } - *tok++ = *s++; - } - *tok = 0; - return (char *)s; -} -/*---------------------------------------------------------------------------*/ - char *SkipSpace(char *pText) - { - char *pRes; - - pRes = pText; - while(*pRes) - { - if( (*pRes != ' ') && (*pRes != '\t') && (*pRes != '\r') ) - { - return pRes; - } - pRes++; - } - return NULL; - } - - - - - - - - - - +/* +** stptok() -- public domain by Ray Gardner, modified by Bob Stout +** +** You pass this function a string to parse, a buffer to receive the +** "token" that gets scanned, the length of the buffer, and a string of +** "break" characters that stop the scan. It will copy the string into +** the buffer up to any of the break characters, or until the buffer is +** full, and will always leave the buffer null-terminated. It will +** return a pointer to the first non-breaking character after the one +** that stopped the scan. +*/ + +#include +#include + +char *stptok(const char *s, char *tok, size_t toklen, char *brk) +{ + char *lim, *b; + + if (!*s) + return NULL; + + lim = tok + toklen - 1; + while ( *s && tok < lim ) + { + for ( b = brk; *b; b++ ) + { + if ( *s == *b ) + { + *tok = 0; + return (char *)(s+1); + } + } + *tok++ = *s++; + } + *tok = 0; + return (char *)s; +} +/*---------------------------------------------------------------------------*/ + char *SkipSpace(char *pText) + { + char *pRes; + + pRes = pText; + while(*pRes) + { + if( (*pRes != ' ') && (*pRes != '\t') && (*pRes != '\r') ) + { + return pRes; + } + pRes++; + } + return NULL; + } + + + + + + + + + + diff --git a/stringdict.c b/stringdict.c index 10219bfe..16767163 100644 --- a/stringdict.c +++ b/stringdict.c @@ -75,12 +75,13 @@ return pNew; } /*------------------------------------------------------------------------*/ - void DeleteStringDict(pStringDict self) + void DeleteStringDict(pStringDict self) { int iRet; SDE sVal; assert(self); + iRet = LLDnodePtr2First(self->iList); while(iRet != 0) { @@ -173,11 +174,16 @@ { if(pResult == NULL) { - return strlen(sVal.value); + return strlen(sVal.value) + 1; /* for \0 */ } else { strncpy(pResult,sVal.value,iLen); + /* strncpy is not guaranteed to be '\0' terminated */ + if (iLen > 0 && pResult[iLen-1] != '\0') { + /* overflow */ + pResult[iLen-1] = '\0'; + } return 1; } } @@ -185,6 +191,24 @@ } return 0; } +/*--------------------------------------------------------------------------*/ + char *StringDictGetShort(pStringDict self, char *name) + { + SDE sVal; + int iRet; + + iRet = LLDnodePtr2First(self->iList); + while(iRet != 0) + { + LLDnodeDataTo(self->iList,&sVal); + if(strcmp(sVal.name,name) == 0) + { + return sVal.value; + } + iRet = LLDnodePtr2Next(self->iList); + } + return NULL; + } /*------------------------------------------------------------------------*/ int StringDictDelete(pStringDict self, char *name) { diff --git a/stringdict.h b/stringdict.h index 6a5d5d0c..9d20ce9b 100644 --- a/stringdict.h +++ b/stringdict.h @@ -25,6 +25,10 @@ int StringDictExists(pStringDict self, char *name); int StringDictUpdate(pStringDict self, char *name, char *value); int StringDictGet(pStringDict self, char *name, char *pResult, int iLen); + + /* the result of StringDictGetShort is only valid as long that the entry is not changed */ + char *StringDictGetShort(pStringDict self, char *name); + int StringDictGetAsNumber(pStringDict self, char *name, float *fVal); int StringDictDelete(pStringDict self, char *name); diff --git a/tasdrive.c b/tasdrive.c index 9fd4e8bf..2d00de02 100644 --- a/tasdrive.c +++ b/tasdrive.c @@ -4,6 +4,9 @@ copyright: see file COPYRIGHT Mark Koennecke, May 2005 + + In the long run it might be useful to switch all this to the more + general motor list driving code. --------------------------------------------------------------------*/ #include #include "sics.h" @@ -34,6 +37,11 @@ static long TASSetValue(void *pData, SConnection *pCon, ptasMot self = (ptasMot)pData; assert(self); + if(self->code > 5 && self->math->tasMode == ELASTIC){ + SCWrite(pCon,"ERROR: cannot drive this motor in elastic mode", + eError); + return HWFault; + } setTasPar(&self->math->target,self->math->tasMode,self->code,value); self->math->mustDrive = 1; return OKOK; @@ -132,6 +140,10 @@ static float TASGetValue(void *pData, SConnection *pCon){ self->math->mustRecalculate = 0; } val = getTasPar(self->math->current,self->code); + if(self->code > 5 && self->math->tasMode == ELASTIC){ + SCWrite(pCon,"WARNING: value for this motor is meaningless in elastic mode", + eWarning); + } return (float)val; } /*-----------------------------------------------------------------*/ @@ -192,134 +204,122 @@ static int TASHalt(void *pData){ } /*--------------------------------------------------------------------------*/ -static void writeMotPos(SConnection *pCon, char *name, +static void writeMotPos(SConnection *pCon, int silent, char *name, float val, float target){ char pBueffel[132]; - snprintf(pBueffel,131,"Driving %5s from %8.3f to %8.3f", - name, val, target); - SCWrite(pCon,pBueffel,eWarning); - + if(silent != 1) { + snprintf(pBueffel,131,"Driving %5s from %8.3f to %8.3f", + name, val, target); + SCWrite(pCon,pBueffel,eWarning); + } } +/*--------------------------------------------------------------------------*/ +static float getMotorValue(pMotor mot, SConnection *pCon){ + float val; + + MotorGetSoftPosition(mot,pCon,&val); + return val; +} +/*--------------------------------------------------------------------------*/ +static int startTASMotor(pMotor mot, SConnection *pCon, char *name, + double target, int silent){ + float val, fixed; + int status = OKOK; + char buffer[132]; + + val = getMotorValue(mot,pCon); + MotorGetPar(mot,"fixed",&fixed); + if(ABS(fixed - 1.0) < .1) { + snprintf(buffer,131,"WARNING: %s is FIXED", name); + SCWrite(pCon,buffer,eWarning); + return OKOK; + } + if(ABS(val - target) > MOTPREC){ + status = mot->pDrivInt->SetValue(mot,pCon,(float)target); + if(status != OKOK){ + return status; + } + } + writeMotPos(pCon,silent,name,val, target); + return status; +} /*---------------------------------------------------------------------------*/ static int startMotors(ptasMot self, tasAngles angles, SConnection *pCon, int driveQ, int driveTilt){ - float val; double curve; - int status; + int status, silent; + silent = self->math->silent; + self->math->mustRecalculate = 1; /* monochromator */ - val = self->math->motors[A1]->pDrivInt->GetValue(self->math->motors[A1],pCon); - if(ABS(val - angles.monochromator_two_theta/2.) > MOTPREC){ - status = self->math->motors[A1]->pDrivInt->SetValue(self->math->motors[A1], - pCon, - angles.monochromator_two_theta/2.); - if(status != OKOK){ - return status; - } + status = startTASMotor(self->math->motors[A1],pCon, "a1", + angles.monochromator_two_theta/2.,silent); + if(status != OKOK){ + return status; } - writeMotPos(pCon,"a1",val, angles.monochromator_two_theta/2.); - - val = self->math->motors[A2]->pDrivInt->GetValue(self->math->motors[A2],pCon); - if(ABS(val - angles.monochromator_two_theta) > MOTPREC){ - status = self->math->motors[A2]->pDrivInt->SetValue(self->math->motors[A2], - pCon, - angles.monochromator_two_theta); - if(status != OKOK){ - return status; - } + status = startTASMotor(self->math->motors[A2],pCon, "a2", + angles.monochromator_two_theta,silent); + if(status != OKOK){ + return status; } - writeMotPos(pCon,"a2",val, angles.monochromator_two_theta); - + if(self->math->motors[MCV] != NULL){ curve = maCalcVerticalCurvature(self->math->machine.monochromator, angles.monochromator_two_theta); - val = self->math->motors[MCV]->pDrivInt->GetValue(self->math->motors[MCV],pCon); - if(ABS(val - curve) > MOTPREC){ - status = self->math->motors[MCV]->pDrivInt->SetValue(self->math->motors[MCV], - pCon, - curve); - if(status != OKOK){ - return status; - } + status = startTASMotor(self->math->motors[MCV],pCon, "mcv", + curve,silent); + if(status != OKOK){ + return status; } - writeMotPos(pCon,"mcv",val, curve); - } - + } + if(self->math->motors[MCH] != NULL){ curve = maCalcHorizontalCurvature(self->math->machine.monochromator, - angles.monochromator_two_theta); - val = self->math->motors[MCH]->pDrivInt->GetValue(self->math->motors[MCH],pCon); - if(ABS(val - curve) > MOTPREC){ - status = self->math->motors[MCH]->pDrivInt->SetValue(self->math->motors[MCH], - pCon, - curve); - if(status != OKOK){ - return status; - } + angles.monochromator_two_theta); + status = startTASMotor(self->math->motors[MCH],pCon, "mch", + curve,silent); + if(status != OKOK){ + return status; } - writeMotPos(pCon,"mch",val, curve); } + /* analyzer */ if(self->math->tasMode != ELASTIC){ - val = self->math->motors[A5]->pDrivInt->GetValue(self->math->motors[A5], - pCon); - if(ABS(val - angles.analyzer_two_theta/2.) > MOTPREC){ - status = self->math->motors[A5]->pDrivInt->SetValue(self->math->motors[A5], - pCon, - angles.analyzer_two_theta/2.); - if(status != OKOK){ - return status; - } - } - writeMotPos(pCon,self->math->motors[A5]->name, - val, angles.analyzer_two_theta/2.); - - val = self->math->motors[A6]->pDrivInt->GetValue(self->math->motors[A6],pCon); - if(ABS(val - angles.analyzer_two_theta) > MOTPREC){ - status = self->math->motors[A6]->pDrivInt->SetValue(self->math->motors[A6], - pCon, - angles.analyzer_two_theta); - if(status != OKOK){ - return status; - } - } - writeMotPos(pCon,"a6",val, angles.analyzer_two_theta); + status = startTASMotor(self->math->motors[A5],pCon, "a5", + angles.analyzer_two_theta/2.0,silent); + if(status != OKOK){ + return status; + } + status = startTASMotor(self->math->motors[A6],pCon, "a6", + angles.analyzer_two_theta,silent); + if(status != OKOK){ + return status; + } if(self->math->motors[ACV] != NULL){ curve = maCalcVerticalCurvature(self->math->machine.analyzer, angles.analyzer_two_theta); - val = self->math->motors[ACV]->pDrivInt->GetValue(self->math->motors[ACV],pCon); - if(ABS(val - curve) > MOTPREC){ - status = self->math->motors[ACV]->pDrivInt->SetValue(self->math->motors[ACV], - pCon, - curve); - if(status != OKOK){ - return status; - } - } - writeMotPos(pCon,"acv",val, curve); - } + status = startTASMotor(self->math->motors[ACV],pCon, "acv", + curve,silent); + if(status != OKOK){ + return status; + } + } if(self->math->motors[ACH] != NULL){ curve = maCalcHorizontalCurvature(self->math->machine.analyzer, angles.analyzer_two_theta); - val = self->math->motors[ACH]->pDrivInt->GetValue(self->math->motors[ACH], - pCon); - if(ABS(val - curve) > MOTPREC){ - status = self->math->motors[ACH]->pDrivInt->SetValue(self->math->motors[ACH], - pCon, - curve); - if(status != OKOK){ - return status; - } - } - } - writeMotPos(pCon,"ach",val, curve); + status = startTASMotor(self->math->motors[ACH],pCon, "ach", + curve,silent); + if(status != OKOK){ + return status; + } + } } if(driveQ == 0){ @@ -329,50 +329,28 @@ static int startMotors(ptasMot self, tasAngles angles, /* crystal */ - val = self->math->motors[A3]->pDrivInt->GetValue(self->math->motors[A3],pCon); - if(ABS(val - angles.a3) > MOTPREC){ - status = self->math->motors[A3]->pDrivInt->SetValue(self->math->motors[A3], - pCon, - angles.a3); - if(status != OKOK){ - return status; - } + status = startTASMotor(self->math->motors[A3],pCon, "a3", + angles.a3,silent); + if(status != OKOK){ + return status; } - writeMotPos(pCon,"a3",val, angles.a3); - - val = self->math->motors[A4]->pDrivInt->GetValue(self->math->motors[A4],pCon); - if(ABS(val - angles.sample_two_theta) > MOTPREC){ - status = self->math->motors[A4]->pDrivInt->SetValue(self->math->motors[A4], - pCon, - angles.sample_two_theta); - if(status != OKOK){ - return status; - } + status = startTASMotor(self->math->motors[A4],pCon, "a4", + angles.sample_two_theta,silent); + if(status != OKOK){ + return status; } - writeMotPos(pCon,"a4",val, angles.sample_two_theta); - + if(driveTilt == 1){ - val = self->math->motors[SGL]->pDrivInt->GetValue(self->math->motors[SGL],pCon); - if(ABS(val - angles.sgl) > MOTPREC){ - status = self->math->motors[SGL]->pDrivInt->SetValue(self->math->motors[SGL], - pCon, - angles.sgl); - if(status != OKOK){ - return status; - } - } - writeMotPos(pCon,"sgl",val, angles.sgl); - - val = self->math->motors[SGU]->pDrivInt->GetValue(self->math->motors[SGU],pCon); - if(ABS(val - angles.sgu) > MOTPREC){ - status = self->math->motors[SGU]->pDrivInt->SetValue(self->math->motors[SGU], - pCon, - angles.sgu); - if(status != OKOK){ - return status; - } - } - writeMotPos(pCon,"sgu",val, angles.sgu); + status = startTASMotor(self->math->motors[SGL],pCon, "sgl", + angles.sgl,silent); + if(status != OKOK){ + return status; + } + status = startTASMotor(self->math->motors[SGU],pCon, "sgu", + angles.sgu,silent); + if(status != OKOK){ + return status; + } } self->math->mustDrive = 0; return OKOK; @@ -383,17 +361,21 @@ static int checkQMotorLimits(ptasMot self, SConnection *pCon, int status, retVal = 1; char error[131]; char pBueffel[256]; + float val; - status = self->math->motors[A3]->pDrivInt->CheckLimits(self->math->motors[A3], - angles.a3, - error, - 131); - if(status != 1) { - retVal = 0; - snprintf(pBueffel,256,"ERROR: limit violation an a3: %s", error); - SCWrite(pCon,pBueffel,eError); + MotorGetPar(self->math->motors[A3],"fixed",&val); + if((int)val != 1){ + status = self->math->motors[A3]->pDrivInt->CheckLimits(self->math->motors[A3], + angles.a3, + error, + 131); + if(status != 1) { + retVal = 0; + snprintf(pBueffel,256,"ERROR: limit violation an a3: %s", error); + SCWrite(pCon,pBueffel,eError); + } } - + status = self->math->motors[A4]->pDrivInt->CheckLimits(self->math->motors[A4], angles.sample_two_theta, error, @@ -494,13 +476,23 @@ static int calculateAndDrive(ptasMot self, SConnection *pCon){ /*-----------------------------------------------------------------------------*/ static int checkMotors(ptasMot self, SConnection *pCon){ int i, status, length = 12; + int mask[12]; self->math->mustRecalculate = 1; if(self->math->tasMode == ELASTIC){ length = 8; } + memset(mask,0,12*sizeof(int)); + for(i = 0; i < length; i++){ + mask[i] = 1; + } + if(self->math->outOfPlaneAllowed != 0){ + mask[SGU] = 0; + mask[SGL] = 0; + } + for(i = 0; i < 12; i++){ - if(self->math->motors[i] != NULL){ + if(self->math->motors[i] != NULL && mask[i] != 0){ status = self->math->motors[i]->pDrivInt->CheckStatus(self->math->motors[i], pCon); if(status != HWIdle && status != OKOK){ @@ -532,117 +524,86 @@ static int startQMMotors(ptasMot self, tasAngles angles, SConnection *pCon){ float val; double curve; - int status; + int status, silent; + silent = self->math->silent; /* monochromator */ - val = self->math->motors[A1]->pDrivInt->GetValue(self->math->motors[A1],pCon); - if(ABS(val - angles.monochromator_two_theta/2.) > MOTPREC){ - status = self->math->motors[A1]->pDrivInt->SetValue(self->math->motors[A1], - pCon, - angles.monochromator_two_theta/2.); - if(status != OKOK){ - return status; - } + status = startTASMotor(self->math->motors[A1],pCon, "a1", + angles.monochromator_two_theta/2.,silent); + if(status != OKOK){ + return status; } - val = self->math->motors[A2]->pDrivInt->GetValue(self->math->motors[A2],pCon); - if(ABS(val - angles.monochromator_two_theta) > MOTPREC){ - status = self->math->motors[A2]->pDrivInt->SetValue(self->math->motors[A2], - pCon, - angles.monochromator_two_theta); - if(status != OKOK){ - return status; - } + status = startTASMotor(self->math->motors[A2],pCon, "a2", + angles.monochromator_two_theta,silent); + if(status != OKOK){ + return status; } + if(self->math->motors[MCV] != NULL){ curve = maCalcVerticalCurvature(self->math->machine.monochromator, - angles.monochromator_two_theta); - val = self->math->motors[MCV]->pDrivInt->GetValue(self->math->motors[MCV],pCon); - if(ABS(val - curve) > MOTPREC){ - status = self->math->motors[MCV]->pDrivInt->SetValue(self->math->motors[MCV], - pCon, - curve); - if(status != OKOK){ - return status; - } + angles.monochromator_two_theta); + status = startTASMotor(self->math->motors[MCV],pCon, "mcv", + curve,silent); + if(status != OKOK){ + return status; } } + if(self->math->motors[MCH] != NULL){ curve = maCalcHorizontalCurvature(self->math->machine.monochromator, - angles.monochromator_two_theta); - val = self->math->motors[MCH]->pDrivInt->GetValue(self->math->motors[MCH],pCon); - if(ABS(val - curve) > MOTPREC){ - status = self->math->motors[MCH]->pDrivInt->SetValue(self->math->motors[MCH], - pCon, - curve); - if(status != OKOK){ - return status; - } + angles.monochromator_two_theta); + status = startTASMotor(self->math->motors[MCH],pCon, "mch", + curve,silent); + if(status != OKOK){ + return status; } } + /* analyzer */ - val = self->math->motors[A5]->pDrivInt->GetValue(self->math->motors[A5],pCon); - if(ABS(val - angles.analyzer_two_theta/2.) > MOTPREC){ - status = self->math->motors[A5]->pDrivInt->SetValue(self->math->motors[A5], - pCon, - angles.analyzer_two_theta/2.); + status = startTASMotor(self->math->motors[A5],pCon, "a5", + angles.analyzer_two_theta/2.0,silent); if(status != OKOK){ - return status; + return status; } - } - val = self->math->motors[A6]->pDrivInt->GetValue(self->math->motors[A6],pCon); - if(ABS(val - angles.analyzer_two_theta) > MOTPREC){ - status = self->math->motors[A6]->pDrivInt->SetValue(self->math->motors[A6], - pCon, - angles.analyzer_two_theta); + status = startTASMotor(self->math->motors[A6],pCon, "a6", + angles.analyzer_two_theta,silent); if(status != OKOK){ - return status; + return status; } - } - if(self->math->motors[ACV] != NULL){ - curve = maCalcVerticalCurvature(self->math->machine.analyzer, - angles.analyzer_two_theta); - val = self->math->motors[ACV]->pDrivInt->GetValue(self->math->motors[ACV],pCon); - if(ABS(val - curve) > MOTPREC){ - status = self->math->motors[ACV]->pDrivInt->SetValue(self->math->motors[ACV], - pCon, - curve); - if(status != OKOK){ - return status; - } + + if(self->math->motors[ACV] != NULL){ + curve = maCalcVerticalCurvature(self->math->machine.analyzer, + angles.analyzer_two_theta); + status = startTASMotor(self->math->motors[ACV],pCon, "acv", + curve,silent); + if(status != OKOK){ + return status; + } } - } - if(self->math->motors[ACH] != NULL){ - curve = maCalcHorizontalCurvature(self->math->machine.analyzer, - angles.analyzer_two_theta); - val = self->math->motors[ACH]->pDrivInt->GetValue(self->math->motors[ACH],pCon); - if(ABS(val - curve) > MOTPREC){ - status = self->math->motors[ACH]->pDrivInt->SetValue(self->math->motors[ACH], - pCon, - curve); - if(status != OKOK){ - return status; - } + if(self->math->motors[ACH] != NULL){ + curve = maCalcHorizontalCurvature(self->math->machine.analyzer, + angles.analyzer_two_theta); + status = startTASMotor(self->math->motors[ACH],pCon, "ach", + curve,silent); + if(status != OKOK){ + return status; + } } - } /* crystal */ - val = self->math->motors[A4]->pDrivInt->GetValue(self->math->motors[A4],pCon); - if(ABS(val - angles.sample_two_theta) > MOTPREC){ - status = self->math->motors[A4]->pDrivInt->SetValue(self->math->motors[A4], - pCon, - angles.sample_two_theta); - if(status != OKOK){ - return status; - } + status = startTASMotor(self->math->motors[A4],pCon, "a4", + angles.sample_two_theta,silent); + if(status != OKOK){ + return status; } - + self->math->mustDrive = 0; return OKOK; } diff --git a/task.c b/task.c index 235917bc..74ff28b4 100644 --- a/task.c +++ b/task.c @@ -243,7 +243,8 @@ IncrTaskPointer(self); while(self->iStop == 0) { - if((self->pCurrent != pEnd) && (self->pCurrent->iStatus == READY)) + if((self->pCurrent != pEnd) && (self->pCurrent->iStatus == READY) + && self->pCurrent != NULL) /* omit ourselves! */ { iRet = self->pCurrent->pRun(self->pCurrent->pData); diff --git a/tasscanub.c b/tasscanub.c index 644f7828..e8b8e24d 100644 --- a/tasscanub.c +++ b/tasscanub.c @@ -142,16 +142,7 @@ static float readDrivable(char *val, SConnection *pCon){ float fVal; /* - if motor: read motor - */ - pMot = FindMotor(pServ->pSics,val); - if(pMot != NULL){ - MotorGetSoftPosition(pMot,pCon,&fVal); - return fVal; - } - - /* - else: read general drivable + read general drivable */ pCom = FindCommand(pServ->pSics,val); if(pCom != NULL){ @@ -197,6 +188,8 @@ static int TASUBHeader(pScanData self) int fx; tasReflection r; double kfix; + pSicsVariable sVar = NULL; + float f1, f2, f3, f4; assert(self); assert(pTAS); @@ -345,6 +338,57 @@ static int TASUBHeader(pScanData self) r.qe.qk, r.qe.ql); + /* + * write mupad stuff if available + */ + sVar = FindCommandData(pServ->pSics,"w1","SicsVariable"); + if(sVar != NULL) + { + f1 = sVar->fVal; + sVar = FindCommandData(pServ->pSics,"w2","SicsVariable"); + if(sVar != NULL) + { + f2 = sVar->fVal; + } + sVar = FindCommandData(pServ->pSics,"w3","SicsVariable"); + if(sVar != NULL) + { + f3 = sVar->fVal; + } + sVar = FindCommandData(pServ->pSics,"w4","SicsVariable"); + if(sVar != NULL) + { + f4 = sVar->fVal; + } + fprintf(self->fd,"PARAM: W1=%8.4f, W2=%8.4f, W3=%8.4f, W4=%8.4f\n", + f1, f2, f3, f4); + + sVar = FindCommandData(pServ->pSics,"p1","SicsVariable"); + if(sVar != NULL) + { + f1 = sVar->fVal; + } + sVar = FindCommandData(pServ->pSics,"p2","SicsVariable"); + if(sVar != NULL) + { + f2 = sVar->fVal; + } + sVar = FindCommandData(pServ->pSics,"p3","SicsVariable"); + if(sVar != NULL) + { + f3 = sVar->fVal; + } + sVar = FindCommandData(pServ->pSics,"p4","SicsVariable"); + if(sVar != NULL) + { + f4 = sVar->fVal; + } + fprintf(self->fd,"PARAM: P1=%8.4f, P2=%8.4f, P3=%8.4f, P4=%8.4f\n", + f1, f2, f3, f4); + + + } + /* write currents if in polarisation mode */ @@ -639,6 +683,7 @@ static int TASUBScanDrive(pScanData self, int iPoint) int iTAS = 0; pMotor pMot; + pTAS->ub->silent = 1; /* loop through all the scan variables */ @@ -668,6 +713,7 @@ static int TASUBScanDrive(pScanData self, int iPoint) else { status = Wait4Success(GetExecutor()); + pTAS->ub->silent = 0; } return 1; } @@ -776,6 +822,8 @@ static int TASUBScanCount(pScanData self, int iPoint) /* loop over all scan variables */ status = 1; + + pTAS->ub->silent = 0; for(i = 0; i < self->iScanVar; i++) { DynarGet(self->pScanVar,i,&pDings); @@ -975,7 +1023,7 @@ int TASUBPrepare(pScanData self) /*---------------------------------------------------------------------*/ static void TASUBDump(pTASdata self, SicsInterp *pSics, SConnection *pCon, int argc, char *argv[]){ - float v[3], ub[3][3], cell[6]; + float v[9], ub[3][3], cell[6]; int status, i, j; pNXScript nxscript = NULL; char pBueffel[256]; @@ -1035,7 +1083,13 @@ static void TASUBDump(pTASdata self, SicsInterp *pSics, SConnection *pCon, v[0] = r.qe.qh; v[1] = r.qe.qk; v[2] = r.qe.ql; - status = NXDputalias(nxscript->fileHandle,nxscript->dictHandle,pBueffel,v); + v[3] = r.angles.a3; + v[4] = r.angles.sample_two_theta; + v[5] = r.angles.sgl; + v[6] = r.angles.sgu; + v[7] = KtoEnergy(r.qe.ki); + v[8] = KtoEnergy(r.qe.kf); + status = NXDputalias(nxscript->fileHandle,nxscript->dictHandle,pBueffel,v); if(status != NX_OK){ snprintf(pBueffel,255,"ERROR: failed to write plane vector 1 to %s_vec1",argv[3]); SCWrite(pCon,pBueffel,eWarning); @@ -1045,6 +1099,12 @@ static void TASUBDump(pTASdata self, SicsInterp *pSics, SConnection *pCon, v[0] = r.qe.qh; v[1] = r.qe.qk; v[2] = r.qe.ql; + v[3] = r.angles.a3; + v[4] = r.angles.sample_two_theta; + v[5] = r.angles.sgl; + v[6] = r.angles.sgu; + v[7] = KtoEnergy(r.qe.ki); + v[8] = KtoEnergy(r.qe.kf); status = NXDputalias(nxscript->fileHandle,nxscript->dictHandle,pBueffel,v); if(status != NX_OK){ snprintf(pBueffel,255,"ERROR: failed to write plane vector 2 to %s_vec2",argv[3]); diff --git a/tasub.c b/tasub.c index b04967b7..7822128d 100644 --- a/tasub.c +++ b/tasub.c @@ -9,6 +9,7 @@ #include #include "sics.h" #include "lld.h" +#include "trigd.h" #include "tasub.h" #include "tasdrive.h" /*------------------- motor indexes in motor data structure ---------*/ @@ -226,6 +227,13 @@ static int testMotor(ptasUB pNew, SConnection *pCon, char *name, int idx){ return 1; } } +/*-------------------------------------------------------------------*/ +static void updateTargets(ptasUB pNew, SConnection *pCon){ + tasAngles ang; + + readTASAngles(pNew,pCon,&ang); + calcTasQEPosition(&pNew->machine, ang, &pNew->target); +} /*--------------------------------------------------------------------*/ int TasUBFactory(SConnection *pCon,SicsInterp *pSics, void *pData, int argc, char *argv[]){ @@ -241,6 +249,10 @@ int TasUBFactory(SConnection *pCon,SicsInterp *pSics, void *pData, SCWrite(pCon,"ERROR: need name to install tasUB",eError); return 0; } + if(argc > 2 && argc < 14){ + SCWrite(pCon,"ERROR: not enough motor names specified for MakeTasUB",eError); + return 0; + } pNew = MakeTasUB(); if(pNew == NULL){ SCWrite(pCon,"ERROR: out of memory creating tasUB",eError); @@ -250,19 +262,39 @@ int TasUBFactory(SConnection *pCon,SicsInterp *pSics, void *pData, /* assign motors */ - pNew->motors[0] = FindMotor(pSics,"a1"); - pNew->motors[1] = FindMotor(pSics,"a2"); - pNew->motors[2] = FindMotor(pSics,"mcv"); - pNew->motors[3] = FindMotor(pSics,"mch"); - pNew->motors[4] = FindMotor(pSics,"a3"); - pNew->motors[5] = FindMotor(pSics,"a4"); - pNew->motors[6] = FindMotor(pSics,"sgu"); - pNew->motors[7] = FindMotor(pSics,"sgl"); - pNew->motors[8] = FindMotor(pSics,"a5"); - pNew->motors[9] = FindMotor(pSics,"a6"); - pNew->motors[10] = FindMotor(pSics,"acv"); - pNew->motors[11] = FindMotor(pSics,"ach"); - + if(argc < 14){ + /* + * default names and assignement + */ + pNew->motors[0] = FindMotor(pSics,"a1"); + pNew->motors[1] = FindMotor(pSics,"a2"); + pNew->motors[2] = FindMotor(pSics,"mcv"); + pNew->motors[3] = FindMotor(pSics,"mch"); + pNew->motors[4] = FindMotor(pSics,"a3"); + pNew->motors[5] = FindMotor(pSics,"a4"); + pNew->motors[6] = FindMotor(pSics,"sgu"); + pNew->motors[7] = FindMotor(pSics,"sgl"); + pNew->motors[8] = FindMotor(pSics,"a5"); + pNew->motors[9] = FindMotor(pSics,"a6"); + pNew->motors[10] = FindMotor(pSics,"acv"); + pNew->motors[11] = FindMotor(pSics,"ach"); + } else { + /* + * user defined names + */ + pNew->motors[0] = FindMotor(pSics,argv[2]); + pNew->motors[1] = FindMotor(pSics,argv[3]); + pNew->motors[2] = FindMotor(pSics,argv[4]); + pNew->motors[3] = FindMotor(pSics,argv[5]); + pNew->motors[4] = FindMotor(pSics,argv[6]); + pNew->motors[5] = FindMotor(pSics,argv[7]); + pNew->motors[6] = FindMotor(pSics,argv[8]); + pNew->motors[7] = FindMotor(pSics,argv[9]); + pNew->motors[8] = FindMotor(pSics,argv[10]); + pNew->motors[9] = FindMotor(pSics,argv[11]); + pNew->motors[10] = FindMotor(pSics,argv[12]); + pNew->motors[11] = FindMotor(pSics,argv[13]); + } /* curvature motors may be missing, anything else is a serious problem */ @@ -275,9 +307,10 @@ int TasUBFactory(SConnection *pCon,SicsInterp *pSics, void *pData, status += testMotor(pNew, pCon,"a5",A5); status += testMotor(pNew, pCon,"a6",A6); if(status != 8){ + SCWrite(pCon,"ERROR: a required motor is mssing, tasub NOT installed",eError); return 0; } - + status = AddCommand(pSics,argv[1], TasUBWrapper, KillTasUB, @@ -389,7 +422,7 @@ static int getCrystalParameters(pmaCrystal crystal, SConnection *pCon, SCWrite(pCon,pBueffel,eValue); return 1; }else if(strcmp(argv[2],"vb2") == 0){ - snprintf(pBueffel,131,"%s.%s.vb2 = %f",argv[0],argv[1],crystal->VB1); + snprintf(pBueffel,131,"%s.%s.vb2 = %f",argv[0],argv[1],crystal->VB2); SCWrite(pCon,pBueffel,eValue); return 1; }else if(strcmp(argv[2],"ss") == 0){ @@ -849,9 +882,175 @@ static void listDiagnostik(ptasUB self, SConnection *pCon){ status = LLDnodePtr2Next(self->reflectionList); } } +/*-----------------------------------------------------------------*/ +static int addAuxReflection(ptasUB self, SConnection *pCon, + SicsInterp *pSics, int argc, char *argv[]){ + int status; + tasReflection r1, r2; + float value = -999.99; + char pBueffel[256]; + MATRIX UB = NULL, B = NULL; + + if(argc < 5){ + SCWrite(pCon, + "ERROR: not enough arguments auxiliary reflection, need HKL", + eError); + return 0; + } + + if(!SCMatchRights(pCon,usUser)){ + return 0; + } + + status = Tcl_GetDouble(InterpGetTcl(pSics),argv[2],&r2.qe.qh); + if(status != TCL_OK){ + snprintf(pBueffel,255,"ERROR: failed to convert %s to number",argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + status = Tcl_GetDouble(InterpGetTcl(pSics),argv[3],&r2.qe.qk); + if(status != TCL_OK){ + snprintf(pBueffel,255,"ERROR: failed to convert %s to number",argv[3]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + status = Tcl_GetDouble(InterpGetTcl(pSics),argv[4],&r2.qe.ql); + if(status != TCL_OK){ + snprintf(pBueffel,255,"ERROR: failed to convert %s to number",argv[4]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + + B = mat_creat(3,3,ZERO_MATRIX); + if(B == NULL){ + SCWrite(pCon,"ERROR: out of memory creating B matrix",eError); + return 0; + } + status = calculateBMatrix(self->cell,B); + if(status < 0){ + SCWrite(pCon,"ERROR: bad cell constants, no volume",eError); + mat_free(B); + return 0; + } + + status = findReflection(self->reflectionList, 0,&r1); + if(status != 1){ + r2.qe.kf = self->current.kf; + r2.qe.ki = self->current.ki; + MotorGetSoftPosition(self->motors[A3],pCon,&value); + r2.angles.a3 = value + 180.; + r2.angles.sgu = .0; + r2.angles.sgl = .0; + calcTwoTheta(B,r2.qe,self->machine.ss_sample,&r2.angles.sample_two_theta); + r1 = r2; + } + + status = makeAuxReflection(B, r1, &r2,self->machine.ss_sample); + mat_free(B); + if(status < 0){ + SCWrite(pCon,"ERROR: out of memory in makeAuxUB or scattering angle not closed", + eError); + return 0; + } + LLDnodeAppend(self->reflectionList,&r2); + SCSendOK(pCon); + return 1; +} +/*------------------------------------------------------------------*/ +static int calcAuxUB(ptasUB self, SConnection *pCon, SicsInterp *pSics, + int argc, char *argv[]){ + int status; + tasReflection r1, r2; + char pBueffel[256]; + MATRIX UB = NULL, B = NULL; + + if(argc < 5){ + SCWrite(pCon, + "ERROR: not enough arguments for UB calculation, need HKL of second plane vector", + eError); + return 0; + } + + if(!SCMatchRights(pCon,usUser)){ + return 0; + } + + status = Tcl_GetDouble(InterpGetTcl(pSics),argv[2],&r2.qe.qh); + if(status != TCL_OK){ + snprintf(pBueffel,255,"ERROR: failed to convert %s to number",argv[2]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + status = Tcl_GetDouble(InterpGetTcl(pSics),argv[3],&r2.qe.qk); + if(status != TCL_OK){ + snprintf(pBueffel,255,"ERROR: failed to convert %s to number",argv[3]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + status = Tcl_GetDouble(InterpGetTcl(pSics),argv[4],&r2.qe.ql); + if(status != TCL_OK){ + snprintf(pBueffel,255,"ERROR: failed to convert %s to number",argv[4]); + SCWrite(pCon,pBueffel,eError); + return 0; + } + + status = findReflection(self->reflectionList, 0,&r1); + if(status != 1){ + snprintf(pBueffel,255,"ERROR: cannot find first reflection"); + SCWrite(pCon,pBueffel,eError); + return 0; + } + B = mat_creat(3,3,ZERO_MATRIX); + if(B == NULL){ + SCWrite(pCon,"ERROR: out of memory creating B matrix",eError); + return 0; + } + status = calculateBMatrix(self->cell,B); + if(status < 0){ + SCWrite(pCon,"ERROR: bad cell constants, no volume",eError); + mat_free(B); + return 0; + } + + status = makeAuxReflection(B, r1, &r2,self->machine.ss_sample); + mat_free(B); + if(status < 0){ + SCWrite(pCon,"ERROR: out of memory in makeAuxUB",eError); + return 0; + } + + UB = calcTasUBFromTwoReflections(self->cell,r1,r2,&status); + if(UB == NULL){ + switch(status){ + case UBNOMEMORY: + SCWrite(pCon,"ERROR: out of memory calculating UB matrix",eError); + break; + case REC_NO_VOLUME: + SCWrite(pCon,"ERROR: bad cell constants, no volume",eError); + break; + } + return 0; + } + if(mat_det(UB) < .000001){ + SCWrite(pCon,"ERROR: invalid UB matrix, check reflections",eError); + return 0; + } + if(self->machine.UB != NULL){ + mat_free(self->machine.UB); + } + if(self->machine.planeNormal != NULL){ + mat_free(self->machine.planeNormal); + } + self->machine.UB = UB; + self->machine.planeNormal = calcPlaneNormal(r1,r2); + self->ubValid = 1; + SCparChange(pCon); + SCSendOK(pCon); + return 1; +} /*------------------------------------------------------------------*/ static int calcUB(ptasUB self, SConnection *pCon, SicsInterp *pSics, - int argc, char *argv[]){ + int argc, char *argv[]){ int idx1, idx2, status; tasReflection r1, r2; char pBueffel[256]; @@ -859,8 +1058,8 @@ static int calcUB(ptasUB self, SConnection *pCon, SicsInterp *pSics, if(argc < 4){ SCWrite(pCon, - "ERROR: not enough arguments for UB calculation, need index of two reflections", - eError); + "ERROR: not enough arguments for UB calculation, need index of two reflections", + eError); return 0; } @@ -927,6 +1126,54 @@ static int calcUB(ptasUB self, SConnection *pCon, SicsInterp *pSics, SCparChange(pCon); return 1; } +/*-----------------------------------------------------------------*/ +static int calcUBFromCell(ptasUB self, SConnection *pCon){ + MATRIX B, U, UB; + tasReflection r1; + int status; + + B = mat_creat(3,3,UNIT_MATRIX); + U = mat_creat(3,3,UNIT_MATRIX); + status = findReflection(self->reflectionList, 0,&r1); + if(status == 1) { + /* + U[0][0] = Cosd(r1.angles.a3); + U[0][1] = -Sind(r1.angles.a3); + U[1][0] = Sind(r1.angles.a3); + U[1][1] = Cosd(r1.angles.a3); + */ + } + if(B == NULL || U == NULL){ + SCWrite(pCon,"ERROR: out of memory in calcUBFromCell",eError); + return 0; + } + status = calculateBMatrix(self->cell,B); + if(status == REC_NO_VOLUME){ + SCWrite(pCon,"ERROR: cell has no volume",eError); + return 0; + } + UB = mat_mul(U,B); + if(UB == NULL){ + SCWrite(pCon,"ERROR: matrix multiplication failed",eError); + return 0; + } + if(mat_det(UB) < .000001){ + SCWrite(pCon,"ERROR: invalid UB matrix, check cell",eError); + return 0; + } + if(self->machine.UB != NULL){ + mat_free(self->machine.UB); + } + self->machine.UB = UB; + self->machine.planeNormal[0][0] = .0; + self->machine.planeNormal[1][0] = .0; + self->machine.planeNormal[2][0] = 1.; + self->ubValid = 1; + SCparChange(pCon); + mat_free(U); + mat_free(B); + return 1; +} /*------------------------------------------------------------------*/ static int calcRefAngles(ptasUB self, SConnection *pCon, SicsInterp *pSics, @@ -1198,6 +1445,21 @@ static int setUB(SConnection *pCon, SicsInterp *pSics, ptasUB self, return 1; } /*------------------------------------------------------------------*/ +static int getUB(SConnection *pCon, SicsInterp *pSics, ptasUB self, + int argc, char *argv[]){ + double value; + char pBueffel[512]; + int status; + + snprintf(pBueffel,511,"tasub.ub = %f %f %f %f %f %f %f %f %f", + self->machine.UB[0][0], self->machine.UB[0][1], self->machine.UB[0][2], + self->machine.UB[1][0], self->machine.UB[1][1], self->machine.UB[1][2], + self->machine.UB[2][0], self->machine.UB[2][1], self->machine.UB[2][2]); + SCWrite(pCon,pBueffel,eValue); + + return 1; +} +/*------------------------------------------------------------------*/ static int setNormal(SConnection *pCon, SicsInterp *pSics, ptasUB self, int argc, char *argv[]){ double value; @@ -1370,9 +1632,13 @@ int TasUBWrapper(SConnection *pCon,SicsInterp *pSics, void *pData, strtolower(argv[1]); if(strcmp(argv[1],"mono") == 0){ - return handleCrystalCommands(&self->machine.monochromator,pCon,argc,argv); + status = handleCrystalCommands(&self->machine.monochromator,pCon,argc,argv); + self->mustRecalculate = 1; + return status; } else if(strcmp(argv[1],"ana") == 0){ - return handleCrystalCommands(&self->machine.analyzer,pCon,argc,argv); + status = handleCrystalCommands(&self->machine.analyzer,pCon,argc,argv); + self->mustRecalculate = 1; + return status; }else if(strcmp(argv[1],"cell") == 0){ if(argc > 2){ return tasReadCell(pCon,self,argc,argv); @@ -1396,12 +1662,20 @@ int TasUBWrapper(SConnection *pCon,SicsInterp *pSics, void *pData, return 1; } else if(strcmp(argv[1],"makeub") == 0){ return calcUB(self,pCon,pSics,argc,argv); + } else if(strcmp(argv[1],"makeauxub") == 0){ + return calcAuxUB(self,pCon,pSics,argc,argv); + } else if(strcmp(argv[1],"addauxref") == 0){ + return addAuxReflection(self,pCon,pSics,argc,argv); + } else if(strcmp(argv[1],"makeubfromcell") == 0){ + return calcUBFromCell(self,pCon); } else if(strcmp(argv[1],"calcang") == 0){ return calcRefAngles(self,pCon,pSics,argc,argv); } else if(strcmp(argv[1],"calcqe") == 0){ return calcQFromAngles(self,pCon,pSics,argc,argv); } else if(strcmp(argv[1],"setub") == 0){ return setUB(pCon,pSics,self,argc,argv); + } else if(strcmp(argv[1],"getub") == 0){ + return getUB(pCon,pSics,self,argc,argv); } else if(strcmp(argv[1],"setnormal") == 0){ return setNormal(pCon,pSics,self,argc,argv); } else if(strcmp(argv[1],"settarget") == 0){ @@ -1414,6 +1688,10 @@ int TasUBWrapper(SConnection *pCon,SicsInterp *pSics, void *pData, return readReflection(pCon,pSics,&self->r1,argc,argv); } else if(strcmp(argv[1],"r2") == 0){ return readReflection(pCon,pSics,&self->r2,argc,argv); + } else if(strcmp(argv[1],"updatetargets") == 0){ + updateTargets(self,pCon); + SCSendOK(pCon); + return 1; } else if(strcmp(argv[1],"const") == 0){ if(argc > 2){ strtolower(argv[2]); @@ -1489,6 +1767,25 @@ int TasUBWrapper(SConnection *pCon,SicsInterp *pSics, void *pData, SCWrite(pCon,pBueffel,eValue); return 1; } + } else if(strcmp(argv[1],"silent") == 0){ + if(argc > 2){ + strtolower(argv[2]); + if(!SCMatchRights(pCon,usUser)){ + return 0; + } + status = Tcl_GetInt(InterpGetTcl(pSics),argv[2],&newSS); + if(status != TCL_OK){ + SCWrite(pCon,"ERROR: failed to convert argument to number",eError); + return 0; + } + self->silent = newSS; + SCSendOK(pCon); + return 1; + } else { + snprintf(pBueffel,131,"%s.silent = %d",argv[0],self->silent); + SCWrite(pCon,pBueffel,eValue); + return 1; + } } else { snprintf(pBueffel,131,"ERROR: subcommand %s to %s not defined",argv[1], argv[0]); diff --git a/tasub.h b/tasub.h index ac97a2de..1d22c3ab 100644 --- a/tasub.h +++ b/tasub.h @@ -30,6 +30,7 @@ pMotor motors[12]; tasReflection r1, r2; int ubValid; + int silent; }tasUB, *ptasUB; diff --git a/tasub.w b/tasub.w index afd15d07..a74ba7f1 100644 --- a/tasub.w +++ b/tasub.w @@ -32,6 +32,7 @@ A data structure: pMotor motors[12]; tasReflection r1, r2; int ubValid; + int silent; }tasUB, *ptasUB; @} \begin{description} @@ -55,6 +56,9 @@ A data structure: runtime. \item[r1,r2] The indexs of the reflections used for calculating the orientation matrix. +\item[ubValid] a flag denoting if the UB is valid. +\item[silent] A flga which when 1 suppresses the printing of motor positions + when driving. Usefule for scans. \end{description} For the virtual motors, there must be a data structure, too: diff --git a/tasublib.c b/tasublib.c index 4d8a45f3..eee5a650 100644 --- a/tasublib.c +++ b/tasublib.c @@ -139,6 +139,38 @@ static double calcTheta(double ki, double kf, double two_theta){ return rtan(ABS(ki) - ABS(kf)*Cosd(two_theta), ABS(kf)*Sind(two_theta))/DEGREE_RAD; } +/*-------------------------------------------------------------------------*/ +double tasAngleBetweenReflections(MATRIX B, tasReflection r1, tasReflection r2){ + MATRIX chi1, chi2, h1, h2; + double angle; + + h1 = makeVector(); + if(h1 == NULL){ + return -9999.99; + } + h1[0][0] = r1.qe.qh; + h1[1][0] = r1.qe.qk; + h1[2][0] = r1.qe.ql; + + h2 = makeVector(); + if(h2 == NULL){ + return -999.99; + } + h2[0][0] = r2.qe.qh; + h2[1][0] = r2.qe.qk; + h2[2][0] = r2.qe.ql; + + chi1 = mat_mul(B,h1); + chi2 = mat_mul(B,h2); + if(chi1 != NULL && chi2 != NULL){ + angle = angleBetween(chi1,chi2); + killVector(chi1); + killVector(chi2); + } + killVector(h1); + killVector(h2); + return angle; +} /*--------------------------------------------------------------------*/ static MATRIX uFromAngles(double om, double sgu, double sgl){ MATRIX u; @@ -161,6 +193,82 @@ static MATRIX calcTasUVectorFromAngles(tasReflection r){ om = r.angles.a3 - theta; return uFromAngles(om,r.angles.sgu, r.angles.sgl); } +/*-----------------------------------------------------------------------------*/ +static MATRIX tasReflectionToQC(tasQEPosition r, MATRIX UB){ + MATRIX Q, QC; + + Q = makeVector(); + if(Q == NULL){ + return NULL; + } + vectorSet(Q,0,r.qh); + vectorSet(Q,1,r.qk); + vectorSet(Q,2,r.ql); + QC = mat_mul(UB,Q); + killVector(Q); + return QC; +} +/*-----------------------------------------------------------------*/ +int makeAuxReflection(MATRIX B, tasReflection r1, tasReflection *r2, + int ss){ + double theta, om, q, cos2t; + MATRIX QC; + + r2->angles = r1.angles; + r2->qe.ki = r1.qe.ki; + r2->qe.kf= r1.qe.kf; + + theta = calcTheta(r1.qe.ki,r1.qe.kf,r1.angles.sample_two_theta); + om = r1.angles.a3 - theta; + om += tasAngleBetweenReflections(B,r1,*r2); + + QC = tasReflectionToHC(r2->qe,B); + if(QC == NULL){ + return UBNOMEMORY; + } + + q = vectorLength(QC); + q = 2.*PI*vectorLength(QC); + cos2t = (r1.qe.ki*r1.qe.ki + r1.qe.kf*r1.qe.kf - q*q)/ + (2. * ABS(r1.qe.ki) * ABS(r1.qe.kf)); + if(ABS(cos2t) > 1.){ + killVector(QC); + return TRIANGLENOTCLOSED; + } + r2->angles.sample_two_theta = ss*Acosd(cos2t); + theta = calcTheta(r1.qe.ki,r1.qe.kf,r2->angles.sample_two_theta); + r2->angles.a3 = om + theta; + r2->angles.a3 -= 180.; + if(r2->angles.a3 < -180.){ + r2->angles.a3 += 360.; + } + mat_free(QC); + + return 1; +} +/*------------------------------------------------------------------*/ +int calcTwoTheta(MATRIX B, tasQEPosition ref, int ss, double *value){ + MATRIX QC; + double cos2t, q; + + QC = tasReflectionToHC(ref,B); + if(QC == NULL){ + return UBNOMEMORY; + } + + q = vectorLength(QC); + q = 2.*PI*vectorLength(QC); + killVector(QC); + + cos2t = (ref.ki*ref.ki + ref.kf*ref.kf - q*q)/ + (2. * ABS(ref.ki) * ABS(ref.kf)); + if(ABS(cos2t) > 1.){ + return TRIANGLENOTCLOSED; + } + *value = ss*Acosd(cos2t); + + return 1; +} /*-------------------------------------------------------------------*/ MATRIX calcPlaneNormal(tasReflection r1, tasReflection r2){ MATRIX u1 = NULL, u2 = NULL, planeNormal = NULL; @@ -326,21 +434,6 @@ static MATRIX buildTVMatrix(MATRIX U1V, MATRIX U2V){ killVector(T3V); return T; } -/*-----------------------------------------------------------------------------*/ -static MATRIX tasReflectionToQC(tasQEPosition r, MATRIX UB){ - MATRIX Q, QC; - - Q = makeVector(); - if(Q == NULL){ - return NULL; - } - vectorSet(Q,0,r.qh); - vectorSet(Q,1,r.qk); - vectorSet(Q,2,r.ql); - QC = mat_mul(UB,Q); - killVector(Q); - return QC; -} /*----------------------------------------------------------------------------*/ static MATRIX buildRMatrix(MATRIX UB, MATRIX planeNormal, tasQEPosition qe, int *errorCode){ diff --git a/tasublib.h b/tasublib.h index 1bbfe463..fa8ee5bf 100644 --- a/tasublib.h +++ b/tasublib.h @@ -124,6 +124,28 @@ double maCalcHorizontalCurvature(maCrystal data, double two_theta); */ double maCalcK(maCrystal data, double two_theta); /*======================= reciprocal space =============================*/ +/** + * make an auxiliary reflection which has the same sgu and sgl as r1, but + * an omega which is adjusted to the angle difference between r1 and + * the target. This is useful for generating an auxilairy UB during + * alignment. + * @param B The B matrix + * @param r1 The first reflection + * @param r2 a pointer to the second reflection, QH, QK, QL initialized + * @param ss The scattering sense at the sample + * @return 1 on success, a negative error code on failure. + */ +int makeAuxReflection(MATRIX B, tasReflection r1, + tasReflection *r2, int ss); +/** + * calculate two theta for the reflection ref + * @param B the metric matrix, or the UB + * @param ref The reflection for which to calculate two theta + * @param ss The scattering sense + * @param twoTheta The new two theta value (output) + * @return a negative error code on failure, 1 on success + */ +int calcTwoTheta(MATRIX B, tasQEPosition ref, int ss, double *twoTheta); /** * calculate a UB from two reflections and the cell. * @param cell The lattice constant of the crystal diff --git a/tcl/analyzedevexeclog b/tcl/analyzedevexeclog new file mode 100755 index 00000000..d6efdbb3 --- /dev/null +++ b/tcl/analyzedevexeclog @@ -0,0 +1,274 @@ +#!/usr/bin/tclsh +#----------------------------------------------------------------------------- +# This program analyses a devexec log as written by SICS. It should produce +# a list of devices together with the time each device was active +# in seconds. +# +# Mark Koennecke, January 2007 +#---------------------------------------------------------------------------- +# Some utility routines for processing an entry in the devexeclog. A line +# has the form: +# DEVEXEC:OP:DEVICE:SECONDS:NANOSECONDS +# This is split into a list and accessor function are provided for various +# items +#--------------------------------------------------------------------------- +proc parseLogLine {line} { + set l [split $line :] + set tst [lindex $l 0] + if {[string compare $tst DEVEXEC] != 0} { + error "Bad log line: $line" + } + return [lrange $l 1 end] +} +#-------------------------------------------------------------------------- +proc getLogOp {logList} { + return [lindex $logList 0] +} +#-------------------------------------------------------------------------- +proc getLogDevice {logList} { + return [lindex $logList 1] +} +#-------------------------------------------------------------------------- +proc getLogSeconds {logList} { + return [lindex $logList 2] +} +#-------------------------------------------------------------------------- +proc getLogNanos {logList} { + return [lindex $logList 3] +} +#------------------------------------------------------------------------- +proc getStamp {logList} { + return [lrange $logList 2 end] +} +#========================================================================== +proc calcTimeDiff {sec1 nano1 sec2 nano2} { + set secSum 0 + set nanoSum 0 + if {$sec2 > $sec1} { + set nanoSum [expr 1000000 - $nano1] + set secSum [expr $sec2 - $sec1 + 1] + set nanoSum [expr $nanoSum + $nano2] + } elseif {$sec2 == $sec1} { + set secSum 0 + set nanoSum [expr $nano2 - $nano1] + } else { + error "Bad time order: sec2 should be bigger then sec1" + } + return [list $secSum $nanoSum] +} +#========================================================================= +# There are two arrays: +# One is called devices and holds the device name and the total number +# of seconds this device has run. There are special devices: +# - nobeam for couting NOBEAM time. This has later to be subtracted from +# counting times. +# - unallocated time which can not be clearly given to some device +# This might happen if the SICServer restarts whilst something is +# running. +# +# The other one is running and holds all the devices which are currently +# being run. For each such device a list will be held with seconds +# and nanos. At each Start and stop, time differences to the previous +# event will be calculated and added to the devices running. If more then +# one device is running at a given time, the time will be distributed +# equally to all devices. +# +# There is also a counter for devices which are currently running. +# +# This section now provides helper functions for dealing with these +# arrays +#======================================================================== +set devRun 0 +set devices(nobeam) 0 +set devices(unaccounted) 0 +set sicsRestart 0 +#------------------------------------------------------------------------ +proc addToDevice {dev sec nano} { + upvar #0 devices devices + set totalSec [expr double($sec) + double($nano)/1000000.0] + if {[info exists devices($dev)] } { + set devices($dev) [expr $devices($dev) + $totalSec] + } else { + set devices($dev) $totalSec + } +} +#------------------------------------------------------------------------ +proc addToRunning {dev sec nano} { + upvar #0 running running devRun devRun + if {[info exists running($dev)] } { + set l $running($dev) + set newSec [expr double([lindex $l 0]) + double($sec)] + set newNano [expr double([lindex $l 1]) + double($nano)] + set running($dev) [list $newSec $newNano] + } else { + set running($dev) [list $sec $nano] + incr devRun + } +} +#------------------------------------------------------------------------ +proc stopRunning {dev} { + upvar #0 running running devRun devRun devices devices + set l $running($dev) + addToDevice $dev [lindex $l 0] [lindex $l 1] + incr devRun -1 + unset running($dev) +} +#----------------------------------------------------------------------- +proc isDevRunning {dev} { + upvar #0 running running + return [info exists running($dev)] +} +#------------------------------------------------------------------------ +proc cancelAll {} { + upvar #0 running running devRun devRun devices devices + upvar #0 sicsRestart sicsRestart + if {$devRun > 0} { + incr sicsRestart + } + set runlist [array names running] + foreach dev $runlist { + puts stdout "Live restart on device $dev" + set l $running($dev) + addToDevice unaccounted [lindex $l 0] [lindex $l 1] + unset running($dev) + } + set devRun 0 +} +#--------------------------------------------------------------------- +proc addRunDiff {dev stamp lastStamp} { + upvar #0 running running devRun devRun + + set diff [calcTimeDiff [lindex $lastStamp 0] \ + [lindex $lastStamp 1] \ + [lindex $stamp 0]\ + [lindex $stamp 1]] + if {![info exists running($dev)] } { + addToRunning $dev 0 0 + } + set disSec [expr double([lindex $diff 0])/double($devRun)] + set disNano [expr double([lindex $diff 1])/double($devRun)] + set devlist [array names running] + foreach d $devlist { + addToRunning $d $disSec $disNano + } +} +#--------------------------------------------------------------------- +proc clearAll {} { + upvar #0 devRun devRun __lastStamp lastStamp __nobeamStamp nobeamStamp + upvar #0 devices devices running running sicsRestart sicsRestart + set devRun 0 + catch {unset lastStamp} + catch {unset nobeamStamp} + set l [array names devices] + foreach d $l { + unset devices($d) + } + set l [array names running] + foreach d $l { + unset running($d) + } + set devices(nobeam) 0 + set devices(unaccounted) 0 + set sicsRestart 0 +} +#======================================================================= +# This section contains the code with the main interpretation and +# analysis +#======================================================================= +proc analyzeLine {line after} { + upvar #0 devRun devRun __lastStamp lastStamp __nobeamStamp nobeamStamp + set log [parseLogLine $line] + set afterSec [clock scan $after] + set op [getLogOp $log] + set t [getLogSeconds $log] + if {$t < $afterSec} { + return + } + switch $op { + START { + set dev [getLogDevice $log] + if {[string compare $dev SICS] == 0} { + cancelAll + return + } + if {$devRun > 0} { + set stamp [getStamp $log] + addRunDiff $dev $stamp $lastStamp + set lastStamp $stamp + } else { + if {![isDevRunning $dev] } { + addToRunning $dev 0 0 + set lastStamp [getStamp $log] + } + } + } + STOP { + if {![info exists lastStamp]} { + return + } + set dev [getLogDevice $log] + if {[string compare $dev SICS] == 0} { + cancelAll + return + } + set stamp [getStamp $log] + addRunDiff $dev $stamp $lastStamp + if {[isDevRunning $dev] } { + stopRunning $dev + } + if {$devRun == 0} { + unset lastStamp + } + } + NOBEAM { + set nobeamStamp [getStamp $log] + } + CONTINUE { + set stamp [getStamp $log] + set diff [calcTimeDiff [lindex $nobeamStamp 0] \ + [lindex $nobeamStamp 1] \ + [lindex $stamp 0]\ + [lindex $stamp 1]] + addToDevice nobeam [lindex $diff 0] [lindex $diff 1] + unset nobeamStamp + } + } +} +#========================================================================== +proc printResult {} { + upvar #0 devices devices sicsRestart sicsRestart + set l [array names devices] + puts stdout "DEVICE SECONDS" + foreach dev $l { + puts stdout [format "%-20s %12.2f" $dev $devices($dev)] + } + puts stdout [format "%-20s %12.2f" "Live Restarts" $sicsRestart] +} +#========================================================================= +proc analyzeFile {filename after} { + set f [open $filename r] + while {[gets $f line] >= 0} { + set status [catch {analyzeLine $line $after} msg] + if {$status != 0} { + puts stdout "ERROR: error $msg processing $line" + } + } + close $f +} +#=============== MAIN Program =========================================== +proc main {} { + global argv + if {[llength $argv] < 2} { + puts stdout "Usage:\n\tanalysedevexeclog filename after" + puts stdout "\t with after being a date in format MM/DD/YYYY" + exit 1 + } + + analyzeFile [lindex $argv 0] [lindex $argv 1] + + printResult +} + +main +exit 0 + diff --git a/tcl/gumxml.tcl b/tcl/gumxml.tcl new file mode 100644 index 00000000..bb9e538b --- /dev/null +++ b/tcl/gumxml.tcl @@ -0,0 +1,80 @@ +proc getdataType {path} { + return [lindex [split [hinfo $path] ,] 0] +} + + +proc make_nodes {path result indent} { +set nodename [file tail $path]; +set type [getdataType $path] +set prefix [string repeat " " $indent] +set newIndent [expr $indent + 2] +#array set prop_list [ string trim [join [split [hlistprop $path] =]] ] + set prop_list(control) true + set we_have_control [info exists prop_list(control)] + if {$we_have_control == 0 || $we_have_control && $prop_list(control) == "true"} { + append result "$prefix\n" + foreach p [property_elements $path $newIndent] { + append result $p + } + foreach x [hlist $path] { + set result [make_nodes [string map {// /} "$path/$x"] $result $newIndent] + } + append result "$prefix\n" + } + return $result +} + +proc property_elements_old {path indent} { + set prefix [string repeat " " $indent] + foreach {key value} [string map {= " "} [hlistprop $path]] { + if {[string compare -nocase $key "control"] == 0} {continue} + lappend proplist "$prefix\n" +# foreach v [split $value ,] { +# lappend proplist "$prefix$prefix$v\n" +# } + lappend proplist "$prefix$prefix$value\n" + lappend proplist "$prefix\n" + } + if [info exists proplist] {return $proplist} +} + +proc property_elements {path indent} { + set prefix [string repeat " " $indent] + set data [hlistprop $path] + set propList [split $data \n] + foreach prop $propList { + set pl [split $prop =] + set key [string trim [lindex $pl 0]] + set value [string trim [lindex $pl 1]] + if {[string length $key] < 1} { + continue + } + lappend proplist "$prefix\n" + lappend proplist "$prefix$prefix$value\n" + lappend proplist "$prefix\n" + } + if [info exists proplist] {return $proplist} +} + +proc getgumtreexml {path} { + append result "\n" + append result "\n" + + if {[string compare $path "/" ] == 0} { + foreach n [hlist $path] { + set result [make_nodes $n $result 2] + } + } else { +# set result [make_nodes $path $result 2] + foreach n [hlist $path] { + set result [make_nodes $path/$n $result 2] + } + } + + append result "\n" +} + +if {[info exists guminit] == 0} { + set guminit 1 + Publish getgumtreexml Spy +} diff --git a/tcl/hdbutil.tcl b/tcl/hdbutil.tcl new file mode 100644 index 00000000..dde7e9fb --- /dev/null +++ b/tcl/hdbutil.tcl @@ -0,0 +1,690 @@ +#----------------------------------------------------------------------- +# This is a collection of utility procedures to help with Hipadaba and +# Gumtree Swiss Edition. This file is supposed to be sourced by any +# instrument using Hipadaba. +# +# Copyright: see file COPYRIGHT +# +# Collected from various files: Mark Koennecke, March 2008 +# +# Requirements: +# * the internal scan command xxxscan +# * scan data to live /graphics/scan_data +#---------------------------------------------------------------------- +if { [info exists hdbinit] == 0 } { + set hdbinit 1 + InstallHdb + MakeStateMon + Publish getgumtreexml Spy + if {[string first tmp $home] < 0} { + set tmppath $home/tmp + } else { + set tmppath $home + } + Publish mgbatch Spy + Publish loadmgbatch Spy + Publish hsearchprop Spy + Publish hdbscan User + Publish hdbprepare User + Publish hdbcollect User + Publish listbatchfiles Spy + Publish makemumopos User + Publish dropmumo User + Publish hdbbatchpath User +# Publish hmake Mugger +# Publish hmakescript Mugger +# Publish hlink Mugger +# Publish hcommand Mugger +} +#=================================================================== +# Configuration commands provided: +# hdbReadOnly +# makesampleenv path +# makestdscan path +# makestdscangraphics path +# makestdbatch +# makeQuickPar name path +# makeslit path left right upper lower +# configures a slit. Missing motors can be indicated with NONE +# makestdadmin +# makecount path +# makerepeat path +# makekillfile path +# makesuccess path +# makestdgui +# makewait path +# makeevproxy rootpath hdbname devicename +# makemumo rootpath mumoname +# makeexe +#===================== hfactory adapters ========================== +proc hmake {path priv type {len 1}} { + hfactory $path plain $priv $type $len +} +#-------------------------------------------------------------------- +proc hmakescript {path readscript writescript type {len 1}} { + hfactory $path script $readscript $writescript $type $len +} +#------------------------------------------------------------------- +proc hlink {path obj {treename NONE} } { + if {[string equal $treename NONE]} { + set treename $ob + } + append realpath $path / $treename + hfactory $realpath link $obj +} +#------------------------------------------------------------------- +proc hcommand {path script} { + hfactory $path command $script +} +#================ make XML tree ===================================== +proc getdataType {path} { + return [lindex [split [hinfo $path] ,] 0] +} +#---------------------------------------------------------------------- +proc make_nodes {path result indent} { +set nodename [file tail $path]; +set type [getdataType $path] +set prefix [string repeat " " $indent] +set newIndent [expr $indent + 2] +#array set prop_list [ string trim [join [split [hlistprop $path] =]] ] + set prop_list(control) true + set we_have_control [info exists prop_list(control)] + if {$we_have_control == 0 || $we_have_control && $prop_list(control) == "true"} { + append result "$prefix\n" + foreach p [property_elements $path $newIndent] { + append result $p + } + foreach x [hlist $path] { + set result [make_nodes [string map {// /} "$path/$x"] $result $newIndent] + } + append result "$prefix\n" + } + return $result +} +#------------------------------------------------------------------- +proc property_elements_old {path indent} { + set prefix [string repeat " " $indent] + foreach {key value} [string map {= " "} [hlistprop $path]] { + if {[string compare -nocase $key "control"] == 0} {continue} + lappend proplist "$prefix\n" +# foreach v [split $value ,] { +# lappend proplist "$prefix$prefix$v\n" +# } + lappend proplist "$prefix$prefix$value\n" + lappend proplist "$prefix\n" + } + if [info exists proplist] {return $proplist} +} +#----------------------------------------------------------------------- +proc property_elements {path indent} { + set prefix [string repeat " " $indent] + set data [hlistprop $path] + set propList [split $data \n] + foreach prop $propList { + set pl [split $prop =] + set key [string trim [lindex $pl 0]] + set value [string trim [lindex $pl 1]] + if {[string length $key] < 1} { + continue + } + lappend proplist "$prefix\n" + lappend proplist "$prefix$prefix$value\n" + lappend proplist "$prefix\n" + } + if [info exists proplist] {return $proplist} +} +#-------------------------------------------------------------------------- +proc getgumtreexml {path} { + append result "\n" + append result "\n" + + if {[string compare $path "/" ] == 0} { + foreach n [hlist $path] { + set result [make_nodes $n $result 2] + } + } else { +# set result [make_nodes $path $result 2] + foreach n [hlist $path] { + set result [make_nodes $path/$n $result 2] + } + } + + append result "\n" +} +#==================== Gumtree batch ========================================= +proc searchPathForDrivable {name} { + set path [string trim [hmatchprop / sicsdev $name]] + if {[string compare $path NONE] != 0} { + return $path + } + set txt [findalias $name] + if {[string compare $txt NONE] == 0} { + return NONE + } + set l1 [split $txt =] + set l [split [lindex $l1 1] ,] + foreach alias $l { + set alias [string trim $alias] + set path [string trim [hmatchprop / sicsdev $alias]] + if {[string compare $path NONE] != 0} { + return $path + } + } + return NONE +} +#---------------------------------------------------------------- +proc searchForCommand {name} { + return [string trim [hmatchprop / sicscommand $name]] +} +#---------------------------------------------------------------- +proc treatsscan {scanpath command out} { + set l [split $command] + set len [llength $l] + set noVar [expr ($len-2)/3] + set np [lindex $l [expr $len -2]] + set preset [lindex $l [expr $len -1]] + for {set i 0} {$i < $noVar} {incr i} { + set start [expr $i * 3] + set scanVar [lindex $l [expr 1 + $start]] + set scanStart [lindex $l [expr 2 + $start]] + set scanEnd [lindex $l [expr 3 + $start]] + set scanStep [expr ($scanEnd*1. - $scanStart*1.)/$np*1.] + append hdbVar $scanVar , + append hdbStart $scanStart , + append hdbStep $scanStep , + } + set hdbVar [string trim $hdbVar ,] + set hdbStart [string trim $hdbStart ,] + set hdbStep [string trim $hdbStep ,] + puts $out "\#NODE: $scanpath" + puts $out "clientput BatchPos = 1" + puts $out "hdbscan $hdbVar $hdbStart $hdbStep $np monitor $preset" +} +#---------------------------------------------------------------- +proc treatcscan {scanpath command out} { + set l [split $command] + set scanVar [lindex $l 1] + set scanCenter [lindex $l 2] + set scanStep [lindex $l 3] + set np [lindex $l 4] + set preset [lindex $l 5] + set hdbStart [expr $scanCenter - ($np*1.0)/2. * $scanStep*1.0] + puts $out "\#NODE: $scanpath" + puts $out "clientput BatchPos = 1" + puts $out "hdbscan $scanVar $hdbStart $scanStep $np monitor $preset" +} +#---------------------------------------------------------------- +proc translateCommand {command out} { + set drivelist [list drive dr run] + set textList [list for while source if] +# clientput "Translating: $command" + set command [string trim $command] + if {[string length $command] < 2} { + return + } + set l [split $command] + set obj [string trim [lindex $l 0]] +#------- check for drive commands + set idx [lsearch $drivelist $obj] + if {$idx >= 0} { + set dev [lindex $l 1] + set path [searchPathForDrivable $dev] + if {[string compare $path NONE] != 0} { + set realTxt [hgetprop $path sicsdev] + set realL [split $realTxt =] + set realDev [lindex $realL 1] + set mapList [list $dev $realDev] + set newCom [string map $mapList $command] + puts $out "\#NODE: $path" + puts $out "clientput BatchPos = 1" + puts $out $newCom + return + } + } +#------ check for well known broken commands + set idx [lsearch $textList $obj] + if {$idx >= 0} { + puts $out "\#NODE: /batch/commandtext" + puts $out "clientput BatchPos = 1" + set buffer [string map {\n @nl@} $command] + puts $out "hset /batch/commandtext $buffer" + return + } +#--------- check for simple commands + set path [searchForCommand $command] + if {[string compare $path NONE] != 0} { + puts $out "\#NODE: $path" + puts $out "clientput BatchPos = 1" + puts $out $command + return + } + set scancom [searchForCommand hdbscan] +#---------- deal with scans + if {[string first sscan $obj] >= 0} { + if {[catch {treatsscan $scancom $command $out}] == 0} { + return + } + } + if {[string first cscan $obj] >= 0} { + if {[catch {treatsscan $scancom $command $out}] == 0} { + return + } + } +#--------- give up: output as a text node + puts $out "\#NODE: /batch/commandtext" + puts $out "clientput BatchPos = 1" + set buffer [string map {\n @nl@} $command] + puts $out "hset /batch/commandtext $buffer" +} +#---------------------------------------------------------------- +proc mgbatch {filename} { + global tmppath + set f [open $filename r] + gets $f line + close $f + if {[string first MOUNTAINBATCH $line] > 0} { +#--------- This is a mountaingum batch file which does not need +# to be massaged + return $filename + } + set f [open $filename r] + set realfilename [file tail $filename] + set out [open $tmppath/$realfilename w] + puts $out \#MOUNTAINBATCH + while {[gets $f line] >= 0} { + append buffer $line + if {[info complete $buffer] == 1} { + translateCommand $buffer $out + unset buffer + } else { + append buffer \n + } + } + close $out + return $tmppath/$realfilename +} +#---------------------------------------------------------------- +proc loadmgbatch {filename} { + set txt [exe fullpath $filename] + set l [split $txt =] + set realf [lindex $l 1] + set realf [mgbatch $realf] + return [exe print $realf] +} +#============== hdbscan ========================================= +proc hdbscan {scanvars scanstart scanincr np mode preset} { + global stdscangraph hdbscanactive + xxxscan clear + xxxscan configure script + xxxscan function prepare hdbprepare + xxxscan function collect hdbcollect + set varlist [split $scanvars ,] + set startlist [split $scanstart ,] + set incrlist [split $scanincr ,] + hset $stdscangraph/scan_variable/name [lindex $varlist 0] + set count 0 + foreach var $varlist { + if {[string first / $var] >= 0} { + set var [string trim [SplitReply [hgetprop $var sicsdev]]] + } + xxxscan add $var [lindex $startlist $count] [lindex $incrlist $count] + incr count + } + set hdbscanactive 1 + set status [catch {xxxscan run $np $mode $preset} msg] + set hdbscanactive 0 + if {$status == 0} { + return $msg + } else { + error $msg + } +} +#------------------------------------------------------------------------------ +proc hdbprepare {obj userdata } { + global stdscangraph + stdscan prepare $obj userdata + hupdate $stdscangraph/dim +} +#------------------------------------------------------------------------------ +proc hdbcollect {obj userobj np} { + global stdscangraph + stdscan collect $obj $userobj $np + hupdate $stdscangraph/scan_variable + hupdate $stdscangraph/counts +} +#----------------------------------------------------------------------------- +proc gethdbscanvardata {no} { + set np [string trim [SplitReply [xxxscan np]]] + if {$np == 0} { + return ".0 .0 .0" + } + set status [catch {SplitReply [xxxscan getvardata $no]} txt] + if {$status == 0} { + return [join $txt] + } else { + return ".0 .0 .0" + } +} +#---------------------------------------------------------------------------- +proc gethdbscancounts {} { + set np [string trim [SplitReply [xxxscan np]]] + if {$np == 0} { + return "0 0 0" + } + set status [catch {SplitReply [xxxscan getcounts]} txt] + if {$status == 0} { + return [join $txt] + } else { + return "0 0 0" + } +} +#================= helper to get the list of batch files ================= +proc listbatchfiles {} { + set ext [list *.tcl *.job *.run] + set txt [SplitReply [exe batchpath]] + set dirlist [split $txt :] + set txt [SplitReply [exe syspath]] + set dirlist [concat $dirlist [split $txt :]] +# clientput $dirlist + set result [list ""] + foreach dir $dirlist { + foreach e $ext { + set status [catch {glob [string trim $dir]/$e} filetxt] + if {$status == 0} { + set filelist [split $filetxt] + foreach f $filelist { +# clientput "Working at $f" + set nam [file tail $f] + if { [lsearch $result $nam] < 0} { +# clientput "Adding $nam" + lappend result $nam + } + } + } else { +# clientput "ERROR: $filetxt" + } + } + } + foreach bf $result { + append resulttxt $bf , + } + return [string trim $resulttxt ,] +} +#------------------------------------------------------------------------- +proc hsearchprop {root prop val} { + set children [hlist $root] + set childlist [split $children \n] + if {[llength $childlist] <= 0} { + error "No children" + } + foreach child $childlist { + if {[string length $child] < 1} { + continue + } + catch {hgetprop $root/$child $prop} msg + if { [string first ERROR $msg] < 0} { + set value [string trim [SplitReply $msg]] + if { [string equal -nocase $value $val] == 1} { + return $root/$child + } + } + set status [catch {hsearchprop $root/$child $prop $val} node] + if {$status == 0} { + return $node + } + } + error "Not found" +} +#============ various utility routines ===================================== +proc hdbReadOnly {} { + error "Parameter is READ ONLY" +} +#--------------------------------------------------------------------------- +proc makesampleenv {path} { + hfactory $path plain spy none + hsetprop $path type graphdata + hsetprop $path viewer mountaingumui.TimeSeries + hfactory $path/vars plain user text + hset $path/vars tomato + hfactory $path/rank plain user int + hset $path/rank 1 + hfactory $path/dim plain user intar 1 + hset $path/dim 300 + hfactory $path/getdata plain user text + hsetprop $path/getdata type logcommand + hfactory $path/getdata/starttime plain spy text + hfactory $path/getdata/endtime plain spy text +} +#-------------------------------------------------- +proc makestdscan {path} { + hfactory $path command hdbscan + hsetprop $path type command + hsetprop $path viewer mountaingumui.ScanEditor + hsetprop $path priv user + hfactory $path/scan_variables plain user text + hsetprop $path/scan_variables argtype drivable + hfactory $path/scan_start plain user text + hfactory $path/scan_increments plain user text + hfactory $path/NP plain user int + hfactory $path/mode plain user text + hsetprop $path/mode values "monitor,timer" + hfactory $path/preset plain user float +} +#--------------------------------------------------- +proc makestdscangraphics {path} { + global stdscangraph + + set stdscangraph $path + + hfactory $path plain spy none + hsetprop $path type graphdata + hsetprop $path viewer default + hattach $path title title + hfactory $path/rank plain mugger int + hset $path/rank 1 + hsetprop $path/rank priv internal + hfactory $path/dim script "xxxscan np" hdbReadOnly intar 1 + hsetprop $path/dim priv internal + hfactory $path/scan_variable script "gethdbscanvardata 0" hdbReadOnly floatvarar 1 + hsetprop $path/scan_variable type axis + hsetprop $path/scan_variable dim 0 + hsetprop $path/scan_variable transfer zip + hsetprop $path/scan_variable priv internal + hfactory $path/scan_variable/name plain user text + hfactory $path/counts script "gethdbscancounts" hdbReadOnly intvarar 1 + hsetprop $path/counts type data + hsetprop $path/counts transfer zip + hsetprop $path/counts priv internal +} +#---------------------------------------------------- +proc makeQuickPar {name path} { + hfactory /quickview/$name plain mugger text + hset /quickview/$name $path +} +#--------------------------------------------------- +proc makestdbatch {} { + hfactory /batch plain spy none + hfactory /batch/bufferlist script listbatchfiles hdbReadOnly text + sicspoll add /batch/bufferlist hdb 30 + hfactory /batch/commandtext plain spy text + hsetprop /batch/commandtext viewer mountaingumui.TextEdit + hsetprop /batch/commandtext commandtext true + hfactory /batch/currentline plain user int +} +#----------------------------------------------------- +proc makeslit {path left right upper bottom} { + hfactory $path plain spy none + hsetprop $path type part + if {![string equal $left NONE]} { + hattach $path $left left + } + if {![string equal $right NONE]} { + hattach $path $right right + } + if {![string equal $upper NONE]} { + hattach $path $upper upper + } + if {![string equal $bottom NONE]} { + hattach $path $bottom bottom + } +} +#--------------------------------------------------------- +proc makestdadmin {} { + hfactory /instrument/experiment plain spy none + hattach /instrument/experiment title title + hattach /instrument/experiment user user + hattach /instrument/experiment/user adress address + hattach /instrument/experiment/user phone phone + hattach /instrument/experiment/user email email + hfactory /instrument/experiment/datafilenumber script sicsdatanumber \ + hdbReadOnly int + hsetprop /instrument/experiment/datafilenumber priv internal + hfactory /instrument/experiment/batchpath script "exe batchpath" \ + "exe batchpath" text + hsetprop /instrument/experiment/batchpath priv user +} +#---------------------------------------------------------- +proc makecount {path} { + hfactory $path command count + hsetprop $path type command + hsetprop $path priv user + hfactory $path/mode plain user text + hsetprop $path/mode values "monitor,timer" + hfactory $path/preset plain user float + hset $path/preset 60000 + hset $path/mode monitor +} +#---------------------------------------------------------- +proc makerepeat {path} { + hfactory $path command repeat + hsetprop $path type command + hsetprop $path priv user + hfactory $path/num plain user int + hfactory $path/mode plain user text + hsetprop $path/mode values "monitor,timer" + hfactory $path/preset plain user float + hset $path/preset 60000 + hset $path/mode monitor +} +#---------------------------------------------------------- +proc makekillfile {path} { + hcommand $path killfile + hsetprop $path type command + hsetprop $path priv manager +} +#---------------------------------------------------------- +proc makesuccess {path} { + hcommand $path success + hsetprop $path type command + hsetprop $path priv user +} +#----------------------------------------------------------- +proc makestdgui {} { + hfactory /gui plain spy none + hfactory /gui/status plain internal text + status hdbinterest /gui/status +} +#------------------------------------------------------------ +proc makewait {path} { + hfactory $path command wait + hsetprop $path type command + hsetprop $path priv user + hfactory $path/time plain user int +} +#------------------------------------------------------------ +proc makeevproxy {rootpath hdbname devicename} { + MakeProxy p${devicename} $devicename float + p${devicename} map upperlimit upperlimit float user + p${devicename} map lowerlimit lowerlimit float user + hlink $rootpath p${devicename} $hdbname + hsetprop $rootpath/$hdbname sicsdev $devicename + hsetprop $rootpath/$hdbname type drivable + sicspoll add $rootpath/$hdbname hdb 30 +} +#================== multi motor stuff ======================= +proc getNamposList {mumo} { + set txt [$mumo list] + set l [split $txt "\n"] + set lala [llength $l] + for {set i 1} {$i < [llength $l]} {incr i} { + set pos [lindex $l $i] + if {[string length $pos] > 1} { + append result [lindex $l $i] "," + } + } + if { ![info exists result] } { +# clientput "nampos = $txt" + append result UNKNOWN + } + return [string trimright $result ","] +} +#------------------------------------------------------------ +proc getNamPos {mumo} { + set txt [$mumo find] + set l [split $txt =] + return [string trim [lindex $l 1]] +} +#----------------------------------------------------------- +proc updateNamePosValues {rootpath} { + hupdate $rootpath/namedposition/values + hupdate $rootpath/dropnamedposition/name/values +} +#------------------------------------------------------------ +proc makemumopos {mumo rootpath name} { + $mumo pos $name + updateNamePosValues $rootpath +} +#----------------------------------------------------------- +proc dropmumo {mumo rootpath name} { + $mumo drop $name + updateNamePosValues $rootpath +} +#------------------------------------------------------------ +proc getDropList {mumo} { + set txt [getNamposList $mumo] + append txt ",all" + return $txt +} +#------------------------------------------------------------- +proc makemumo {rootpath mumoname} { + hfactory $rootpath/namedposition script "getNamPos $mumoname" \ + $mumoname text + hsetprop $rootpath/namedposition priv user + hfactory $rootpath/namedposition/values script \ + "getNamposList $mumoname" hdbReadOnly text + hsetprop $rootpath/namedposition/values visible false + hupdate $rootpath/namedposition/values + hfactory $rootpath/assignname2current command \ + "makemumopos $mumoname $rootpath" + hsetprop $rootpath/assignname2current priv user + hsetprop $rootpath/assignname2current type command + hfactory $rootpath/assignname2current/name plain user text + hset $rootpath/assignname2current/name "Undefined" + hfactory $rootpath/dropnamedposition command \ + "dropmumo $mumoname $rootpath" + hsetprop $rootpath/dropnamedposition priv user + hsetprop $rootpath/dropnamedposition type command + hfactory $rootpath/dropnamedposition/name plain user text + hfactory $rootpath/dropnamedposition/name/values script \ + "getDropList $mumoname" hdbReadOnly text + hsetprop $rootpath/dropnamedposition/name/values visible false + hupdate $rootpath/dropnamedposition/name/values +} +#----------------------------------------------------------------- +proc hdbbatchpath {pathstring} { + exe batchpath $pathstring + hupdate /instrument/commands/batch/execute/file/values +} +#------------------------------------------------------------------ +proc makeexe {} { + set path /instrument/commands/batch + hfactory $path plain spy none + hfactory $path/batchpath script "exe batchpath" hdbbatchpath text + hsetprop $path/batchpath priv user + hfactory $path/execute command exe + hsetprop $path/execute type command + hsetprop $path/execute priv user + hfactory $path/execute/file plain user text + hfactory $path/execute/file/values script listbatchfiles hdbReadOnly text + sicspoll add $path/execute/file/values hdb 60 +} diff --git a/tcl/makemodrivskel b/tcl/makemodrivskel new file mode 100755 index 00000000..b5c14321 --- /dev/null +++ b/tcl/makemodrivskel @@ -0,0 +1,177 @@ +#!/usr/bin/tclsh +#------------------------------------------------------------------------------ +# Make the skeleton for a motor driver +# +# copyright: see file COPYRIGHT +# +# Mark Koennecke, July 2006 +#------------------------------------------------------------------------------ +if { [llength $argv] < 1} { + puts stdout "Usage:\n\tmakemodrivskel prefix" + exit 1 +} + +set prefix [lindex $argv 0] +#----------------------------------------------------------------------------- +proc stdCast {} { + global prefix + puts stdout " ${prefix}MotorDriver *self = NULL;" + puts stdout " " + puts stdout " self = (${prefix}MotorDriver *)data;" +} + +#----------------- output datastructure +puts stdout "#include " +puts stdout "#include " +puts stdout "#include " +puts stdout "typedef struct __${prefix}MoDriv{" +puts stdout " /* general motor driver interface " +puts stdout " fields. REQUIRED!" +puts stdout " */" +puts stdout " float fUpper; /* upper limit */" +puts stdout " float fLower; /* lower limit */" +puts stdout " char *name;" +puts stdout " int (*GetPosition)(void *self, float *fPos);" +puts stdout " int (*RunTo)(void *self,float fNewVal);" +puts stdout " int (*GetStatus)(void *self);" +puts stdout " void (*GetError)(void *self, int *iCode, char *buffer, int iBufLen);" +puts stdout " int (*TryAndFixIt)(void *self, int iError,float fNew);" +puts stdout " int (*Halt)(void *self);" +puts stdout " int (*GetDriverPar)(void *self, char *name, " +puts stdout " float *value);" +puts stdout " int (*SetDriverPar)(void *self,SConnection *pCon," +puts stdout " char *name, float newValue);" +puts stdout " void (*ListDriverPar)(void *self, char *motorName," +puts stdout " SConnection *pCon);" +puts stdout " void (*KillPrivate)(void *self);" +puts stdout " /* your drivers private fields follow below */" +puts stdout " } ${prefix}MotorDriver;" +puts stdout " " + + +puts stdout "/*================================================================" +puts stdout " GetPos returns OKOK on success, HWFault on failure " +puts stdout "------------------------------------------------------------------*/" +puts stdout "static int ${prefix}GetPos(void *data, float *fPos){" +stdCast +puts stdout " return OKOK;" +puts stdout "}" + +puts stdout "/*----------------------------------------------------------------" +puts stdout " RunTo starts the motor running. Returns OKOK on success, HWfault" +puts stdout " on Errors" +puts stdout "------------------------------------------------------------------*/" +puts stdout "static int ${prefix}RunTo(void *data, float newValue){" +stdCast +puts stdout " return OKOK;" +puts stdout "}" + +puts stdout "/*-----------------------------------------------------------------" +puts stdout " CheckStatus queries the sattus of a running motor. Possible return" +puts stdout " values can be:" +puts stdout " HWBusy : motor still running" +puts stdout " HWFault : motor error detected" +puts stdout " HWPosFault : motor finished, but position not reached" +puts stdout " HWIdle : motor finished OK" +puts stdout " HWWarn : motor issued warning" +puts stdout "--------------------------------------------------------------------*/" +puts stdout "static int ${prefix}CheckStatus(void *data){" +stdCast +puts stdout " return HWIdle;" +puts stdout "}" + +puts stdout "/*------------------------------------------------------------------" +puts stdout " GetError gets more information about error which occurred" +puts stdout " *iCode is an integer error code to be used in TryFixIt as indicator" +puts stdout " buffer is a buffer for a text description of the problem" +puts stdout " iBufLen is the length of buffer" +puts stdout "--------------------------------------------------------------------*/" +puts stdout "static void ${prefix}GetError(void *data, int *iCode, char *buffer," +puts stdout " int iBufLen){" +stdCast +puts stdout "}" + +puts stdout "/*------------------------------------------------------------------" +puts stdout " TryAndFixIt tries everything which is possible in software to fix " +puts stdout " a problem. iError is the error code from GetError, newValue is " +puts stdout " the target value for the motor" +puts stdout " Possible retrun values are:" +puts stdout " MOTOK : everything fixed" +puts stdout " MOTREDO : try again " +puts stdout " MOTFAIL : cannot fix this" +puts stdout "--------------------------------------------------------------------*/" +puts stdout "static int ${prefix}FixIt(void *data, int iError, float newValue){" +stdCast +puts stdout " return MOTFAIL;" +puts stdout "}" + +puts stdout "/*-------------------------------------------------------------------" +puts stdout " Halt tries to stop the motor. Halt errors are ignored" +puts stdout "---------------------------------------------------------------------*/" +puts stdout "static int ${prefix}Halt(void *data){" +stdCast +puts stdout " return 1;" +puts stdout "}" + +puts stdout "/*--------------------------------------------------------------------" +puts stdout " GetDriverPar retrieves the value of a driver parameter." +puts stdout " Name is the name of the parameter, fValue the value when found." +puts stdout " Returns 0 on success, 0 else" +puts stdout "-----------------------------------------------------------------------*/" +puts stdout "static int ${prefix}GetDriverPar(void *data, char *name, float *value){" +stdCast +puts stdout " return 0;" +puts stdout "}" + +puts stdout "/*----------------------------------------------------------------------" +puts stdout " SetDriverPar sets a driver parameter. Returns 0 on failure, 1 on " +puts stdout " success. Name is the parameter name, pCon the connection to report" +puts stdout " errors too, value the new value" +puts stdout "------------------------------------------------------------------------*/" +puts stdout "static int ${prefix}SetDriverPar(void *data, SConnection *pCon," +puts stdout " char *name, float value){" +stdCast +puts stdout " return 0;" +puts stdout "}" + +puts stdout "/*-----------------------------------------------------------------------" +puts stdout " ListDriverPar lists the names and values of driver parameters to " +puts stdout " pCon. Motorname is the name of the motor ro prefix to the listing." +puts stdout "-------------------------------------------------------------------------*/" +puts stdout "static void ${prefix}ListDriverPar(void *data, char *motorname, " +puts stdout " SConnection *pCon){" +stdCast +puts stdout "}" + +puts stdout "/*-----------------------------------------------------------------------" +puts stdout " KillPrivate has the task to delete possibly dynamically allocated " +puts stdout " memory in the private part of the driver structure" +puts stdout "------------------------------------------------------------------------*/" +puts stdout "static void ${prefix}KillPrivate(void *data){" +stdCast +puts stdout "}" + +puts stdout "/*=======================================================================*/" +puts stdout "MotorDriver *${prefix}MakeMotorDriver(void) {" +puts stdout " ${prefix}MotorDriver *pNew = NULL;" +puts stdout "" +puts stdout " pNew = malloc(sizeof(${prefix}MotorDriver));" +puts stdout " if(pNew == NULL){" +puts stdout " return NULL;" +puts stdout " }" +puts stdout " memset(pNew,0,sizeof(${prefix}MotorDriver));" +puts stdout " " +puts stdout " pNew->GetPosition = ${prefix}GetPos;" +puts stdout " pNew->RunTo = ${prefix}RunTo;" +puts stdout " pNew->Halt = ${prefix}Halt;" +puts stdout " pNew->GetStatus = ${prefix}CheckStatus;" +puts stdout " pNew->GetError = ${prefix}GetError;" +puts stdout " pNew->TryAndFixIt = ${prefix}FixIt;" +puts stdout " pNew->GetDriverPar = ${prefix}GetDriverPar;" +puts stdout " pNew->SetDriverPar = ${prefix}SetDriverPar;" +puts stdout " pNew->ListDriverPar = ${prefix}ListDriverPar;" +puts stdout " pNew->KillPrivate = ${prefix}KillPrivate;" +puts stdout " " +puts stdout " return (MotorDriver *)pNew;" +puts stdout "}" +puts stdout "" diff --git a/tcl/ritaframe b/tcl/ritaframe new file mode 100755 index 00000000..97b46a3e --- /dev/null +++ b/tcl/ritaframe @@ -0,0 +1,51 @@ +#!/usr/bin/tclsh +#-------------------------------------------------------------------------- +# script for extracting a frame of rita data from a file and to dump +# the frame into an ASCII file +# +# Mark Koennecke, November 2006 +#------------------------------------------------------------------------- +set loadnx "/afs/psi.ch/project/sinq/sl-linux/lib/" +load [file join $loadnx nxinter.so] + +if {$argc < 2} { + puts stdout "Usage:\n\tritaframe filename number" + exit 1 +} +set num [lindex $argv 1] + +set f [nx_open [lindex $argv 0] $NXACC_READ] +nx_openpath $f /entry1/data/counts +set info [nx_getinfo $f] +set dim1 [expr int([get_nxds_value $info 2])] +set dim2 [expr int([get_nxds_value $info 3])] +set nFrames [expr int([get_nxds_value $info 4])] +if {$num < 0 || $num > $nFrames-1} { + nx_close $f + puts stdout "Requested frame out of range" + exit1 +} +set start [create_nxds 1 $NX_INT32 3] +set end [create_nxds 1 $NX_INT32 3] +put_nxds_value $start 0 0 +put_nxds_value $start 0 1 +put_nxds_value $start $num 2 + +put_nxds_value $end $dim1 0 +put_nxds_value $end $dim2 1 +put_nxds_value $end 1 2 + +set data [nx_getslab $f $start $end] +for {set y 0} {$y < $dim2} {incr y} { + for {set x 0} {$x < $dim1} {incr x} { + set val [expr int([get_nxds_value $data $x $y])] + puts -nonewline stdout [format " %8d" $val] + } + puts stdout "" +} +drop_nxds $start +drop_nxds $end +drop_nxds $data + +nx_close $f +exit 0 diff --git a/tcl/sicstcldebug.tcl b/tcl/sicstcldebug.tcl index 9f6e4856..1139e8fc 100644 --- a/tcl/sicstcldebug.tcl +++ b/tcl/sicstcldebug.tcl @@ -7,20 +7,50 @@ # Thus is should be possible to debug SICS Tcl scripts in a normal # standalone interpreter without the overhead of restarting SICS # all the time. It may even be possible to use one of the normal -# Tcl debugfgers then.... +# Tcl debuggers then.... # # Mark Koennecke, February 2006 +# +# Revamped for use in testing SICS instruments. +# Mark Koennecke, November 2006 #------------------------------------------------------------------ - -set socke [socket localhost 2911] -gets $socke -puts $socke "Spy 007" -flush $socke -gets $socke +set host(amor) amor.psi.ch +set host(dmc) dmc.psi.ch +set host(focus) focus.psi.ch +set host(hrpt) hrpt.psi.ch +set host(mars) mars.psi.ch +set host(morpheus) morpheus.psi.ch +set host(narziss) narziss.psi.ch +set host(poldi) poldi.psi.ch +set host(rita2) rita2.psi.ch +set host(sans) sans.psi.ch +set host(sansli) sans2.psi.ch +set host(tasp) tasp.psi.ch +set host(trics) trics.psi.ch +set host(local) localhost + +#------------------------------------------------------------------- +# initialize the socket before debugging. If local == 1, then a +# connection to localhost is built #------------------------------------------------------------------ -proc unknown args { - global socke - append com "transact " [join $args] +proc initSicsDebug {instrument} { + global socke host + catch {close $socke} + set status [catch {set compi $host($instrument)} msg] + if {$status != 0} { + error "Host for $instrument not found" + } + set socke [socket $compi 2911] + gets $socke + puts $socke "Spy 007" + flush $socke + gets $socke +} +#---------------------------------------------------------------- +proc sicscommand args { + global socke + append com "transact " [join $args] + puts stdout "Sending: $com" puts $socke $com flush $socke set reply "" @@ -29,11 +59,16 @@ proc unknown args { if {[string first TRANSACTIONFINISHED $line] >= 0} { return $reply } else { - append reply $line + append reply $line "\n" } } } #------------------------------------------------------------------ +proc unknown args { + return [sicscommand $args] +} +#------------------------------------------------------------------ proc clientput args { puts stdout [join $args] } +#------------------------------------------------------------------ diff --git a/tcl/tjxp b/tcl/tjxp new file mode 100755 index 00000000..3f7682c8 --- /dev/null +++ b/tcl/tjxp @@ -0,0 +1,73 @@ +#!/usr/bin/tclsh +#---------------------------------------------------------------------- +# This is a Tcl template processor in the style of JSP tags. Unmarked +# text is left alone. But there is special markup: +# <% script %> execute Tcl script and output result +# <%=var%> print The Tcl variable var +# <%! script%> execute the script and print nothing +# +# copyright: GPL +# +# Mark Koennecke, November 2006 +#---------------------------------------------------------------------- +proc loadTemplate {input} { + return [read $input] +} +#--------------------------------------------------------------------- +proc processScript {script} { + set startChar [string index $script 0] + if {[string equal $startChar =] == 1 } { + set varName [string trim [string range $script 1 end]] + set cmd [format "return \$%s" $varName] + return [uplevel #0 $cmd] + } elseif {[string equal $startChar !] == 1} { + set script [string range $script 1 end] + uplevel #0 $script + } else { + return [uplevel #0 $script] + } + return "" +} +#---------------------------------------------------------------------- +# process The template: read template from input, +# write to output channel +#---------------------------------------------------------------------- +proc processTemplate {input output} { + set template [loadTemplate $input] + set current 0 + set start [string first "<%" $template] + set end [string first "%>" $template $start] + while {$start >= 0} { + if {$end < 0} { + error "Found start tag but no end in $template" + } + puts -nonewline $output [string range $template $current \ + [expr $start -1]] + set script [string range $template [expr $start +2] \ + [expr $end -1]] + set txt [processScript $script] + if {[string length $txt] >= 1} { + puts -nonewline $output $txt + } + set template [string range $template [expr $end +2] end] + set start [string first "<%" $template] + set end [string first "%>" $template $start] + + } + puts -nonewline $output $template +} +#================ MAIN ================================================ +if {$argc < 2} { + puts stdout "Usage:\n\ttjxp infile outfile" + puts stdout "\t Outfile can be - for stdout" + exit 1 +} +set in [open [lindex $argv 0] r] +set outfile [lindex $argv 1] +if {[string equal [string trim $outfile] -] == 1} { + set out stdout +} else { + set out [open $outfile w] +} +processTemplate $in $out +exit 0 diff --git a/tcl/tjxphelp b/tcl/tjxphelp new file mode 100644 index 00000000..c5847ac1 --- /dev/null +++ b/tcl/tjxphelp @@ -0,0 +1,38 @@ + + + Tcl Template Processing System. + + This is a test file and help text for my Tcl template processing + system. It was inspired by JSP and JXP. But is in my favourite + scripting language Tcl. Basically it allows to mix Tcl scripts with + text. The text can contain special marks which are then executed as + Tcl scripts in a variety of ways. Three tags are supported: + + <%! set var waschmaschine %> + + This tag executes the Tcl script but prints nothing, except may + be error messages. Please note that this can be used to source + more Tcl files which contains procedures you need for doing your + work. + + <%=var %> prints the value of the Tcl variable var. When processed, + this should print waschmaschine. + + <% set a [list 1 2 3] + join $a + %> + executes the Tcl code within and prints the result. This should be + 1 2 3. + + All Tcl code is executed at global level. There is nothing more to + this. All this was done in 75 lines of Tcl, including comments! You + should be able to process this file through tjxp to see what you get. + Txjp is brough to you by: + + Mark Koennecke, Mark.Koennecke@psi.ch + + txjp is copyrighted under the GNU Public Licence 2.0, which you can + find elsewhere. + + Enjoy! + \ No newline at end of file diff --git a/tclintimpl.c b/tclintimpl.c index f64bf7b1..3b340f25 100644 --- a/tclintimpl.c +++ b/tclintimpl.c @@ -136,13 +136,21 @@ int TclIntAction(SConnection *pCon, SicsInterp *pSics, void *pData, pTclInt self = NULL; char pBuffer[1024]; char *cmd; + float val; self = (pTclInt)pData; assert(self); if(argc < 2){ - sprintf(pBuffer,"ERROR: %s expects at least one argument!", argv[0]); - SCWrite(pCon,pBuffer,eError); - return 0; + if(self->pDriv->GetValue != NULL){ + val = self->pDriv->GetValue(self,pCon); + snprintf(pBuffer,1024,"%s = %f", argv[0], val); + SCWrite(pCon,pBuffer,eValue); + return 1; + } else { + sprintf(pBuffer,"ERROR: %s expects at least one argument!", argv[0]); + SCWrite(pCon,pBuffer,eError); + return 0; + } } strtolower(argv[1]); @@ -162,7 +170,7 @@ int TclIntAction(SConnection *pCon, SicsInterp *pSics, void *pData, cmd = Arg2Tcl(argc-2, &argv[2],pBuffer,1023); if (cmd) { if(self->fd != NULL){ - fprintf(self->fd,"%s\n",pBuffer); + fprintf(self->fd,"%s\n",cmd); } if (cmd != pBuffer) free(cmd); SCSendOK(pCon); diff --git a/test/DataNumber b/test/DataNumber new file mode 100644 index 00000000..75148a66 --- /dev/null +++ b/test/DataNumber @@ -0,0 +1,3 @@ + 131 +NEVER, EVER modify or delete this file +You'll risk eternal damnation and a reincarnation as a cockroach!|n \ No newline at end of file diff --git a/test/batchtest.tcl b/test/batchtest.tcl new file mode 100644 index 00000000..a78dca36 --- /dev/null +++ b/test/batchtest.tcl @@ -0,0 +1,128 @@ +#------------------------------------------------------------------------------ +# This is a set of regression tests for the batch processing feauture +# in SICS +# +# Mark Koennecke, October 2006 +#------------------------------------------------------------------------------ + +puts stdout "Testing batch processing" + +test batch-1.0 {Test Batch File Execution} -body { + config rights User User + set result [eval exe job1.tcl] + if {[string first TERMINATED $result] < 0} { + error "Failed to process batch file" + } + if {[string first Job1 $result] < 0} { + error "Output from batch file missing, received: $result" + } + return OK +} -result OK + +test batch-1.1 {Test Batch File Interruption} -body { + config rights User user + exec ./interrupt.tcl & + set result [eval exe job1.tcl] + if {[string first TERMINATED $result] < 0} { + error "Failed to process batch file" + } + if {[string first interrupted $result] < 0} { + error "Interrupting did not work" + } + return OK +} -result OK + +test batch-1.2 {Test Nested Batch File Execution} -body { + config rights User user + set result [eval exe job2.tcl] + if {[string first TERMINATED $result] < 0} { + error "Failed to process batch file" + } + if {[string first NestOne $result] < 0} { + error "Output from batch file missing" + } + if {[string first NestTwo $result] < 0} { + error "Output from batch file missing" + } + if {[string first NestThree $result] < 0} { + error "Output from batch file missing" + } + return OK +} -result OK + +test batch-1.3 {Test Nested Batch File Interruption} -body { + config rights User user + exec ./interrupt.tcl & + set result [eval exe job2.tcl] + if {[string first TERMINATED $result] < 0} { + error "Failed to process batch file" + } + if {[string first NestOne $result] < 0} { + error "Output from batch file missing" + } + if {[string first NestTwo $result] < 0} { + error "Output from batch file missing" + } + if {[string first NestThree $result] < 0} { + error "Output from batch file missing" + } + if {[string first interrupted $result] < 0} { + error "Interrupting did not work" + } + return OK +} -result OK + +test batch-1.4 {Test Path Parameters} -body { + testPar "exe batchpath" tmp User + testPar "exe syspath" tmp Mugger + return OK +} -result OK + +test batch-1.5 {Test Path Failure} -body { + config rights Mugger Mugger + exe batchpath tmp + exe syspath tmp + set result [exe job4.tcl] + if {[string first "not found" $result] < 0} { + error "Batch file found which should not" + } + exe batchpath ./ + exe syspath ./ + return OK +} -result OK + +test batch-1.6 {Test Uploading} -body { + config rights User User + catch {exec rm hugo.job} + testOK "exe upload" + testOK "exe append clientput hugo" + testOK "exe append wait 2" + testOK "exe save hugo.job" + testOK "exe upload" + testOK "exe append clientput hugo" + testOK "exe append wait 2" + set stat [catch {testOK "exe save hugo.job" } msg ] + if {$stat == 0} { + error "Failed to trigger overwrite error" + } + testOK "exe forcesave hugo.job" + return OK +} -result OK + +test batch-1.7 {Test Notifications} -body { + config rights User User + testOK "exe interest" + set result [eval exe job4.tcl] + if {[string first BATCHSTART $result] < 0} { + error "BATCHSTART missing" + } + if {[string first BATCHEND $result] < 0} { + error "BATCHEND missing" + } + if {[string first job4.tcl.range $result] < 0} { + error "Range entries missing" + } + return OK +} -result OK + + diff --git a/test/countertest.tcl b/test/countertest.tcl new file mode 100644 index 00000000..4b814687 --- /dev/null +++ b/test/countertest.tcl @@ -0,0 +1,271 @@ +#------------------------------------------------------------- +# Testing of the counter module +# +# The regression counter has various errortypes which can be simulated: +# 0 = none +# 1 = failed start +# 2 = status failure +# 3 = pause fail +# 4 = continue fail +# 5 = failed read +# +# Another parameter is recover which causes the problem to go away +# when 1 +# +# TODO: What shall happen when pausing fails? Currently it continues +# counting. This may be exactly what we need, but???? +# +# This code needs the counter name two times: once as countername and +# as errorname. The purpose is that this module may be used for testing +# both the real and the multi counter. +# +# Mark Koennecke, September 2006 +#------------------------------------------------------------- +#set countername aba +#set errorname aba + +puts stdout "Testing counter: $countername" + +#--------------------------------------------------------------- +config rights Mugger Mugger +$errorname setpar errortype 1 0 + +test counter-1.0 {Test Mode Setting} -body { + config rights Spy Spy + set res [eval $countername setmode monitor] + if {[string first ERROR $res] < 0} { + error "Managed to set parameter even if not allowed" + } + config rights User User + set res [eval $countername setmode monitor] + if {[string first ERROR $res] >= 0} { + error "Setting parameter failed with $res" + } + set readback [SICSValue "$countername getmode"] + compareValue [string tolower $readback] monitor + config rights Spy Spy + set res [eval $countername setmode timer] + if {[string first ERROR $res] < 0} { + error "Managed to set parameter even if not allowed" + } + config rights User User + set res [eval $countername setmode timer] + if {[string first ERROR $res] >= 0} { + error "Setting parameter failed with $res" + } + set readback [SICSValue "$countername getmode"] + compareValue [string tolower $readback] timer + return "OK" +} -result OK +#------------------------------------------------------------------- +test counter-1.1 {Test Preset Setting} -body { + config rights Spy Spy + set val 12 + set res [eval $countername setpreset $val] + if {[string first ERROR $res] < 0} { + error "Managed to set parameter even if not allowed" + } + config rights User User + set res [eval $countername setpreset $val] + if {[string first ERROR $res] >= 0} { + error "Setting parameter failed with $res" + } + set readback [SICSValue "$countername getpreset"] + compareValue $readback $val + return "OK" +} -result OK +#--------------------------------------------------------------------- +test counter-1.3 {Test Normal Counting} -body { + config rights Spy Spy + set status [catch {testNBCounting "$countername countnb 10" 11} msg] + if {$status == 0} { + error "Counted in spite of lacking privilege" + } + config rights User User + testNBCounting "$countername countnb 10" 11 +} -result OK +#--------------------------------------------------------------------- +test counter-1.4 {Test Blocking Counting} -body { + config rights Spy Spy + set status [catch {testBlockCounting "$countername countnb 10" 11} msg] + if {$status == 0} { + error "Counted in spite of lacking privilege" + } + config rights User User + testBlockCounting "$countername countnb 10" 11 +} -result OK +#-------------------------------------------------------------------- +test counter-1.5 {Interrupted Counting} -body { + testInterruptedCount "$countername countnb 100" +} -result OK +#-------------------------------------------------------------------- +config rights User User + +test counter-1.51 {Pause Counting Test} -body { + global socke + $countername countnb 300 + exec sleep 1 + set ans [status] + if {[string first Counting $ans] < 0} { + error "Failed to start counting: $ans" + } + pause + exec sleep 1 + set ans [status] + if {[string first Paus $ans] < 0} { + error "Failed to pause counting: $ans" + } + puts $socke continue + flush $socke + exec sleep 1 + set ans [status] + if {[string first Count $ans] < 0} { + error "Failed to continue counting: $ans" + } + puts $socke "INT1712 3" + flush $socke + set ans [status] + return OK +} -result OK +#--------------------------------------------------------- +test counter-1.52 {Pause Interrupt Test} -body { + global socke + $countername countnb 300 + exec sleep 2 + set ans [status] + if {[string first Counting $ans] < 0} { + error "Failed to start counting: $ans" + } + pause + exec sleep 1 + set ans [status] + if {[string first Paus $ans] < 0} { + error "Failed to pause counting: $ans" + } + puts $socke "INT1712 3" + flush $socke + set ans [status] + if {[string first Eager $ans] < 0} { + error "Failed to interrupt paused counting: $ans" + } + return OK +} -result OK +#------------------------------------------------------------------- +test counter-1.53 {Counter Value Read Test} -body { + config rights User User + $countername count 10 + set ans [SICSValue "$countername gettime"] + compareValue $ans 10 + set ans [SICSValue "$countername getcounts"] + compareValue $ans 5 + set ans [SICSValue "$countername getmonitor 1"] + compareValue $ans 10 + set ans [SICSValue "$countername getmonitor 2"] + compareValue $ans 25 + set ans [SICSValue "$countername getmonitor 3"] + compareValue $ans 35 + set ans [SICSValue "$countername getmonitor 4"] + compareValue $ans 45 + set ans [SICSValue "$countername getmonitor 5"] + compareValue $ans 55 + set ans [SICSValue "$countername getmonitor 6"] + compareValue $ans 65 + return OK +} -result OK +#-------------------------------------------------------------------- +config rights Mugger Mugger +$errorname setpar errortype 1 1 +$errorname setpar recover 1 0 + +test counter-1.6 {Counting Start Failure} -body { + set ans [$countername countnb 100] + if { [string first "Counting aborted" $ans] < 0} { + error "Failed to trigger count start failure: $ans" + } + return OK +} -result OK +#--------------------------------------------------------------- +$errorname setpar errortype 1 1 +$errorname setpar recover 1 1 + +test counter-1.7 {Counting Start Failure with Recovery} -body { + set ans [$countername countnb 10] + if { [string first "WARNING" $ans] < 0} { + error "Failed to trigger count start failure: $ans" + } + set ans [SICSValue status] + if {[string first Counting $ans] < 0} { + error "Did not recover from start failure" + } + exec sleep 12 + set ans [SICSValue status] + if {[string first Eager $ans] < 0} { + error "Did not stop counting after start failure" + } + return OK +} -result OK +#---------------------------------------------------------------------- +$errorname setpar errortype 1 2 +$errorname setpar recover 1 0 + +test counter-1.8 {Counting Status Failure} -body { + set ans [$countername countnb 100] + set ans [status] + if { [string first "Full Stop called" $ans] < 0} { + error "Failed to trigger count start failure: $ans" + } + return OK +} -result OK +#--------------------------------------------------------------- +$errorname setpar errortype 1 2 +$errorname setpar recover 1 1 + +test counter-1.9 {Counting Status Failure with Recovery} -body { + set ans [$countername countnb 10] + set ans [status] + if { [string first "WARNING" $ans] < 0} { + error "Failed to trigger count start failure: $ans" + } + if {[string first Counting $ans] < 0} { + error "Did not recover from status failure" + } + exec sleep 12 + set ans [SICSValue status] + if {[string first Eager $ans] < 0} { + error "Did not stop counting after status failure" + } + return OK +} -result OK +#------------------------------------------------------------------- +$errorname setpar errortype 1 5 +$errorname setpar recover 1 0 + +test counter-1.10 {Counter Read Failure} -body { + set ans [$countername count 2] + if { [string first "Full Stop" $ans] < 0} { + error "Failed to trigger count read failure: $ans" + } + set ans [SICSValue status] + if {[string first Eager $ans] < 0} { + error "Did not stop counting after read failure" + } + return OK +} -result OK + +#---------------------------------------------------------------- +$errorname setpar errortype 1 5 +$errorname setpar recover 1 1 + +test counter-1.10 {Counter Read Recover} -body { + set ans [$countername count 2] + if { [string first "WARN" $ans] < 0} { + error "Failed to trigger count read failure: $ans" + } + set ans [SICSValue status] + if {[string first Eager $ans] < 0} { + error "Did not stop counting after read failure" + } + return OK +} -result OK + + diff --git a/test/histtest.tcl b/test/histtest.tcl new file mode 100644 index 00000000..c48f48ba --- /dev/null +++ b/test/histtest.tcl @@ -0,0 +1,342 @@ +#--------------------------------------------------------------------------- +# This is for testing the histogram memory code +# The errortypes are the same as in the counter module +# +# Mark Koennecke, October 2006 +#--------------------------------------------------------------------------- +puts stdout "Testing Histogram Memory" +config rights Mugger Mugegr +hm config errotype 0 +hm init + +test hm-1.0 {Test Mode Setting} -body { + config rights Spy Spy + set res [eval hm countmode monitor] + if {[string first ERROR $res] < 0} { + error "Managed to set parameter even if not allowed" + } + config rights User User + set res [eval hm countmode monitor] + if {[string first ERROR $res] >= 0} { + error "Setting parameter failed with $res" + } + set readback [SICSValue "hm countmode"] + compareValue [string tolower $readback] monitor + config rights Spy Spy + set res [eval hm countmode timer] + if {[string first ERROR $res] < 0} { + error "Managed to set parameter even if not allowed" + } + config rights User User + set res [eval hm countmode timer] + if {[string first ERROR $res] >= 0} { + error "Setting parameter failed with $res" + } + set readback [SICSValue "hm countmode"] + compareValue [string tolower $readback] timer + return OK +} -result OK +#------------------------------------------------------------------- +test hm-1.1 {Test Preset Setting} -body { + config rights Spy Spy + set val 12 + set res [eval hm preset $val] + if {[string first ERROR $res] < 0} { + error "Managed to set parameter even if not allowed" + } + config rights User User + set res [eval hm preset $val] + if {[string first ERROR $res] >= 0} { + error "Setting parameter failed with $res" + } + set readback [SICSValue "hm preset"] + compareValue $readback $val + return "OK" +} -result OK +#--------------------------------------------------------------------- +test hm-1.3 {Test Normal Counting} -body { + config rights Spy Spy + set status [catch {testNBCounting "hm count" 11} msg] + if {$status == 0} { + error "Counted in spite of lacking privilege" + } + config rights User User + testOK "hm countmode timer" + testOK "hm preset 5" + testNBCounting "hm count" 11 +} -result OK +#--------------------------------------------------------------------- +test hm-1.4 {Test Blocking Counting} -body { + config rights Spy Spy + set status [catch {testBlockCounting "hm countblock" 11} msg] + if {$status == 0} { + error "Counted in spite of lacking privilege" + } + config rights User User + testBlockCounting "hm countblock" 11 +} -result OK +#-------------------------------------------------------------------- +test hm-1.5 {Interrupted Counting} -body { + hm preset 10 + testInterruptedCount "hm count" +} -result OK +#-------------------------------------------------------------------- +config rights User User + +test hm-1.51 {Pause Counting Test} -body { + global socke + hm preset 300 + hm count + exec sleep 1 + set ans [status] + if {[string first Counting $ans] < 0} { + error "Failed to start counting: $ans" + } + pause + exec sleep 1 + set ans [status] + if {[string first Paus $ans] < 0} { + error "Failed to pause counting: $ans" + } + puts $socke continue + flush $socke + exec sleep 1 + set ans [status] + if {[string first Count $ans] < 0} { + error "Failed to continue counting: $ans" + } + puts $socke "INT1712 3" + flush $socke + set ans [status] + return OK +} -result OK +#--------------------------------------------------------- +test hm-1.52 {Pause Interrupt Test} -body { + global socke + hm count 300 + exec sleep 2 + set ans [status] + if {[string first Counting $ans] < 0} { + error "Failed to start counting: $ans" + } + pause + exec sleep 1 + set ans [status] + if {[string first Paus $ans] < 0} { + error "Failed to pause counting: $ans" + } + puts $socke "INT1712 3" + flush $socke + set ans [status] + if {[string first Eager $ans] < 0} { + error "Failed to interrupt paused counting: $ans" + } + return OK +} -result OK +#-------------------------------------------------------------------- +config rights Mugger Mugger +hm configure errortype 1 +hm configure recover 0 +hm init +hm preset 10 + +test hm-1.6 {Counting Start Failure} -body { + set ans [hm count] + if { [string first "cannot start" $ans] < 0} { + error "Failed to trigger count start failure: $ans" + } + return OK +} -result OK +#------------------------------------------------------------------------- +hm configure errortype 1 +hm configure recover 1 +hm init + +test hm-1.7 {Counting Start Failure with Recovery} -body { + set ans [hm count] + if { [string first "WARNING" $ans] < 0} { + error "Failed to trigger count start failure: $ans" + } + set ans [SICSValue status] + if {[string first Counting $ans] < 0} { + error "Did not recover from start failure" + } + exec sleep 15 + set ans [SICSValue status] + if {[string first Eager $ans] < 0} { + error "Did not stop counting after start failure" + } + return OK +} -result OK +#---------------------------------------------------------------------- +hm configure errortype 2 +hm configure recover 0 +hm init + +test hm-1.8 {Counting Status Failure} -body { + set ans [hm count] + set ans [status] + if { [string first "Full Stop called" $ans] < 0} { + error "Failed to trigger count start failure: $ans" + } + return OK +} -result OK +#--------------------------------------------------------------- +hm configure errortype 2 +hm configure recover 1 +hm init + +test hm-1.9 {Counting Status Failure with Recovery} -body { + set ans [hm count 10] + set ans [status] + if { [string first "WARNING" $ans] < 0} { + error "Failed to trigger count start failure: $ans" + } + if {[string first Counting $ans] < 0} { + error "Did not recover from status failure" + } + exec sleep 12 + set ans [SICSValue status] + if {[string first Eager $ans] < 0} { + error "Did not stop counting after status failure" + } + return OK +} -result OK +#---------------------------------------------------------------------- +hm configure errortype 0 +hm configure recover 0 +hm configure testval 3 +hm init +hm preset 2 +hm countblock + +test hm-1.10 {Test data} -body { + set expected [SICSValue "hm configure dim0"] + set data [hm get 0] + if {[string first ERROR $data] >= 0} { + error "Failed to read HM" + } + if {[string first Histogram $data] < 0} { + error "Bad response from HM" + } + set l [split $data =] + set data [lindex $l 1] + set l [split $data] + set count 0 + foreach e $l { + if {![string is space $e]} { + incr count + if {$e != 3} { + error "Bad value in HM, got $e, expected 3" + } + } + } + if {$count != $expected} { + error "HM Datasize does not match, expected $expected, got $count" + } + return OK +} -result OK + +hm configure testval 7 +hm init +hm countblock + +test hm-1.11 {Test data second} -body { + set expected [SICSValue "hm configure dim0"] + set data [hm get 0] + if {[string first ERROR $data] >= 0} { + error "Failed to read HM" + } + if {[string first Histogram $data] < 0} { + error "Bad response from HM" + } + set l [split $data =] + set data [lindex $l 1] + set l [split $data] + set count 0 + foreach e $l { + if {![string is space $e]} { + incr count + if {$e != 7} { + error "Bad value in HM, got $e, expected 3" + } + } + } + if {$count != $expected} { + error "HM Datasize does not match, expected $expected, got $count" + } + return OK +} -result OK +#-------------------------- --------------------------------------------- +test hm-1.13 {Test hm sum} -body { + set test [SICSValue "hm sum 2 4"] + if {$test != 14} { + error "Summing HM failed, expected 14 got $test" + } + return OK +} -result OK +#-------------------------------------------------------------------------- +test hm-1.14 {Test Setting Time Binning} -body { + config rights Mugger Mugger + testOK "tof genbin 50 20 70" + testOK "tof configure testval 1" + testOK "tof init" + return OK +} -result OK +#------------------------------------------------------------------------- +test hm-1.15 {Test Reading Time Binning} -body { + set tst [SICSValue "tof notimebin"] + if {$tst != 70} { + error "NTOF bad, expected 70, got $tst" + } + set tdata [SICSValue "tof timebin"] + set l [split $tdata] + set count 0 + foreach v $l { + if {![string is space $v]} { + set tval [expr 50.0 + $count*20] + if {abs($v - $tval) > 1} { + error "Bad time value $v, expected $tval" + } + incr count + } + } + if {$count < $tst} { + error "Insufficient number of timebins: $count, expected $tst" + } + return OK +} -result OK +#---------------------------------------------------------------------- +tof countmode timer +tof preset 2 +tof countblock +test hm-1.16 {Test Reading TOF Data} -body { + set ntof [SICSValue "tof notimebin"] + set dim [SICSValue "tof configure dim0"] + set alldata [expr $ntof*$dim] + set tdata [SICSValue "tof get 0"] + set l [split $tdata] + set count 0 + foreach v $l { + if {![string is space $v]} { + if {abs($v - 1) > .1} { + error "Bad data value $v, expected $tval" + } + incr count + } + } + if {$count < $alldata} { + error "Datapoints missing, got $count, expected $alldata" + } + return OK +} -result OK +#---------------------------------------------------------------------- +tof initval 1 +test hm-1.16 {Test TOF Sum} -body { + set val [SICSValue "tof sum 2 3 0 20"] + if {$val != 20 } { + error " tof sum failed, expected 20 received $val" + } + return OK +} -result OK + diff --git a/test/interrupt.tcl b/test/interrupt.tcl new file mode 100755 index 00000000..0389cbff --- /dev/null +++ b/test/interrupt.tcl @@ -0,0 +1,14 @@ +#!/usr/bin/tclsh +#------------------------------------------------------------ +# This is a little script which issues an interrupt to SICS +# after five seconds +# +# Mark Koennecke, October 2006 +#------------------------------------------------------------ +source sicstcldebug.tcl +config rights Mugger Mugger +exec sleep 5 +puts $socke "INT1712 3" +exit 1 + + \ No newline at end of file diff --git a/test/job1.tcl b/test/job1.tcl new file mode 100644 index 00000000..b0a882ec --- /dev/null +++ b/test/job1.tcl @@ -0,0 +1,5 @@ +#---------------------------------------------------------- +# One of the job files for testing batch file processing +#---------------------------------------------------------- +clientput "Job1 batch file" +wait 20 diff --git a/test/job2.tcl b/test/job2.tcl new file mode 100644 index 00000000..6dae8a59 --- /dev/null +++ b/test/job2.tcl @@ -0,0 +1,6 @@ +#---------------------------------------------------------- +# One of the job files for testing batch file processing +#---------------------------------------------------------- +clientput "NestOne" +exe job3.tcl +wait 3 diff --git a/test/job3.tcl b/test/job3.tcl new file mode 100644 index 00000000..155bdc1a --- /dev/null +++ b/test/job3.tcl @@ -0,0 +1,6 @@ +#---------------------------------------------------------- +# One of the job files for testing batch file processing +#---------------------------------------------------------- +clientput "NestTwo" +exe job4.tcl +wait 3 diff --git a/test/job4.tcl b/test/job4.tcl new file mode 100644 index 00000000..8d562ec6 --- /dev/null +++ b/test/job4.tcl @@ -0,0 +1,5 @@ +#---------------------------------------------------------- +# One of the job files for testing batch file processing +#---------------------------------------------------------- +clientput "NestThree" +wait 3 diff --git a/test/mottest.tcl b/test/mottest.tcl new file mode 100644 index 00000000..9c6f7b23 --- /dev/null +++ b/test/mottest.tcl @@ -0,0 +1,303 @@ +#------------------------------------------------------------------------------ +# Regression tests fo a motor. It is assumed that the motors name is +# brumm and that it has been initialized with the regress motor +# driver. Moreover, this has to be loaded after tcltest.tcl, testutil.tcl +# and sicstcldebug.tcl +# +# The regression motor has various errortypes which can be simulated: +# 0 = none +# 1 = failed start +# 2 = position fault +# 3 = hardware failure +# 4 = off position, without explicit position fault +# 5 = failed read +# 6 = keep running (for testing interupting) +# Moreover there is a recover flag which causes the motor to recover when it is +# 1 +# +# copyright: see file COPYRIGHT +# +# Mark Koennecke, July 2006 +# +#------------------------------------------------------------------------------ +puts stdout "Testing motor code" + +test motorpar-1.0 {Test sll} -body { + testPar "brumm softlowerlim" -175 User } -result OK + +test motorpar-1.1 {Test slu} -body { + testPar "brumm softupperlim" 175 User } -result OK + +test motorpar-1.2 {Test ss} -body { + testPar "brumm softzero" 5 User } -result OK + +test motorpar-1.3 {Test interrupt} -body { + testPar "brumm interruptmode" 2 Mugger } -result OK + +test motorpar-1.4 {Test accesscode} -body { + testPar "brumm accesscode" 3 Mugger } -result OK + +test motorpar-1.5 {Test precision} -body { + testPar "brumm precision" .5 Mugger } -result OK + +test motorpar-1.6 {Test fail} -body { + testPar "brumm failafter" 5 Mugger } -result OK + +test motorpar-1.7 {Test retry} -body { + testPar "brumm maxretry" 5 Mugger } -result OK + +test motorpar-1.8 {Test ignorefault} -body { + testPar "brumm ignorefault" 1 Mugger } -result OK + +test motorpar-1.9 {Test movecount} -body { + testPar "brumm movecount" 12 Mugger } -result OK + +test motorpar-1.10 {Test hardupper} -body { + testROPar "brumm hardupperlim" 180 } -result OK + +test motorpar-1.11 {Test hardlower} -body { + testROPar "brumm hardlowerlim" -180 } -result OK + +brumm recover 0 +brumm errortype 0 +test motor-1.0 {Test Normal Driving} -body { + testDrive brumm 10 User } -result OK + +brumm errortype 6 +test motor-1.1 {Test Interrupting} -body { + testDriveInterrupt brumm 0 } -result OK + +brumm errortype 1 +config rights User User + +test motor-1.2 {Test Start Failure} -body { + set ans [drive brumm 20.3] + if { [string first "Failed to start motor" $ans] < 0} { + error "Failed to trigger motor start failure: $ans" + } + set ans [status] + if { [string first "Eager" $ans] < 0} { + error "Motor did not return from start failure" + } + return OK +} -result OK + +brumm recover 1 +test motor-1.3 {Test Recover from start problem} -body { + set ans [drive brumm 20.3] + if { [string first "Failed to start motor" $ans] < 0} { + error "Failed to trigger motor start failure" + } + set ans [status] + if { [string first "Eager" $ans] < 0} { + error "Motor did not return from start failure" + } + compareValue [SICSValue brumm] 20.3 +} -result OK + +brumm recover 0 +brumm errortype 2 + +test motor-1.4 {Test Position Failure} -body { + set ans [drive brumm -20.3] + if { [string first "Position not reached" $ans] < 0} { + error "Failed to trigger motor position fault: $ans" + } + set ans [status] + if { [string first "Eager" $ans] < 0} { + error "Motor did not return from position failure" + } + set ans [SICSValue brumm] + if {abs($ans - -20.3) < .01} { + error "Motor positioned OK inspite of position fault" + } + return OK +} -result OK + +brumm recover 1 + +test motor-1.6 {Test Position Failure Recover} -body { + set ans [drive brumm 20.3] + if { [string first "Position not reached" $ans] < 0} { + error "Failed to trigger motor position fault: $ans" + } + if { [string first "restarting" $ans] < 0} { + error "Restarting message not received" + } + set ans [status] + if { [string first "Eager" $ans] < 0} { + error "Motor did not return from position failure" + } + compareValue [SICSValue brumm] 20.3 + return OK +} -result OK + +brumm errortype 3 +brumm recover 0 +test motor-1.7 {Test Hardware Failure} -body { + set ans [drive brumm 20.3] + if { [string first "Hardware is mad" $ans] < 0} { + error "Failed to trigger motor hardware fault: $ans" + } + set ans [status] + if { [string first "Eager" $ans] < 0} { + error "Motor did not return from position failure" + } + set ans [SICSValue brumm] + if {abs($ans - 20.3) < .01} { + error "Motor positioned OK inspite of hardware fault" + } + return OK +} -result OK + +brumm recover 1 + +test motor-1.8 {Test Hardware Failure Recover} -body { + set ans [drive brumm 20.3] + if { [string first "Hardware is mad" $ans] < 0} { + error "Failed to trigger motor hardware fault: $ans" + } + if { [string first "restarting" $ans] < 0} { + error "Restarting message not received" + } + set ans [status] + if { [string first "Eager" $ans] < 0} { + error "Motor did not return from hardware failure" + } + compareValue [SICSValue brumm] 20.3 + return OK +} -result OK + +brumm errortype 4 + +test motor-1.9 {Test Consistent Mispositioning} -body { + set ans [drive brumm -20.3] + if { [string first "off position" $ans] < 0} { + error "Failed to trigger motor off position" + } + if { [string first "restarting" $ans] < 0} { + error "Restarting message not received" + } + if { [string first "aborting" $ans] < 0} { + error "Aborting message not received" + } + set ans [status] + if { [string first "Eager" $ans] < 0} { + error "Motor did not return after consistent position problem" + } + set ans [SICSValue brumm] + if {abs($ans - -20.3) < .01} { + error "Motor positioned OK inspite of mispositioning" + } + return OK +} -result OK + +brumm errortype 0 +drive brumm 27. + +brumm errortype 5 +brumm recover 0 + +test motor-1.10 {Failed read} -body { + set ans [brumm] + if { [string first "Failed to read" $ans] < 0} { + error "Failed to trigger motor failed read" + } + if { [string first "Error obtaining position" $ans] < 0} { + error "Failed to abort reading" + } + if { [string first "cannot fix" $ans] < 0} { + error "Missing cannot fix message" + } + set ans [status] + if { [string first "Eager" $ans] < 0} { + error "Motor did not return after failed read" + } + return OK +} -result OK +brumm recover 1 + +test motor-1.11 {Failed read recover} -body { + set ans [brumm] + if { [string first "Failed to read" $ans] < 0} { + error "Failed to trigger motor failed read" + } + if { [string first "brumm =" $ans] < 0} { + error "Motor did not return value after fixing failed read" + } + set ans [status] + if { [string first "Eager" $ans] < 0} { + error "Motor did not return after failed read" + } + return OK +} -result OK + +brumm errortype 0 +drive brumm 27. +brumm recover 0 +brumm errortype 3 + +test motor-1.12 {Motor Alarm} -body { + drive brumm -27 + drive brumm -27 + drive brumm -27 + drive brumm -27 + set ans [drive brumm -27] + if { [string first "MOTOR ALARM" $ans] < 0} { + error "Motor did not stop with Alarm" + } + set ans [status] + if { [string first "Eager" $ans] < 0} { + error "Motor did not return after Alarm" + } + brumm errortype 0 + set ans [drive brumm -27] + if { [string first "sucessfully" $ans] < 0} { + error "Motor did not recover after Alarm" + } + return OK +} -result OK + +brumm errortype 0 +config rights Mugger Mugger + +test motor-1.13 {Motor Sign} -body { + brumm sign 1 + drive brumm 27. + set old [SICSValue brumm] + brumm sign -1 + set newVal [SICSValue brumm] + set br [brumm sign] + brumm sign 1 + return [compareValue [expr $old * -1] $newVal] +} -result OK + +brumm sign 1. + +test motor-1.14 {Motor Recover} -body { + brumm sign -1. + brumm softzero 5 + set data [brumm list] + backup hugo.bck + recover hugo.bck + set newData [brumm list] + if {[string compare $data $newData] != 0} { + backup hugo2.bck + error "Recovery failed: look at diff between hugo.bck and hugo2.bck" + } + brumm sign 1 + brumm softzero 0 + set data [brumm list] + backup hugo.bck + recover hugo.bck + set newData [brumm list] + if {[string compare $data $newData] != 0} { + backup hugo2.bck + error "Recovery failed: look at diff between hugo.bck and hugo2.bck" + } + return OK +} -result OK + +config rights Spy Spy +test motorpar-1.15 {Test sign setting} -body { + testPar "brumm sign" -1 Mugger } -result OK diff --git a/test/nxscripttest.tcl b/test/nxscripttest.tcl new file mode 100644 index 00000000..6bcf28b4 --- /dev/null +++ b/test/nxscripttest.tcl @@ -0,0 +1,117 @@ +#--------------------------------------------------------------------------- +# Regression tests for the SICS nxscript module. +# +# Mark Koennecke, November 2006 +#--------------------------------------------------------------------------- + +puts stdout "Testing NXScript" + +test nxscript-1.0 {Test opening file} -body { + config rights Spy Spy + testCommand "nxscript createxml test.xml test.dic" ERROR + config rights User User + testOK "nxscript createxml test.xml test.dic" +} -result OK + +test nxscript-1.1 {Write text} -body { + testNoError "nxscript puttext text Hugo ist eine Nassnase" +} -result OK + +test nxscript-1.2 {Write float} -body { + testNoError "nxscript putfloat testfloat 27.8" +} -result OK + +test nxscript-1.3 {Write int} -body { + testNoError "nxscript putint testint 177" +} -result OK + +drive a4 15 +a4 softzer0 1. + +test nxscript-1.4 {Write motor} -body { + testNoError "nxscript putmot testmot a4" +} -result OK + +aba count 10 +test nxscript-1.5 {Write counter} -body { + testNoError "nxscript putcounter testcter aba" +} -result OK + +hm initval 55 +test nxscript-1.6 {Write HM} -body { + testNoError "nxscript puthm testhm hm" +} -result OK + +config rights Mugger Mugger +tof genbin 500 300 20 +tof init +config rights User User + +test nxscript-1.7 {Write time binning} -body { + testNoError "nxscript puttimebinning testhmtb tof" +} -result OK + + +test nxscript-1.8 {Write array } -body { + makearray + set t [nxscript putarray testar ar 10] + if {[string first ERROR $t] >= 0 || [string first WARNING $t] >= 0} { + error "Failed to write array with: $t" + } + return OK +} -result OK + +test nxscript-1.9 {Write int array } -body { + makeintarray + set t [nxscript putintarray testintar ar 10] + if {[string first ERROR $t] >= 0 || [string first WARNING $t] >= 0} { + error "Failed to write intarray with: $t" + } + return OK +} -result OK + +test nxscript-1.10 {Write global } -body { + testNoError "nxscript putglobal Instrument Washmaschine" +} -result OK + +test nxscript-1.11 {Write attribute } -body { + testNoError "nxscript putattribute testhm signal 1" +} -result OK + +test nxscript-1.12 {Writing link } -body { + testNoError "nxscript makelink testlink text" +} -result OK + +test nxscript-1.13 {Writing sicsdata } -body { + hm initval 23 + data clear + data copyhm 0 hm + testNoError "nxscript putsicsdata testsd data" +} -result OK + +test nxscript-1.14 {Writing slabs } -body { + data clear + data putfloat 0 1.1 + testNoError "nxscript putslab testslab [list 0] [list 1] data" + data putfloat 0 2.2 + testNoError "nxscript putslab testslab [list 1] [list 1] data" + data putfloat 0 3.3 + testNoError "nxscript putslab testslab [list 2] [list 1] data" +} -result OK + + +test nxscript-1.20 {Close file} -body { + testOK "nxscript close" +} -result OK + +test nxscript-1.21 {Testing file content } -body { + set status [catch {exec diff --ignore-matching-lines=file_time test.xml testsoll.xml} msg] + if {$status != 0} { + error "Difference in NXSCRIP-XML file: $msg" + } + return OK +} -result OK + + + + \ No newline at end of file diff --git a/test/object.tcl b/test/object.tcl new file mode 100644 index 00000000..7aa7e856 --- /dev/null +++ b/test/object.tcl @@ -0,0 +1,305 @@ +# +# $Id$ +# +# This software is copyright (C) 1995 by the Lawrence Berkeley Laboratory. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that: (1) source code distributions +# retain the above copyright notice and this paragraph in its entirety, (2) +# distributions including binary code include the above copyright notice and +# this paragraph in its entirety in the documentation or other materials +# provided with the distribution, and (3) all advertising materials mentioning +# features or use of this software display the following acknowledgement: +# ``This product includes software developed by the University of California, +# Lawrence Berkeley Laboratory and its contributors.'' Neither the name of +# the University nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# + +set object_priv(currentClass) {} +set object_priv(objectCounter) 0 + +#---------------------------------------------------------------------- +proc object_class {name spec} { + global object_priv + set object_priv(currentClass) $name + lappend object_priv(objects) $name + upvar #0 ${name}_priv class + set class(__members) {} + set class(__methods) {} + set class(__params) {} + set class(__class_vars) {} + set class(__class_methods) {} + uplevel $spec + proc $name:config args "uplevel \[concat object_config \$args]" + proc $name:configure args "uplevel \[concat object_config \$args]" + proc $name:cget {self option} "uplevel \[list object_cget \$self \$option]" +} +#--------------------------------------------------------------------- +proc method {name args body} { + global object_priv + set className $object_priv(currentClass) + upvar #0 ${className}_priv class + if {[lsearch $class(__methods) $name] < 0} { + lappend class(__methods) $name + } + set methodArgs self + append methodArgs " " $args + proc $className:$name $methodArgs "upvar #0 \$self slot ${className}_priv class_var\n$body" +} +#------------------------------------------------------------------ +proc object_method {name {defaultValue {}}} [info body method] +#------------------------------------------------------------------ +proc member {name {defaultValue {}}} { + global object_priv + set className $object_priv(currentClass) + upvar #0 ${className}_priv class + lappend class(__members) [list $name $defaultValue] +} +#---------------------------------------------------------------------- +proc object_member {name {defaultValue {}}} [info body member] +#--------------------------------------------------------------------- +proc param {name {defaultValue {}} {resourceClass {}} {configCode {}}} { + global object_priv + set className $object_priv(currentClass) + upvar #0 ${className}_priv class + if {$resourceClass == ""} { + set resourceClass \ + [string toupper [string index $name 0]][string range $name 1 end] + } + if ![info exists class(__param_info/$name)] { + lappend class(__params) $name + } + set class(__param_info/$name) [list $defaultValue $resourceClass] + if {$configCode != {}} { + proc $className:config:$name self $configCode + } +} +#------------------------------------------------------------------------- +proc object_param {name {defaultValue {}} {resourceClass {}} {configCode {}}} \ + [info body param] + +#-------------------------------------------------------------------------- +proc object_class_var {name {initialValue ""}} { + global object_priv + set className $object_priv(currentClass) + upvar #0 ${className}_priv class + set class($name) $initialValue + set class(__initial_value.$name) $initialValue + lappend class(__class_vars) $name +} +#--------------------------------------------------------------------------- +proc object_class_method {name args body} { + global object_priv + set className $object_priv(currentClass) + upvar #0 ${className}_priv class + if {[lsearch $class(__class_methods) $name] < 0} { + lappend class(__class_methods) $name + } + proc $className:$name $args "upvar #0 ${className}_priv class_var\n$body" +} +#--------------------------------------------------------------------------- +proc object_include {super_class_name} { + global object_priv + set className $object_priv(currentClass) + upvar #0 ${className}_priv class + upvar #0 ${super_class_name}_priv super_class + foreach p $super_class(__params) { + lappend class(__params) $p + set class(__param_info/$p) $super_class(__param_info/$p) + } + set class(__members) [concat $super_class(__members) $class(__members)] + set class(__class_vars) \ + [concat $super_class(__class_vars) $class(__class_vars)] + foreach v $super_class(__class_vars) { + set class($v) \ + [set class(__initial_value.$v) $super_class(__initial_value.$v)] + } + set class(__class_methods) \ + [concat $super_class(__class_methods) $class(__class_methods)] + set class(__methods) \ + [concat $super_class(__methods) $class(__methods)] + foreach m $super_class(__methods) { + set proc $super_class_name:$m + proc $className:$m [object_get_formals $proc] [info body $proc] + } + foreach m $super_class(__class_methods) { + set proc $super_class_name:$m + regexp "^\[^\n\]+\n(.*)" [info body $proc] dummy body + proc $className:$m [object_get_formals $proc] \ + "upvar #0 ${className}_priv class_var\n$body" + } +} +#--------------------------------------------------------------------------- +proc object_new {className {name {}}} { + if {$name == {}} { + global object_priv + set name O_[incr object_priv(objectCounter)] + } + upvar #0 $name object + upvar #0 ${className}_priv class + set object(__class) $className + foreach var $class(__params) { + set info $class(__param_info/$var) + set resourceClass [lindex $info 1] + if ![catch {set val [option get $name $var $resourceClass]}] { + if {$val == ""} { + set val [lindex $info 0] + } + } else { + set val [lindex $info 0] + } + set object($var) $val + } + foreach var $class(__members) { + set object([lindex $var 0]) [lindex $var 1] + } + proc $name {method args} [format { + upvar #0 %s object + uplevel [concat $object(__class):$method %s $args] + } $name $name] + return $name +} +#--------------------------------------------------------------- +proc object_define_creator {windowType name spec} { + object_class $name $spec + if {[info procs $name:create] == {}} { + error "widget \"$name\" must define a create method" + } + if {[info procs $name:reconfig] == {}} { + error "widget \"$name\" must define a reconfig method" + } + proc $name {window args} [format { + %s $window -class %s + rename $window object_window_of$window + upvar #0 $window object + set object(__window) $window + object_new %s $window + proc %s:frame {self args} \ + "uplevel \[concat object_window_of$window \$args]" + uplevel [concat $window config $args] + $window create + set object(__created) 1 + bind $window \ + "if !\[string compare %%W $window\] { object_delete $window }" + $window reconfig + return $window + } $windowType \ + [string toupper [string index $name 0]][string range $name 1 end] \ + $name $name] +} +#------------------------------------------------------------------ +proc object_config {self args} { + upvar #0 $self object + set len [llength $args] + if {$len == 0} { + upvar #0 $object(__class)_priv class + set result {} + foreach param $class(__params) { + set info $class(__param_info/$param) + lappend result \ + [list -$param $param [lindex $info 1] [lindex $info 0] \ + $object($param)] + } + if [info exists object(__window)] { + set result [concat $result [object_window_of$object(__window) config]] + } + return $result + } + if {$len == 1} { + upvar #0 $object(__class)_priv class + if {[string index $args 0] != "-"} { + error "param '$args' didn't start with dash" + } + set param [string range $args 1 end] + if {[set ndx [lsearch -exact $class(__params) $param]] == -1} { + if [info exists object(__window)] { + return [object_window_of$object(__window) config -$param] + } + error "no param '$args'" + } + set info $class(__param_info/$param) + return [list -$param $param [lindex $info 1] [lindex $info 0] \ + $object($param)] + } + # accumulate commands and eval them later so that no changes will take + # place if we find an error + set cmds "" + while {$args != ""} { + set fieldId [lindex $args 0] + if {[string index $fieldId 0] != "-"} { + error "param '$fieldId' didn't start with dash" + } + set fieldId [string range $fieldId 1 end] + if ![info exists object($fieldId)] { + if {[info exists object(__window)]} { + if [catch [list object_window_of$object(__window) config -$fieldId]] { + error "tried to set param '$fieldId' which did not exist." + } else { + lappend cmds \ + [list object_window_of$object(__window) config -$fieldId [lindex $args 1]] + set args [lrange $args 2 end] + continue + } + } + + } + if {[llength $args] == 1} { + return $object($fieldId) + } else { + lappend cmds [list set object($fieldId) [lindex $args 1]] + if {[info procs $object(__class):config:$fieldId] != {}} { + lappend cmds [list $self config:$fieldId] + } + set args [lrange $args 2 end] + } + } + foreach cmd $cmds { + eval $cmd + } + if {[info exists object(__created)] && [info procs $object(__class):reconfig] != {}} { + $self reconfig + } +} + +proc object_cget {self var} { + upvar #0 $self object + return [lindex [object_config $self $var] 4] +} +#--------------------------------------------------------------------------- +proc object_delete self { + upvar #0 $self object + if {[info exists object(__class)] && [info commands $object(__class):destroy] != ""} { + $object(__class):destroy $self + } + if [info exists object(__window)] { + if [string length [info commands object_window_of$self]] { + catch {rename $self {}} + rename object_window_of$self $self + } + destroy $self + } + catch {unset object} +} +#-------------------------------------------------------------------------- +proc object_slotname slot { + upvar self self + return [set self]($slot) +} +#-------------------------------------------------------------------------- +proc object_get_formals {proc} { + set formals {} + foreach arg [info args $proc] { + if [info default $proc $arg def] { + lappend formals [list $arg $def] + } else { + lappend formals $arg + } + } + return $formals +} diff --git a/test/optitest.tcl b/test/optitest.tcl new file mode 100644 index 00000000..824f311e --- /dev/null +++ b/test/optitest.tcl @@ -0,0 +1,171 @@ +#-------------------------------------------------------------------- +# This file contaisn test for the peak optimization routines in SICS +# +# Mark Koennecke, October 2006 +#------------------------------------------------------------------- +clientput "Testing optimization routines...." + +config rights Mugger Mugger +aba setpar errortype 1 0 +config rights User User + +test opt-1.0 {Test Normal Optimizer} -body { + testOK "opti clear" + testOK "opti addvar a4 .3 20 .3" + testOK "opti preset 2" + testOK "opti countmode timer" + drive a4 4. + set result [eval opti run] + if { [string first ERROR $result] > 0} { + puts stdout $result + error "Error occurred during optimization run" + } + if {[string first finished $result] < 0} { + error "Optimization did not finish normally" + } + set val [SICSValue a4] + if {abs($val - 5.3) > .1} { + error "Optimisation did not arrive at desired position" + } + return OK +} -result OK + +test opt-1.1 {Test Normal Optimizer Aborting} -body { + testOK "opti clear" + testOK "opti addvar a4 .3 20 .3" + testOK "opti preset 2" + testOK "opti countmode timer" + drive a4 4. + exec interrupt.tcl & + set result [eval opti run] + if {[string first "Scan aborted" $result] < 0} { + error "Optimiser did not abort + } + set result [status] + if { [string first Eager $result] < 0} { + puts stdout $result + error "Optimiser did not interrupt!" + } + return OK +} -result OK + +test opt-1.2 {Test Climbing } -body { + testOK "opti clear" + testOK "opti addvar a4 .3 20 .5" + testOK "opti preset 2" + testOK "opti countmode timer" + drive a4 4. + set result [eval opti climb] + if { [string first ERROR $result] > 0} { + puts stdout $result + error "Error occurred during optimization run" + } + if {[string first finished $result] < 0} { + error "Optimization did not finish normally" + } + set val [SICSValue a4] + if {abs($val - 5.3) > .5} { + error "Optimisation did not arrive at desired position" + } + return OK +} -result OK + +test opt-1.3 {Test Climbing } -body { + testOK "opti clear" + testOK "opti addvar a4 .3 20 .5" + testOK "opti preset 2" + testOK "opti countmode timer" + drive a4 6. + set result [eval opti climb] + if { [string first ERROR $result] > 0} { + puts stdout $result + error "Error occurred during optimization run" + } + if {[string first finished $result] < 0} { + error "Optimization did not finish normally" + } + set val [SICSValue a4] + if {abs($val - 5.3) > .5} { + error "Optimisation did not arrive at desired position" + } + return OK +} -result OK + +test opt-1.4 {Test Climbing Interruption} -body { + testOK "opti clear" + testOK "opti addvar a4 .3 20 .5" + testOK "opti preset 2" + testOK "opti countmode timer" + drive a4 4. + exec interrupt.tcl & + set result [eval opti climb] + if {[string first "Scan was aborted" $result] < 0} { + error "Optimiser did not abort" + } + set result [status] + if { [string first Eager $result] < 0} { + puts stdout $result + error "Optimiser did not interrupt!" + } + return OK +} -result OK + + +test opt-1.5 {Test Maximizer} -body { + drive a4 4. + set result [eval max a4 .2 timer 2] + if { [string first ERROR $result] > 0} { + puts stdout $result + error "Error occurred during maximization" + } + if {[string first Found $result] < 0} { + error "Optimization did not finish normally" + } + set val [SICSValue a4] + if {abs($val - 5.3) > .1} { + error "Maximization did not arrive at desired position" + } + return OK +} -result OK + +test opt-1.6 {Test Maximizer} -body { + drive a4 6. + set result [eval max a4 .2 timer 2] + if { [string first ERROR $result] > 0} { + puts stdout $result + error "Error occurred during maximization" + } + if {[string first Found $result] < 0} { + error "Optimization did not finish normally" + } + set val [SICSValue a4] + if {abs($val - 5.3) > .1} { + error "Maximization did not arrive at desired position" + } + return OK +} -result OK + +test opt-1.6 {Test Maximizer Aborting} -body { + drive a4 6. + exec interrupt.tcl & + set result [eval max a4 .2 timer 2] + if { [string first "Full Stop" $result] < 0} { + puts stdout $result + error "Maximize did not interrupt!" + } + set result [status] + if { [string first Eager $result] < 0} { + puts stdout $result + error "Maximize did not interrupt!" + } + return OK +} -result OK + +test opt-1.7 {Test Maximizer Parameter} -body { + testOK "max in360 1" + testOK "max maxpts 50" + testCommand "max in360" max.in360 + testCommand "max maxpts" max.maxpts + return OK +} -result OK + diff --git a/test/samenv/tomato/07-20.log b/test/samenv/tomato/07-20.log new file mode 100644 index 00000000..da845dd1 --- /dev/null +++ b/test/samenv/tomato/07-20.log @@ -0,0 +1,11390 @@ +#2006-07-20 00:00:00 isdst 1 period 5 exact 1 +00:00:00 4.4220 +00:00:05 4.6817 +00:00:10 4.8740 +00:00:15 4.5413 +00:00:20 4.8290 +00:00:25 4.6405 +00:00:30 4.7156 +00:00:35 4.7455 +00:00:40 4.6929 +00:00:45 4.9565 +00:00:50 4.6585 +00:00:55 4.7447 +00:01:00 4.8010 +00:01:05 4.6188 +00:01:10 4.8519 +00:01:15 4.7247 +00:01:20 4.7355 +00:01:25 4.7258 +00:01:30 4.7237 +00:01:35 4.7483 +00:01:40 4.7082 +00:01:45 4.8206 +00:01:50 4.7051 +00:01:55 4.7323 +00:02:00 4.6824 +00:02:05 4.7419 +00:02:10 4.6984 +00:02:15 4.6150 +00:02:20 4.6714 +00:02:25 4.7849 +00:02:30 4.6965 +00:02:35 4.5760 +00:02:40 4.9699 +00:02:45 4.7526 +00:02:50 4.7628 +00:02:55 4.9419 +00:03:00 4.6565 +00:03:05 4.6991 +00:03:10 4.8350 +00:03:15 4.5047 +00:03:20 4.7496 +00:03:25 4.9007 +00:03:30 4.5065 +00:03:35 4.5825 +00:03:40 4.7890 +00:03:45 4.5428 +00:03:50 4.8036 +00:03:55 4.9926 +00:04:00 4.7839 +00:04:05 4.5629 +00:04:10 4.5483 +00:04:15 4.7965 +00:04:20 4.7145 +00:04:25 4.4598 +00:04:30 4.7563 +00:04:35 4.9160 +00:04:40 4.6162 +00:04:45 4.6842 +00:04:50 4.8263 +00:04:55 4.6060 +00:05:00 4.6326 +00:05:05 4.8250 +00:05:10 4.5465 +00:05:15 4.4622 +00:05:20 4.7847 +00:05:25 5.0117 +00:05:30 4.6701 +00:05:35 4.5193 +00:05:40 4.9961 +00:05:45 4.7811 +00:05:50 4.4906 +00:05:55 4.7368 +00:06:00 4.8617 +00:06:05 4.5488 +00:06:10 4.7841 +00:06:15 4.7002 +00:06:20 4.6989 +00:06:25 4.6631 +00:06:30 4.7193 +00:06:35 4.7415 +00:06:40 4.6767 +00:06:45 4.6453 +00:06:50 4.6289 +00:06:55 4.6962 +00:07:00 4.7160 +00:07:05 4.6867 +00:07:10 4.5543 +00:07:15 4.8891 +00:07:20 4.7052 +00:07:25 4.7904 +00:07:30 4.7234 +00:07:35 4.8186 +00:07:40 4.6969 +00:07:45 4.7239 +00:07:50 4.6745 +00:07:55 4.7484 +00:08:00 4.7717 +00:08:05 4.7169 +00:08:10 4.7550 +00:08:15 4.7468 +00:08:20 4.7159 +00:08:25 4.7103 +00:08:30 4.6670 +00:08:35 4.6043 +00:08:40 4.6312 +00:08:45 4.6176 +00:08:50 4.6648 +00:08:55 4.5711 +00:09:00 4.6102 +00:09:05 4.6890 +00:09:10 4.7265 +00:09:15 4.7744 +00:09:20 4.8012 +00:09:25 4.7519 +00:09:30 4.6055 +00:09:35 4.6143 +00:09:40 4.6462 +00:09:45 4.5684 +00:09:50 4.7042 +00:09:55 4.7895 +00:10:00 4.6858 +00:10:05 4.7278 +00:10:10 4.6530 +00:10:15 4.7561 +00:10:20 4.7606 +00:10:25 4.6972 +00:10:30 4.7211 +00:10:35 4.6725 +00:10:40 4.6632 +00:10:45 4.6419 +00:10:50 4.6491 +00:10:55 4.7272 +00:11:00 4.6261 +00:11:05 4.6826 +00:11:10 4.7016 +00:11:15 4.6607 +00:11:20 4.7134 +00:11:25 4.7481 +00:11:30 4.7713 +00:11:35 4.7040 +00:11:40 4.5523 +00:11:45 4.5043 +00:11:50 4.5869 +00:11:55 4.7134 +00:12:00 4.8288 +00:12:05 4.7514 +00:12:10 4.6918 +00:12:15 4.6551 +00:12:20 4.6139 +00:12:25 4.5990 +00:12:30 4.6124 +00:12:35 4.6045 +00:12:40 4.7286 +00:12:45 4.6947 +00:12:50 4.6556 +00:12:55 4.6186 +00:13:00 4.6898 +00:13:05 4.6398 +00:13:10 4.6271 +00:13:15 4.6729 +00:13:20 4.7055 +00:13:25 4.7354 +00:13:30 4.6922 +00:13:35 4.6496 +00:13:40 4.5600 +00:13:45 4.5622 +00:13:50 4.6482 +00:13:55 4.6778 +00:14:00 4.6985 +00:14:05 4.7838 +00:14:10 4.8014 +00:14:15 4.7729 +00:14:20 4.7496 +00:14:25 4.6716 +00:14:30 4.7095 +00:14:35 4.7489 +00:14:40 4.8078 +00:14:45 4.7304 +00:14:50 4.6622 +00:14:55 4.5774 +00:15:00 4.5783 +00:15:05 4.6438 +00:15:10 4.6234 +00:15:15 4.5829 +00:15:20 4.6076 +00:15:25 4.6770 +00:15:30 4.6758 +00:15:35 4.7493 +00:15:40 4.6859 +00:15:45 4.6512 +00:15:50 4.6163 +00:15:55 4.5922 +00:16:00 4.6821 +00:16:05 4.6786 +00:16:10 4.7159 +00:16:15 4.7040 +00:16:20 4.5776 +00:16:25 4.5723 +00:16:30 4.6434 +00:16:35 4.6034 +00:16:40 4.5789 +00:16:45 4.5967 +00:16:50 4.5976 +00:16:55 4.6045 +00:17:00 4.5067 +00:17:05 4.5015 +00:17:10 4.5918 +00:17:15 4.6710 +00:17:20 4.6784 +00:17:25 4.6811 +00:17:30 4.6865 +00:17:35 4.6031 +00:17:40 4.5319 +00:17:45 4.5924 +00:17:50 4.6342 +00:17:55 4.6277 +00:18:00 4.7354 +00:18:05 4.7905 +00:18:10 4.6982 +00:18:15 4.6340 +00:18:20 4.6698 +00:18:25 4.7368 +00:18:30 4.6674 +00:18:35 4.6634 +00:18:40 4.6390 +00:18:45 4.5669 +00:18:50 4.6421 +00:18:55 4.6479 +00:19:00 4.6485 +00:19:05 4.5901 +00:19:10 4.6362 +00:19:15 4.5978 +00:19:20 4.6604 +00:19:25 4.6522 +00:19:30 4.5403 +00:19:35 4.5121 +00:19:40 4.5020 +00:19:45 4.5524 +00:19:50 4.5470 +00:19:55 4.6470 +00:20:00 4.7540 +00:20:05 4.6294 +00:20:10 4.6719 +00:20:15 4.7052 +00:20:20 4.7341 +00:20:25 4.5804 +00:20:30 4.4892 +00:20:35 4.5266 +00:20:40 4.4486 +00:20:45 4.5381 +00:20:50 4.6343 +00:20:55 4.7059 +00:21:00 4.6584 +00:21:05 4.6118 +00:21:10 4.6244 +00:21:15 4.5285 +00:21:20 4.5853 +00:21:25 4.6887 +00:21:30 4.6226 +00:21:35 4.6477 +00:21:40 4.6974 +00:21:45 4.7163 +00:21:50 4.6072 +00:21:55 4.6236 +00:22:00 4.6290 +00:22:05 4.6181 +00:22:10 4.6448 +00:22:15 4.5960 +00:22:20 4.5376 +00:22:25 4.5801 +00:22:30 4.6342 +00:22:35 4.6794 +00:22:40 4.6625 +00:22:45 4.5974 +00:22:50 4.5335 +00:22:55 4.5006 +00:23:00 4.5708 +00:23:05 4.6173 +00:23:10 4.6414 +00:23:15 4.7294 +00:23:20 4.6904 +00:23:25 4.5300 +00:23:30 4.5055 +00:23:35 4.5294 +00:23:40 4.5644 +00:23:45 4.6120 +00:23:50 4.5459 +00:23:55 4.5728 +00:24:00 4.6435 +00:24:05 4.5234 +00:24:10 4.5409 +00:24:15 4.6420 +00:24:20 4.5347 +00:24:25 4.4408 +00:24:30 4.4648 +00:24:35 4.6354 +00:24:40 4.7121 +00:24:45 4.5644 +00:24:50 4.6285 +00:24:55 4.6569 +00:25:00 4.6329 +00:25:05 4.5035 +00:25:10 4.5827 +00:25:15 4.6283 +00:25:20 4.5210 +00:25:25 4.6864 +00:25:30 4.6783 +00:25:35 4.5636 +00:25:40 4.4468 +00:25:45 4.5108 +00:25:50 4.5875 +00:25:55 4.5955 +00:26:00 4.6033 +00:26:05 4.5904 +00:26:10 4.5635 +00:26:15 4.6481 +00:26:20 4.5530 +00:26:25 4.4963 +00:26:30 4.4125 +00:26:35 4.4791 +00:26:40 4.6544 +00:26:45 4.6642 +00:26:50 4.6169 +00:26:55 4.6023 +00:27:00 4.5304 +00:27:05 4.5282 +00:27:10 4.5902 +00:27:15 4.6882 +00:27:20 4.5866 +00:27:25 4.4517 +00:27:30 4.5170 +00:27:35 4.4722 +00:27:40 4.4385 +00:27:45 4.5143 +00:27:50 4.5300 +00:27:55 4.5571 +00:28:00 4.5760 +00:28:05 4.5816 +00:28:10 4.6103 +00:28:15 4.6614 +00:28:20 4.6084 +00:28:25 4.5492 +00:28:30 4.5768 +00:28:35 4.6348 +00:28:40 4.6360 +00:28:45 4.6178 +00:28:50 4.5579 +00:28:55 4.5451 +00:29:00 4.4516 +00:29:05 4.5316 +00:29:10 4.5465 +00:29:15 4.4857 +00:29:20 4.5358 +00:29:25 4.5520 +00:29:30 4.5424 +00:29:35 4.5882 +00:29:40 4.6289 +00:29:45 4.5276 +00:29:50 4.4803 +00:29:55 4.5501 +00:30:00 4.5949 +00:30:05 4.4963 +00:30:10 4.4566 +00:30:15 4.5125 +00:30:20 4.5830 +00:30:25 4.5704 +00:30:30 4.5960 +00:30:35 4.6502 +00:30:40 4.6714 +00:30:45 4.4935 +00:30:50 4.5360 +00:30:55 4.5935 +00:31:00 4.5341 +00:31:05 4.5745 +00:31:10 4.5400 +00:31:15 4.4936 +00:31:20 4.5170 +00:31:25 4.4749 +00:31:30 4.5411 +00:31:35 4.5790 +00:31:40 4.6014 +00:31:45 4.5899 +00:31:50 4.6218 +00:31:55 4.5692 +00:32:00 4.5669 +00:32:05 4.5436 +00:32:10 4.5633 +00:32:15 4.5415 +00:32:20 4.5203 +00:32:25 4.5493 +00:32:30 4.5474 +00:32:35 4.5792 +00:32:40 4.4826 +00:32:45 4.5856 +00:32:50 4.6930 +00:32:55 4.4555 +00:33:00 4.4590 +00:33:05 4.4742 +00:33:10 4.3939 +00:33:15 4.5159 +00:33:20 4.5537 +00:33:25 4.5368 +00:33:30 4.6501 +00:33:35 4.5147 +00:33:40 4.4638 +00:33:45 4.5270 +00:33:50 4.5219 +00:33:55 4.5862 +00:34:00 4.5359 +00:34:05 4.5661 +00:34:10 4.5016 +00:34:15 4.4848 +00:34:20 4.5342 +00:34:25 4.5615 +00:34:30 4.4712 +00:34:35 4.4322 +00:34:40 4.4936 +00:34:45 4.5149 +00:34:50 4.4938 +00:34:55 4.5250 +00:35:00 4.6035 +00:35:05 4.6155 +00:35:10 4.4953 +00:35:15 4.4337 +00:35:20 4.4871 +00:35:25 4.4752 +00:35:30 4.4806 +00:35:35 4.5323 +00:35:40 4.4637 +00:35:45 4.5142 +00:35:50 4.5820 +00:35:55 4.4824 +00:36:00 4.3943 +00:36:05 4.4192 +00:36:10 4.5344 +00:36:15 4.6162 +00:36:20 4.5492 +00:36:25 4.4987 +00:36:30 4.4474 +00:36:35 4.5160 +00:36:40 4.5782 +00:36:45 4.6402 +00:36:50 4.6382 +00:36:55 4.5542 +00:37:00 4.6179 +00:37:05 4.5565 +00:37:10 4.4886 +00:37:15 4.5097 +00:37:20 4.5766 +00:37:25 4.5297 +00:37:30 4.5245 +00:37:35 4.5737 +00:37:40 4.6012 +00:37:45 4.5268 +00:37:50 4.5338 +00:37:55 4.5797 +00:38:00 4.6208 +00:38:05 4.5725 +00:38:10 4.4286 +00:38:15 4.5019 +00:38:20 4.4811 +00:38:25 4.4893 +00:38:30 4.5711 +00:38:35 4.5490 +00:38:40 4.5073 +00:38:45 4.4377 +00:38:50 4.5116 +00:38:55 4.6269 +00:39:00 4.6124 +00:39:05 4.6049 +00:39:10 4.5082 +00:39:15 4.4839 +00:39:20 4.5409 +00:39:25 4.5523 +00:39:30 4.4416 +00:39:35 4.5306 +00:39:40 4.5770 +00:39:45 4.6104 +00:39:50 4.6233 +00:39:55 4.5381 +00:40:00 4.5173 +00:40:05 4.5568 +00:40:10 4.5026 +00:40:15 4.5160 +00:40:20 4.4674 +00:40:25 4.4774 +00:40:30 4.4854 +00:40:35 4.4668 +00:40:40 4.4376 +00:40:45 4.4997 +00:40:50 4.5646 +00:40:55 4.5126 +00:41:00 4.4743 +00:41:05 4.5321 +00:41:10 4.5567 +00:41:15 4.5144 +00:41:20 4.5040 +00:41:25 4.5271 +00:41:30 4.3999 +00:41:35 4.4462 +00:41:40 4.4895 +00:41:45 4.5344 +00:41:50 4.5659 +00:41:55 4.5152 +00:42:00 4.5203 +00:42:05 4.4866 +00:42:10 4.4151 +00:42:15 4.4226 +00:42:20 4.5671 +00:42:25 4.4509 +00:42:30 4.4198 +00:42:35 4.5083 +00:42:40 4.5386 +00:42:45 4.5630 +00:42:50 4.4550 +00:42:55 4.4774 +00:43:00 4.4932 +00:43:05 4.5156 +00:43:10 4.5474 +00:43:15 4.5158 +00:43:20 4.5327 +00:43:25 4.5506 +00:43:30 4.4828 +00:43:35 4.3712 +00:43:40 4.4493 +00:43:45 4.4934 +00:43:50 4.5200 +00:43:55 4.4524 +00:44:00 4.4418 +00:44:05 4.5536 +00:44:10 4.5164 +00:44:15 4.3941 +00:44:20 4.4460 +00:44:25 4.4619 +00:44:30 4.4449 +00:44:35 4.4079 +00:44:40 4.4321 +00:44:45 4.4676 +00:44:50 4.4853 +00:44:55 4.5050 +00:45:00 4.4927 +00:45:05 4.5000 +00:45:10 4.4526 +00:45:15 4.5871 +00:45:20 4.4945 +00:45:25 4.4748 +00:45:30 4.4008 +00:45:35 4.4567 +00:45:40 4.5168 +00:45:45 4.4177 +00:45:50 4.4600 +00:45:55 4.4661 +00:46:00 4.4410 +00:46:05 4.4642 +00:46:10 4.4806 +00:46:15 4.4312 +00:46:20 4.4568 +00:46:25 4.5353 +00:46:30 4.5235 +00:46:35 4.4324 +00:46:40 4.4251 +00:46:45 4.4565 +00:46:50 4.4686 +00:46:55 4.3992 +00:47:00 4.4211 +00:47:05 4.4312 +00:47:10 4.4105 +00:47:15 4.4845 +00:47:20 4.4690 +00:47:25 4.5173 +00:47:30 4.4620 +00:47:35 4.4822 +00:47:40 4.4820 +00:47:45 4.4630 +00:47:50 4.4731 +00:47:55 4.3761 +00:48:00 4.4958 +00:48:05 4.4974 +00:48:10 4.4085 +00:48:15 4.4012 +00:48:20 4.3988 +00:48:25 4.4623 +00:48:30 4.4448 +00:48:35 4.4673 +00:48:40 4.5114 +00:48:45 4.4476 +00:48:50 4.3874 +00:48:55 4.4134 +00:49:00 4.4743 +00:49:05 4.3290 +00:49:10 4.4620 +00:49:15 4.4206 +00:49:20 4.4504 +00:49:25 4.4320 +00:49:30 4.5030 +00:49:35 4.4395 +00:49:40 4.4745 +00:49:45 4.4807 +00:49:50 4.4628 +00:49:55 4.4855 +00:50:00 4.4465 +00:50:05 4.4064 +00:50:10 4.3766 +00:50:15 4.4768 +00:50:20 4.4859 +00:50:25 4.4232 +00:50:30 4.4185 +00:50:35 4.3699 +00:50:40 4.3610 +00:50:45 4.4088 +00:50:50 4.4081 +00:50:55 4.3916 +00:51:00 4.4049 +00:51:05 4.4238 +00:51:10 4.4703 +00:51:15 4.4308 +00:51:20 4.3797 +00:51:25 4.3573 +00:51:30 4.4307 +00:51:35 4.4622 +00:51:40 4.4205 +00:51:45 4.4631 +00:51:50 4.4219 +00:51:55 4.3917 +00:52:00 4.4872 +00:52:05 4.4361 +00:52:10 4.4310 +00:52:15 4.4007 +00:52:20 4.3004 +00:52:25 4.3968 +00:52:30 4.4389 +00:52:35 4.3863 +00:52:40 4.3400 +00:52:45 4.3791 +00:52:50 4.4838 +00:52:55 4.4260 +00:53:00 4.3531 +00:53:05 4.3816 +00:53:10 4.3660 +00:53:15 4.3466 +00:53:20 4.3526 +00:53:25 4.3207 +00:53:30 4.3762 +00:53:35 4.4650 +00:53:40 4.4402 +00:53:45 4.3859 +00:53:50 4.4056 +00:53:55 4.3779 +00:54:00 4.3604 +00:54:05 4.2799 +00:54:10 4.4375 +00:54:15 4.4396 +00:54:20 4.4272 +00:54:25 4.3911 +00:54:30 4.2900 +00:54:35 4.3089 +00:54:40 4.3577 +00:54:45 4.5759 +00:54:50 4.5102 +00:54:55 4.4915 +00:55:00 4.4122 +00:55:05 4.4031 +00:55:10 4.4639 +00:55:15 4.5257 +00:55:20 4.5316 +00:55:25 4.4545 +00:55:30 4.3464 +00:55:35 4.3142 +00:55:40 4.3456 +00:55:45 4.3361 +00:55:50 4.3511 +00:55:55 4.3789 +00:56:00 4.4700 +00:56:05 4.3797 +00:56:10 4.3984 +00:56:15 4.3820 +00:56:20 4.3497 +00:56:25 4.4434 +00:56:30 4.4500 +00:56:35 4.3652 +00:56:40 4.4015 +00:56:45 4.4029 +00:56:50 4.4404 +00:56:55 4.4655 +00:57:00 4.3896 +00:57:05 4.4025 +00:57:10 4.4612 +00:57:15 4.4121 +00:57:20 4.2687 +00:57:25 4.3414 +00:57:30 4.3969 +00:57:35 4.4380 +00:57:40 4.4466 +00:57:45 4.4784 +00:57:50 4.3828 +00:57:55 4.4135 +00:58:00 4.3907 +00:58:05 4.2682 +00:58:10 4.3720 +00:58:15 4.4584 +00:58:20 4.4061 +00:58:25 4.4598 +00:58:30 4.4075 +00:58:35 4.3735 +00:58:40 4.4250 +00:58:45 4.4295 +00:58:50 4.3214 +00:58:55 4.3382 +00:59:00 4.3685 +00:59:05 4.4048 +00:59:10 4.4172 +00:59:15 4.3918 +00:59:20 4.4032 +00:59:25 4.4608 +00:59:30 4.5191 +00:59:35 4.3325 +00:59:40 4.2876 +00:59:45 4.3512 +00:59:50 4.4767 +00:59:55 4.4332 +01:00:00 4.4074 +01:00:05 4.3878 +01:00:10 4.4312 +01:00:15 4.4159 +01:00:20 4.4617 +01:00:25 4.3944 +01:00:30 4.3727 +01:00:35 4.3883 +01:00:40 4.3865 +01:00:45 4.4664 +01:00:50 4.4872 +01:00:55 4.3948 +01:01:00 4.3590 +01:01:05 4.3126 +01:01:10 4.3429 +01:01:15 4.4328 +01:01:20 4.3562 +01:01:25 4.2807 +01:01:30 4.3410 +01:01:35 4.4265 +01:01:40 4.4184 +01:01:45 4.4009 +01:01:50 4.3631 +01:01:55 4.4086 +01:02:00 4.3344 +01:02:05 4.4368 +01:02:10 4.4384 +01:02:15 4.4348 +01:02:20 4.4381 +01:02:25 4.3338 +01:02:30 4.4227 +01:02:35 4.3804 +01:02:40 4.3475 +01:02:45 4.2865 +01:02:50 4.3633 +01:02:55 4.4499 +01:03:00 4.3904 +01:03:05 4.4279 +01:03:10 4.3076 +01:03:15 4.3375 +01:03:20 4.3368 +01:03:25 4.3967 +01:03:30 4.2929 +01:03:35 4.4090 +01:03:40 4.2816 +01:03:45 4.3679 +01:03:50 4.2956 +01:03:55 4.1736 +01:04:00 4.3769 +01:04:05 4.3249 +01:04:10 4.3678 +01:04:15 4.3364 +01:04:20 4.4167 +01:04:25 4.3658 +01:04:30 4.3167 +01:04:35 4.4009 +01:04:40 4.4293 +01:04:45 4.3985 +01:04:50 4.3601 +01:04:55 4.3750 +01:05:00 4.4278 +01:05:05 4.3288 +01:05:10 4.2940 +01:05:15 4.3823 +01:05:20 4.3564 +01:05:25 4.2809 +01:05:30 4.3604 +01:05:35 4.3538 +01:05:40 4.2968 +01:05:45 4.4204 +01:05:50 4.4499 +01:05:55 4.3640 +01:06:00 4.3667 +01:06:05 4.3279 +01:06:10 4.3124 +01:06:15 4.3250 +01:06:20 4.3208 +01:06:25 4.3676 +01:06:30 4.3484 +01:06:35 4.3444 +01:06:40 4.3129 +01:06:45 4.4036 +01:06:50 4.3900 +01:06:55 4.2903 +01:07:00 4.3628 +01:07:05 4.3733 +01:07:10 4.3928 +01:07:15 4.3860 +01:07:20 4.3117 +01:07:25 4.3622 +01:07:30 4.3702 +01:07:35 4.4324 +01:07:40 4.4328 +01:07:45 4.2880 +01:07:50 4.3981 +01:07:55 4.4315 +01:08:00 4.3964 +01:08:05 4.4300 +01:08:10 4.3672 +01:08:15 4.4513 +01:08:20 4.4274 +01:08:25 4.4875 +01:08:30 4.3679 +01:08:35 4.2216 +01:08:40 4.3919 +01:08:45 4.3782 +01:08:50 4.4025 +01:08:55 4.3142 +01:09:00 4.3559 +01:09:05 4.4162 +01:09:10 4.2810 +01:09:15 4.3664 +01:09:20 4.3626 +01:09:25 4.3086 +01:09:30 4.2889 +01:09:35 4.3088 +01:09:40 4.3192 +01:09:45 4.3935 +01:09:50 4.3739 +01:09:55 4.4131 +01:10:00 4.4618 +01:10:05 4.3094 +01:10:10 4.3700 +01:10:15 4.4166 +01:10:20 4.3390 +01:10:25 4.3123 +01:10:30 4.3284 +01:10:35 4.2842 +01:10:40 4.2512 +01:10:45 4.3096 +01:10:50 4.2674 +01:10:55 4.2534 +01:11:00 4.3029 +01:11:05 4.3657 +01:11:10 4.3892 +01:11:15 4.3644 +01:11:20 4.3541 +01:11:25 4.3757 +01:11:30 4.2622 +01:11:35 4.3126 +01:11:40 4.2781 +01:11:45 4.3066 +01:11:50 4.3498 +01:11:55 4.3250 +01:12:00 4.2310 +01:12:05 4.3490 +01:12:10 4.3291 +01:12:15 4.3629 +01:12:20 4.4209 +01:12:25 4.3311 +01:12:30 4.3032 +01:12:35 4.2930 +01:12:40 4.2438 +01:12:45 4.3059 +01:12:50 4.3176 +01:12:55 4.3965 +01:13:00 4.3454 +01:13:05 4.3334 +01:13:10 4.3779 +01:13:15 4.3510 +01:13:20 4.3688 +01:13:25 4.4130 +01:13:30 4.3286 +01:13:35 4.2304 +01:13:40 4.2886 +01:13:45 4.3177 +01:13:50 4.2048 +01:13:55 4.2450 +01:14:00 4.2222 +01:14:05 4.2135 +01:14:10 4.3035 +01:14:15 4.3195 +01:14:20 4.3443 +01:14:25 4.3293 +01:14:30 4.4398 +01:14:35 4.4170 +01:14:40 4.4308 +01:14:45 4.4393 +01:14:50 4.3871 +01:14:55 4.3797 +01:15:00 4.3682 +01:15:05 4.3024 +01:15:10 4.2482 +01:15:15 4.2591 +01:15:20 4.2860 +01:15:25 4.2180 +01:15:30 4.2693 +01:15:35 4.3441 +01:15:40 4.4240 +01:15:45 4.4088 +01:15:50 4.3174 +01:15:55 4.1964 +01:16:00 4.1919 +01:16:05 4.2968 +01:16:10 4.2617 +01:16:15 4.3784 +01:16:20 4.4402 +01:16:25 4.3435 +01:16:30 4.2817 +01:16:35 4.2669 +01:16:40 4.3331 +01:16:45 4.3752 +01:16:50 4.3216 +01:16:55 4.3644 +01:17:00 4.4401 +01:17:05 4.4274 +01:17:10 4.4199 +01:17:15 4.4060 +01:17:20 4.4387 +01:17:25 4.4009 +01:17:30 4.3730 +01:17:35 4.3241 +01:17:40 4.3408 +01:17:45 4.3974 +01:17:50 4.3881 +01:17:55 4.3509 +01:18:00 4.3589 +01:18:05 4.3207 +01:18:10 4.1858 +01:18:15 4.1990 +01:18:20 4.2375 +01:18:25 4.3330 +01:18:30 4.2635 +01:18:35 4.2315 +01:18:40 4.2903 +01:18:45 4.3211 +01:18:50 4.2690 +01:18:55 4.2920 +01:19:00 4.2308 +01:19:05 4.3474 +01:19:10 4.2146 +01:19:15 4.1216 +01:19:20 4.2235 +01:19:25 4.3442 +01:19:30 4.3767 +01:19:35 4.2057 +01:19:40 4.2764 +01:19:45 4.3103 +01:19:50 4.3302 +01:19:55 4.2683 +01:20:00 4.2502 +01:20:05 4.3316 +01:20:10 4.3343 +01:20:15 4.2909 +01:20:20 4.3180 +01:20:25 4.3041 +01:20:30 4.3077 +01:20:35 4.4170 +01:20:40 4.3604 +01:20:45 4.3762 +01:20:50 4.3053 +01:20:55 4.2744 +01:21:00 4.2616 +01:21:05 4.3052 +01:21:10 4.3376 +01:21:15 4.3167 +01:21:20 4.2615 +01:21:25 4.3503 +01:21:30 4.2688 +01:21:35 4.2535 +01:21:40 4.3053 +01:21:45 4.2801 +01:21:50 4.2813 +01:21:55 4.1952 +01:22:00 4.2809 +01:22:05 4.2797 +01:22:10 4.1903 +01:22:15 4.2976 +01:22:20 4.3677 +01:22:25 4.3010 +01:22:30 4.3400 +01:22:35 4.2822 +01:22:40 4.2737 +01:22:45 4.3052 +01:22:50 4.3109 +01:22:55 4.2432 +01:23:00 4.2939 +01:23:05 4.3674 +01:23:10 4.2411 +01:23:15 4.3157 +01:23:20 4.2849 +01:23:25 4.2359 +01:23:30 4.2021 +01:23:35 4.2924 +01:23:40 4.3079 +01:23:45 4.2460 +01:23:50 4.1668 +01:23:55 4.2529 +01:24:00 4.4067 +01:24:05 4.3127 +01:24:10 4.2327 +01:24:15 4.1798 +01:24:20 4.2193 +01:24:25 4.2881 +01:24:30 4.3695 +01:24:35 4.3524 +01:24:40 4.3117 +01:24:45 4.2311 +01:24:50 4.2728 +01:24:55 4.3012 +01:25:00 4.1993 +01:25:05 4.2352 +01:25:10 4.2526 +01:25:15 4.3132 +01:25:20 4.3455 +01:25:25 4.3509 +01:25:30 4.2849 +01:25:35 4.2709 +01:25:40 4.2689 +01:25:45 4.3433 +01:25:50 4.3799 +01:25:55 4.2826 +01:26:00 4.2487 +01:26:05 4.2188 +01:26:10 4.3184 +01:26:15 4.2784 +01:26:20 4.2291 +01:26:25 4.1846 +01:26:30 4.2868 +01:26:35 4.3480 +01:26:40 4.2550 +01:26:45 4.1924 +01:26:50 4.2310 +01:26:55 4.2801 +01:27:00 4.1942 +01:27:05 4.2334 +01:27:10 4.2259 +01:27:15 4.0900 +01:27:20 4.0884 +01:27:25 4.1630 +01:27:30 4.3014 +01:27:35 4.2224 +01:27:40 4.2081 +01:27:45 4.2610 +01:27:50 4.3704 +01:27:55 4.4366 +01:28:00 4.4452 +01:28:05 4.3496 +01:28:10 4.2793 +01:28:15 4.2978 +01:28:20 4.3159 +01:28:25 4.3629 +01:28:30 4.3018 +01:28:35 4.3273 +01:28:40 4.4155 +01:28:45 4.2741 +01:28:50 4.2176 +01:28:55 4.2639 +01:29:00 4.1603 +01:29:05 4.1439 +01:29:10 4.2079 +01:29:15 4.2192 +01:29:20 4.2047 +01:29:25 4.2561 +01:29:30 4.3081 +01:29:35 4.2536 +01:29:40 4.2755 +01:29:45 4.3029 +01:29:50 4.2376 +01:29:55 4.3001 +01:30:00 4.3716 +01:30:05 4.2692 +01:30:10 4.2148 +01:30:15 4.2638 +01:30:20 4.2353 +01:30:25 4.2661 +01:30:30 4.2432 +01:30:35 4.2199 +01:30:40 4.2256 +01:30:45 4.1887 +01:30:50 4.2642 +01:30:55 4.2412 +01:31:00 4.2898 +01:31:05 4.3127 +01:31:10 4.3356 +01:31:15 4.2739 +01:31:20 4.2238 +01:31:25 4.3690 +01:31:30 4.3755 +01:31:35 4.3401 +01:31:40 4.3084 +01:31:45 4.2572 +01:31:50 4.3122 +01:31:55 4.2877 +01:32:00 4.2884 +01:32:05 4.2979 +01:32:10 4.3234 +01:32:15 4.3327 +01:32:20 4.2900 +01:32:25 4.2857 +01:32:30 4.1883 +01:32:35 4.3443 +01:32:40 4.3081 +01:32:45 4.2883 +01:32:50 4.2240 +01:32:55 4.2875 +01:33:00 4.1297 +01:33:05 4.3225 +01:33:10 4.3878 +01:33:15 4.3296 +01:33:20 4.2770 +01:33:25 4.2104 +01:33:30 4.2457 +01:33:35 4.1824 +01:33:40 4.2668 +01:33:45 4.2670 +01:33:50 4.2819 +01:33:55 4.3288 +01:34:00 4.2190 +01:34:05 4.2515 +01:34:10 4.2429 +01:34:15 4.1617 +01:34:20 4.2730 +01:34:25 4.3284 +01:34:30 4.3647 +01:34:35 4.2928 +01:34:40 4.2354 +01:34:45 4.2455 +01:34:50 4.1060 +01:34:55 4.1587 +01:35:00 4.2831 +01:35:05 4.2546 +01:35:10 4.1568 +01:35:15 4.2157 +01:35:20 4.3083 +01:35:25 4.2934 +01:35:30 4.2754 +01:35:35 4.2668 +01:35:40 4.2287 +01:35:45 4.1834 +01:35:50 4.2074 +01:35:55 4.2359 +01:36:00 4.2081 +01:36:05 4.1676 +01:36:10 4.1535 +01:36:15 4.1325 +01:36:20 4.1498 +01:36:25 4.2650 +01:36:30 4.3042 +01:36:35 4.2334 +01:36:40 4.1819 +01:36:45 4.2260 +01:36:50 4.2139 +01:36:55 4.1902 +01:37:00 4.2531 +01:37:05 4.2760 +01:37:10 4.2208 +01:37:15 4.2232 +01:37:20 4.3085 +01:37:25 4.2708 +01:37:30 4.1897 +01:37:35 4.2197 +01:37:40 4.1470 +01:37:45 4.2209 +01:37:50 4.2196 +01:37:55 4.1837 +01:38:00 4.2346 +01:38:05 4.2365 +01:38:10 4.2668 +01:38:15 4.2396 +01:38:20 4.3110 +01:38:25 4.2493 +01:38:30 4.2611 +01:38:35 4.2662 +01:38:40 4.2682 +01:38:45 4.3168 +01:38:50 4.2553 +01:38:55 4.2737 +01:39:00 4.1937 +01:39:05 4.1429 +01:39:10 4.1617 +01:39:15 4.2568 +01:39:20 4.2969 +01:39:25 4.2391 +01:39:30 4.2342 +01:39:35 4.1928 +01:39:40 4.3157 +01:39:45 4.2762 +01:39:50 4.2450 +01:39:55 4.1989 +01:40:00 4.2564 +01:40:05 4.2765 +01:40:10 4.2582 +01:40:15 4.1943 +01:40:20 4.1835 +01:40:25 4.0968 +01:40:30 4.0941 +01:40:35 4.1501 +01:40:40 4.2886 +01:40:45 4.3196 +01:40:50 4.2262 +01:40:55 4.2092 +01:41:00 4.1783 +01:41:05 4.1852 +01:41:10 4.2378 +01:41:15 4.2286 +01:41:20 4.2927 +01:41:25 4.2118 +01:41:30 4.2027 +01:41:35 4.3257 +01:41:40 4.1457 +01:41:45 4.2147 +01:41:50 4.2161 +01:41:55 4.2594 +01:42:00 4.2502 +01:42:05 4.2376 +01:42:10 4.2423 +01:42:15 4.2457 +01:42:20 4.1655 +01:42:25 4.1631 +01:42:30 4.2012 +01:42:35 4.1697 +01:42:40 4.1257 +01:42:45 4.1734 +01:42:50 4.2098 +01:42:55 4.2399 +01:43:00 4.2934 +01:43:05 4.2841 +01:43:10 4.2590 +01:43:15 4.1963 +01:43:20 4.2217 +01:43:25 4.2081 +01:43:30 4.2363 +01:43:35 4.2559 +01:43:40 4.2674 +01:43:45 4.2594 +01:43:50 4.2685 +01:43:55 4.2744 +01:44:00 4.2889 +01:44:05 4.2594 +01:44:10 4.2123 +01:44:15 4.3099 +01:44:20 4.1992 +01:44:25 4.1929 +01:44:30 4.1578 +01:44:35 4.2486 +01:44:40 4.2728 +01:44:45 4.2610 +01:44:50 4.2551 +01:44:55 4.2723 +01:45:00 4.2300 +01:45:05 4.2470 +01:45:10 4.2280 +01:45:15 4.2317 +01:45:20 4.2540 +01:45:25 4.2366 +01:45:30 4.2608 +01:45:35 4.3071 +01:45:40 4.2898 +01:45:45 4.2723 +01:45:50 4.2507 +01:45:55 4.2628 +01:46:00 4.2086 +01:46:05 4.1165 +01:46:10 4.1797 +01:46:15 4.1349 +01:46:20 4.1984 +01:46:25 4.1699 +01:46:30 4.2308 +01:46:35 4.1957 +01:46:40 4.1803 +01:46:45 4.1799 +01:46:50 4.2133 +01:46:55 4.2774 +01:47:00 4.3289 +01:47:05 4.2297 +01:47:10 4.2222 +01:47:15 4.2664 +01:47:20 4.2692 +01:47:25 4.1359 +01:47:30 4.1273 +01:47:35 4.1603 +01:47:40 4.2406 +01:47:45 4.3238 +01:47:50 4.3462 +01:47:55 4.1751 +01:48:00 4.2187 +01:48:05 4.1814 +01:48:10 4.1655 +01:48:15 4.2362 +01:48:20 4.2035 +01:48:25 4.3006 +01:48:30 4.2959 +01:48:35 4.1243 +01:48:40 4.1373 +01:48:45 4.1800 +01:48:50 4.1638 +01:48:55 4.1661 +01:49:00 4.1770 +01:49:05 4.1154 +01:49:10 4.1435 +01:49:15 4.2417 +01:49:20 4.1634 +01:49:25 4.1975 +01:49:30 4.2265 +01:49:35 4.2450 +01:49:40 4.2221 +01:49:45 4.2714 +01:49:50 4.2248 +01:49:55 4.2468 +01:50:00 4.2673 +01:50:05 4.1332 +01:50:10 4.0830 +01:50:15 4.1515 +01:50:20 4.1578 +01:50:25 4.1853 +01:50:30 4.2576 +01:50:35 4.2916 +01:50:40 4.2545 +01:50:45 4.1881 +01:50:50 4.2642 +01:50:55 4.1965 +01:51:00 4.1919 +01:51:05 4.2664 +01:51:10 4.2576 +01:51:15 4.1617 +01:51:20 4.0520 +01:51:25 4.1565 +01:51:30 4.1739 +01:51:35 4.1447 +01:51:40 4.1384 +01:51:45 4.0848 +01:51:50 4.1338 +01:51:55 4.1580 +01:52:00 4.1424 +01:52:05 4.1841 +01:52:10 4.3262 +01:52:15 4.3730 +01:52:20 4.2504 +01:52:25 4.1584 +01:52:30 4.1602 +01:52:35 4.2115 +01:52:40 4.1357 +01:52:45 4.1133 +01:52:50 4.2357 +01:52:55 4.3365 +01:53:00 4.3347 +01:53:05 4.1840 +01:53:10 4.1411 +01:53:15 4.1639 +01:53:20 4.2088 +01:53:25 4.1393 +01:53:30 4.1373 +01:53:35 4.2098 +01:53:40 4.1607 +01:53:45 4.2383 +01:53:50 4.3454 +01:53:55 4.2582 +01:54:00 4.2127 +01:54:05 4.2903 +01:54:10 4.1963 +01:54:15 4.2025 +01:54:20 4.1597 +01:54:25 4.1388 +01:54:30 4.1784 +01:54:35 4.1066 +01:54:40 4.1775 +01:54:45 4.0933 +01:54:50 4.1585 +01:54:55 4.2532 +01:55:00 4.1524 +01:55:05 4.1612 +01:55:10 4.2077 +01:55:15 4.1442 +01:55:20 4.0676 +01:55:25 4.0646 +01:55:30 4.1976 +01:55:35 4.2229 +01:55:40 4.1372 +01:55:45 4.2025 +01:55:50 4.1936 +01:55:55 4.1025 +01:56:00 4.1542 +01:56:05 4.2241 +01:56:10 4.2420 +01:56:15 4.2951 +01:56:20 4.2478 +01:56:25 4.2304 +01:56:30 4.1308 +01:56:35 4.1927 +01:56:40 4.2100 +01:56:45 4.1867 +01:56:50 4.1005 +01:56:55 4.1250 +01:57:00 4.2474 +01:57:05 4.0925 +01:57:10 4.0985 +01:57:15 4.1772 +01:57:20 4.2640 +01:57:25 4.3186 +01:57:30 4.3245 +01:57:35 4.1488 +01:57:40 4.1370 +01:57:45 4.1608 +01:57:50 4.1749 +01:57:55 4.2099 +01:58:00 4.1796 +01:58:05 4.2413 +01:58:10 4.1701 +01:58:15 4.1882 +01:58:20 4.2289 +01:58:25 4.1413 +01:58:30 4.1463 +01:58:35 4.2139 +01:58:40 4.2376 +01:58:45 4.2608 +01:58:50 4.1610 +01:58:55 4.1512 +01:59:00 4.1924 +01:59:05 4.1531 +01:59:10 4.2604 +01:59:15 4.2036 +01:59:20 4.1720 +01:59:25 4.2576 +01:59:30 4.2635 +01:59:35 4.2920 +01:59:40 4.2709 +01:59:45 4.1864 +01:59:50 4.1624 +01:59:55 4.2660 +02:00:00 4.2869 +02:00:05 4.2162 +02:00:10 4.2257 +02:00:15 4.2174 +02:00:20 4.2145 +02:00:25 4.1686 +02:00:30 4.1531 +02:00:35 4.2616 +02:00:40 4.1421 +02:00:45 4.1592 +02:00:50 4.1543 +02:00:57 4.2177 +02:01:00 4.1393 +02:01:05 4.0961 +02:01:10 4.2240 +02:01:15 4.2032 +02:01:20 4.2786 +02:01:26 4.1941 +02:01:33 4.2383 +02:01:35 4.1760 +02:01:40 4.1527 +02:01:45 4.2268 +02:01:50 4.1889 +02:01:55 4.1927 +02:02:04 4.1210 +02:02:05 4.0578 +02:02:10 4.2631 +02:02:17 4.1613 +02:02:20 4.2224 +02:02:25 4.2431 +02:02:30 4.2004 +02:02:38 4.2561 +02:02:40 4.1968 +02:02:54 4.2976 +02:02:55 4.2810 +02:03:04 4.2890 +02:03:05 4.2452 +02:03:11 4.1637 +02:03:15 4.1673 +02:03:23 4.1914 +02:03:25 4.1546 +02:03:30 4.1526 +02:03:35 4.2043 +02:03:40 4.1873 +02:03:45 4.1885 +02:03:50 4.1001 +02:03:55 4.2377 +02:04:00 4.1227 +02:04:05 4.1880 +02:04:10 4.2458 +02:04:15 4.2478 +02:04:20 4.2256 +02:04:25 4.1936 +02:04:30 4.1542 +02:04:35 4.2099 +02:04:40 4.1645 +02:04:45 4.2206 +02:04:50 4.1015 +02:04:55 4.2085 +02:05:00 4.1834 +02:05:05 4.1393 +02:05:10 4.1510 +02:05:15 4.2278 +02:05:20 4.2378 +02:05:25 4.2219 +02:05:30 4.1474 +02:05:35 4.0868 +02:05:40 4.1444 +02:05:45 4.2461 +02:05:50 4.1874 +02:05:55 4.1083 +02:06:00 4.1602 +02:06:05 4.2128 +02:06:10 4.2630 +02:06:15 4.3383 +02:06:20 4.2578 +02:06:25 4.2083 +02:06:30 4.2456 +02:06:35 4.2095 +02:06:40 4.1901 +02:06:45 4.1899 +02:06:50 4.2019 +02:06:55 4.2836 +02:07:00 4.1863 +02:07:05 4.2520 +02:07:10 4.1594 +02:07:15 4.1631 +02:07:20 4.2255 +02:07:25 4.2283 +02:07:30 4.2226 +02:07:35 4.1901 +02:07:40 4.1505 +02:07:45 4.1915 +02:07:50 4.1757 +02:07:55 4.1837 +02:08:00 4.2191 +02:08:05 4.1176 +02:08:10 4.3030 +02:08:15 4.2399 +02:08:20 4.1212 +02:08:25 4.0837 +02:08:30 4.2064 +02:08:35 4.2857 +02:08:40 4.1326 +02:08:45 4.1940 +02:08:50 4.1988 +02:08:55 4.1505 +02:09:00 4.0519 +02:09:05 4.1586 +02:09:10 4.1768 +02:09:15 4.1767 +02:09:20 4.2283 +02:09:25 4.2380 +02:09:30 4.1900 +02:09:35 4.1687 +02:09:40 4.2090 +02:09:45 4.1846 +02:09:50 4.2362 +02:09:55 4.1410 +02:10:00 4.0728 +02:10:05 4.1472 +02:10:10 4.1651 +02:10:15 4.2404 +02:10:20 4.2664 +02:10:25 4.2732 +02:10:30 4.1561 +02:10:35 4.1633 +02:10:40 4.1870 +02:10:45 4.2008 +02:10:50 4.1688 +02:10:55 4.2411 +02:11:00 4.2295 +02:11:05 4.1135 +02:11:10 4.1679 +02:11:15 4.1828 +02:11:20 4.3140 +02:11:25 4.2713 +02:11:30 4.2126 +02:11:35 4.1046 +02:11:40 4.1238 +02:11:45 4.1780 +02:11:50 4.1458 +02:11:55 4.1722 +02:12:00 4.1893 +02:12:05 4.1432 +02:12:10 4.1492 +02:12:15 4.2084 +02:12:20 4.2075 +02:12:25 4.1427 +02:12:30 4.1393 +02:12:35 4.1441 +02:12:40 4.2886 +02:12:45 4.2406 +02:12:50 4.1813 +02:12:55 4.2433 +02:13:00 4.2855 +02:13:05 4.1842 +02:13:10 4.1000 +02:13:15 4.1187 +02:13:20 3.9957 +02:13:25 4.0570 +02:13:30 4.1108 +02:13:35 4.0943 +02:13:40 4.1335 +02:13:45 4.1902 +02:13:50 4.1332 +02:13:55 4.1841 +02:14:00 4.2023 +02:14:05 4.0799 +02:14:10 4.1584 +02:14:15 4.2099 +02:14:20 4.1308 +02:14:25 4.0924 +02:14:30 4.1697 +02:14:35 4.2515 +02:14:40 4.1511 +02:14:45 4.1536 +02:14:50 4.1607 +02:14:55 4.1614 +02:15:00 4.1799 +02:15:05 4.1321 +02:15:10 4.1604 +02:15:15 4.1079 +02:15:20 4.1907 +02:15:25 4.2797 +02:15:30 4.1837 +02:15:35 4.1903 +02:15:40 4.2137 +02:15:45 4.1559 +02:15:50 4.1595 +02:15:55 4.0787 +02:16:00 4.1085 +02:16:05 4.1644 +02:16:10 4.1593 +02:16:15 4.1139 +02:16:20 4.1045 +02:16:25 4.1906 +02:16:30 4.1429 +02:16:35 4.1104 +02:16:40 4.1160 +02:16:45 4.1655 +02:16:50 4.2069 +02:16:55 4.1726 +02:17:00 4.1455 +02:17:05 4.1373 +02:17:10 4.1775 +02:17:15 4.1469 +02:17:20 4.1154 +02:17:25 4.1441 +02:17:30 4.1747 +02:17:35 4.1642 +02:17:40 4.1866 +02:17:45 4.1925 +02:17:50 4.1631 +02:17:55 4.1778 +02:18:00 4.1929 +02:18:05 4.1938 +02:18:10 4.1939 +02:18:15 4.1975 +02:18:20 4.1560 +02:18:25 4.0924 +02:18:30 4.0546 +02:18:35 4.0638 +02:18:40 4.1209 +02:18:45 4.1445 +02:18:50 4.2099 +02:18:55 4.1579 +02:19:00 4.1800 +02:19:05 4.1459 +02:19:10 4.2127 +02:19:15 4.1959 +02:19:20 4.1844 +02:19:25 4.1316 +02:19:30 4.2238 +02:19:35 4.1779 +02:19:40 4.1592 +02:19:45 4.1347 +02:19:50 4.1292 +02:19:55 4.2150 +02:20:00 4.1576 +02:20:05 4.1293 +02:20:10 4.2231 +02:20:15 4.2236 +02:20:20 4.1652 +02:20:25 4.1424 +02:20:30 4.1101 +02:20:35 4.1493 +02:20:40 4.2230 +02:20:45 4.1677 +02:20:50 4.1060 +02:20:55 4.0078 +02:21:00 4.0629 +02:21:05 4.1757 +02:21:10 4.1624 +02:21:15 4.1619 +02:21:20 4.1910 +02:21:25 4.1314 +02:21:30 4.1274 +02:21:35 4.0989 +02:21:40 4.0430 +02:21:45 4.1582 +02:21:50 4.1620 +02:21:55 4.1397 +02:22:00 4.1915 +02:22:05 4.1760 +02:22:10 4.1728 +02:22:15 4.1563 +02:22:20 4.0636 +02:22:25 4.1132 +02:22:30 4.0741 +02:22:35 3.9961 +02:22:40 4.2086 +02:22:45 4.1362 +02:22:50 4.1086 +02:22:55 4.2161 +02:23:00 4.2087 +02:23:05 4.1678 +02:23:10 4.1245 +02:23:15 4.1171 +02:23:20 4.1182 +02:23:25 4.1488 +02:23:30 4.1352 +02:23:35 4.1624 +02:23:40 4.1783 +02:23:45 4.1696 +02:23:50 4.1530 +02:23:55 4.1571 +02:24:00 4.1551 +02:24:05 4.1015 +02:24:10 4.1744 +02:24:15 4.1372 +02:24:20 4.1478 +02:24:25 4.1699 +02:24:30 4.1840 +02:24:35 4.2447 +02:24:40 4.2078 +02:24:45 4.1995 +02:24:50 4.2052 +02:24:55 4.2432 +02:25:00 4.1355 +02:25:05 4.0729 +02:25:10 4.1021 +02:25:15 4.1477 +02:25:20 4.2625 +02:25:25 4.2272 +02:25:30 4.0843 +02:25:35 3.9715 +02:25:40 4.1757 +02:25:45 4.0842 +02:25:50 4.0888 +02:25:55 4.0031 +02:26:00 4.1396 +02:26:05 4.1742 +02:26:10 4.1386 +02:26:15 4.2124 +02:26:20 4.2847 +02:26:25 4.2304 +02:26:30 4.1550 +02:26:35 4.1215 +02:26:40 4.0619 +02:26:45 4.1776 +02:26:50 4.1781 +02:26:55 4.0659 +02:27:00 4.0054 +02:27:05 4.1167 +02:27:10 4.2470 +02:27:15 4.2732 +02:27:20 4.2631 +02:27:25 4.2288 +02:27:30 4.1261 +02:27:35 4.0833 +02:27:40 4.1067 +02:27:45 4.1005 +02:27:50 4.0911 +02:27:55 4.1113 +02:28:00 4.1578 +02:28:05 4.2158 +02:28:10 4.2389 +02:28:15 4.1523 +02:28:20 4.0341 +02:28:25 4.0798 +02:28:30 4.1384 +02:28:35 4.1562 +02:28:40 4.1895 +02:28:45 4.2099 +02:28:50 4.0879 +02:28:55 4.0612 +02:29:00 4.2817 +02:29:05 4.3487 +02:29:10 4.2034 +02:29:15 4.1399 +02:29:20 4.1486 +02:29:25 4.1208 +02:29:30 4.1977 +02:29:35 4.1000 +02:29:40 4.0890 +02:29:45 4.1176 +02:29:50 4.1399 +02:29:55 4.1026 +02:30:00 4.1313 +02:30:05 4.2516 +02:30:10 4.1480 +02:30:15 4.0434 +02:30:20 4.1688 +02:30:25 4.1513 +02:30:30 4.1302 +02:30:35 4.2523 +02:30:40 4.1802 +02:30:45 4.0270 +02:30:50 4.1505 +02:30:55 4.2245 +02:31:00 4.2088 +02:31:05 4.2055 +02:31:10 4.0597 +02:31:15 4.0762 +02:31:20 4.1495 +02:31:25 4.1026 +02:31:30 4.1775 +02:31:35 4.1374 +02:31:40 4.1534 +02:31:45 4.1217 +02:31:50 4.1739 +02:31:55 4.1841 +02:32:00 4.1247 +02:32:05 4.1161 +02:32:10 4.1725 +02:32:15 4.1800 +02:32:20 4.1806 +02:32:25 4.0624 +02:32:30 4.0874 +02:32:35 4.0759 +02:32:40 4.0354 +02:32:45 4.0869 +02:32:50 4.1132 +02:32:55 4.0266 +02:33:00 4.1799 +02:33:05 4.2289 +02:33:10 4.0643 +02:33:15 4.1263 +02:33:20 4.1694 +02:33:25 4.0679 +02:33:30 4.0708 +02:33:35 4.0465 +02:33:40 4.1215 +02:33:45 4.1559 +02:33:50 4.1174 +02:33:55 4.0351 +02:34:00 4.1682 +02:34:05 4.1579 +02:34:10 4.0796 +02:34:15 4.0866 +02:34:20 4.0562 +02:34:25 4.0234 +02:34:30 4.0912 +02:34:35 4.1336 +02:34:40 4.1380 +02:34:45 4.2108 +02:34:50 4.1740 +02:34:55 4.2215 +02:35:00 4.2123 +02:35:05 4.1473 +02:35:10 4.2102 +02:35:15 4.1829 +02:35:20 4.1742 +02:35:25 4.1526 +02:35:30 4.1790 +02:35:35 4.1885 +02:35:40 4.0966 +02:35:45 4.1016 +02:35:50 4.0770 +02:35:55 4.1873 +02:36:00 4.1292 +02:36:05 4.0955 +02:36:10 4.0837 +02:36:15 4.1253 +02:36:20 4.2201 +02:36:25 4.1908 +02:36:30 4.1791 +02:36:35 4.1767 +02:36:40 4.0042 +02:36:45 4.0460 +02:36:50 4.2050 +02:36:55 4.2273 +02:37:00 4.2083 +02:37:05 4.1263 +02:37:10 4.1522 +02:37:15 4.1717 +02:37:20 4.2156 +02:37:25 4.1146 +02:37:30 4.1627 +02:37:35 4.2009 +02:37:40 4.1631 +02:37:45 4.1166 +02:37:50 4.1653 +02:37:55 4.2368 +02:38:00 4.1604 +02:38:05 4.1990 +02:38:10 4.2026 +02:38:15 4.1110 +02:38:20 4.0789 +02:38:25 3.9994 +02:38:30 4.0533 +02:38:35 4.1610 +02:38:40 4.1382 +02:38:45 4.1700 +02:38:50 4.1390 +02:38:55 4.1865 +02:39:00 4.1247 +02:39:05 4.0953 +02:39:10 4.1854 +02:39:15 4.1128 +02:39:20 4.1533 +02:39:25 4.1766 +02:39:30 4.1894 +02:39:35 4.0903 +02:39:40 4.1487 +02:39:45 4.1484 +02:39:50 4.1423 +02:39:55 4.1871 +02:40:00 4.1722 +02:40:05 4.1034 +02:40:10 4.1258 +02:40:15 4.1105 +02:40:20 4.1927 +02:40:25 4.1886 +02:40:30 4.1579 +02:40:35 4.1461 +02:40:40 4.1335 +02:40:45 4.1607 +02:40:50 4.1729 +02:40:55 4.1343 +02:41:00 4.1075 +02:41:05 4.0258 +02:41:10 4.1284 +02:41:15 4.1438 +02:41:20 4.1218 +02:41:25 4.1181 +02:41:30 4.0841 +02:41:35 4.1964 +02:41:40 4.1918 +02:41:45 4.1610 +02:41:50 4.1043 +02:41:55 4.0863 +02:42:00 4.1567 +02:42:05 4.0846 +02:42:10 4.1127 +02:42:15 4.1260 +02:42:20 4.1477 +02:42:25 4.2246 +02:42:30 4.2126 +02:42:35 4.1910 +02:42:40 4.0283 +02:42:45 4.1561 +02:42:50 4.1828 +02:42:55 4.0757 +02:43:00 4.0900 +02:43:05 4.1173 +02:43:10 4.1026 +02:43:15 4.0553 +02:43:20 4.1566 +02:43:25 4.2606 +02:43:30 4.2622 +02:43:35 4.2466 +02:43:40 4.1505 +02:43:45 4.1419 +02:43:50 4.0965 +02:43:55 4.1334 +02:44:00 4.1435 +02:44:05 4.1135 +02:44:10 4.1799 +02:44:15 4.1538 +02:44:20 4.1534 +02:44:25 4.0787 +02:44:30 4.0327 +02:44:35 4.0828 +02:44:40 4.2327 +02:44:45 4.2298 +02:44:50 4.1914 +02:44:55 4.1753 +02:45:00 4.2077 +02:45:05 4.1789 +02:45:10 4.1985 +02:45:15 4.2293 +02:45:20 4.1592 +02:45:25 4.1241 +02:45:30 4.1730 +02:45:35 4.1365 +02:45:40 4.0295 +02:45:45 4.1047 +02:45:50 4.1216 +02:45:55 4.1105 +02:46:00 4.0771 +02:46:05 4.0411 +02:46:10 4.0778 +02:46:15 3.9794 +02:46:20 4.0359 +02:46:25 4.0793 +02:46:30 4.0933 +02:46:35 4.1006 +02:46:40 4.1307 +02:46:45 4.1044 +02:46:50 4.1122 +02:46:55 4.0169 +02:47:00 4.1364 +02:47:05 4.1860 +02:47:10 4.1452 +02:47:15 4.1383 +02:47:20 4.1360 +02:47:25 4.1525 +02:47:30 4.1283 +02:47:35 4.1275 +02:47:40 4.0927 +02:47:45 4.1161 +02:47:50 4.0778 +02:47:55 4.0021 +02:48:00 4.0826 +02:48:05 4.1117 +02:48:10 4.0457 +02:48:15 4.0271 +02:48:20 4.0872 +02:48:25 4.0760 +02:48:30 4.0597 +02:48:35 4.0439 +02:48:40 4.1604 +02:48:45 4.1231 +02:48:50 4.1584 +02:48:55 4.0520 +02:49:00 4.0423 +02:49:05 4.0744 +02:49:10 4.0702 +02:49:15 4.1211 +02:49:20 4.0647 +02:49:25 4.0743 +02:49:30 4.1030 +02:49:35 4.0490 +02:49:40 4.0653 +02:49:45 4.1020 +02:49:50 4.1297 +02:49:55 4.2229 +02:50:00 4.2171 +02:50:05 4.0967 +02:50:10 4.1476 +02:50:15 4.1656 +02:50:20 4.1595 +02:50:25 4.1204 +02:50:30 4.1451 +02:50:35 4.2053 +02:50:40 4.2125 +02:50:45 4.0964 +02:50:50 4.1665 +02:50:55 4.1531 +02:51:00 4.0733 +02:51:05 4.1222 +02:51:10 4.1475 +02:51:15 4.1120 +02:51:20 4.1071 +02:51:25 4.0775 +02:51:30 4.1331 +02:51:35 4.2072 +02:51:40 4.1793 +02:51:45 4.1334 +02:51:50 4.0925 +02:51:55 4.1463 +02:52:00 4.1764 +02:52:05 4.0773 +02:52:10 4.1061 +02:52:15 4.0837 +02:52:20 3.9810 +02:52:25 4.0003 +02:52:30 4.1147 +02:52:35 4.1598 +02:52:40 4.1805 +02:52:45 4.1585 +02:52:50 4.0546 +02:52:55 4.0306 +02:53:00 4.0552 +02:53:05 4.1020 +02:53:10 4.2909 +02:53:15 4.2427 +02:53:20 4.1641 +02:53:25 4.1636 +02:53:30 4.1516 +02:53:35 4.1762 +02:53:40 4.2587 +02:53:45 4.2502 +02:53:50 4.2213 +02:53:55 4.2318 +02:54:00 4.1470 +02:54:05 4.1614 +02:54:10 4.1091 +02:54:15 4.0696 +02:54:20 4.0259 +02:54:25 3.9875 +02:54:30 4.0774 +02:54:35 4.0842 +02:54:40 4.0771 +02:54:45 4.1425 +02:54:50 4.1238 +02:54:55 4.0883 +02:55:00 4.0167 +02:55:05 4.0868 +02:55:10 4.1583 +02:55:15 4.2231 +02:55:20 4.1593 +02:55:25 4.1239 +02:55:30 4.0983 +02:55:35 4.1409 +02:55:40 4.1461 +02:55:45 4.0387 +02:55:50 4.1010 +02:55:55 4.2396 +02:56:00 4.1935 +02:56:05 4.0276 +02:56:10 4.0077 +02:56:15 4.0500 +02:56:20 4.2064 +02:56:25 4.0581 +02:56:30 4.1043 +02:56:35 4.2815 +02:56:40 4.2406 +02:56:45 4.1377 +02:56:50 3.9394 +02:56:55 3.9447 +02:57:00 4.0772 +02:57:05 4.1259 +02:57:10 4.2053 +02:57:15 4.1411 +02:57:20 4.0934 +02:57:25 4.2224 +02:57:30 4.2731 +02:57:35 4.1185 +02:57:40 4.1182 +02:57:45 4.0903 +02:57:50 4.0693 +02:57:55 4.0738 +02:58:00 4.0514 +02:58:05 4.1283 +02:58:10 4.1135 +02:58:15 4.0932 +02:58:20 4.1098 +02:58:25 4.1166 +02:58:30 4.1771 +02:58:35 4.1442 +02:58:40 4.1452 +02:58:45 4.1107 +02:58:50 4.0279 +02:58:55 4.0332 +02:59:00 4.0770 +02:59:05 4.1350 +02:59:10 4.2132 +02:59:15 4.1704 +02:59:20 4.1093 +02:59:25 4.1111 +02:59:30 4.0772 +02:59:35 4.0856 +02:59:40 4.1295 +02:59:45 4.1113 +02:59:50 4.0839 +02:59:55 4.1054 +03:00:00 4.0688 +03:00:05 4.1299 +03:00:10 4.0491 +03:00:15 4.0669 +03:00:20 4.1296 +03:00:25 4.0701 +03:00:30 4.1557 +03:00:35 4.0725 +03:00:40 4.1471 +03:00:45 4.1281 +03:00:50 4.1193 +03:00:55 4.0028 +03:01:00 4.1313 +03:01:05 4.0617 +03:01:10 4.0163 +03:01:15 4.0496 +03:01:20 4.1076 +03:01:25 4.0784 +03:01:30 4.0805 +03:01:35 4.1099 +03:01:40 4.0485 +03:01:45 4.1234 +03:01:50 4.1263 +03:01:55 4.1518 +03:02:00 4.1629 +03:02:05 4.1010 +03:02:10 4.0419 +03:02:15 4.0679 +03:02:20 4.0246 +03:02:25 4.0989 +03:02:30 4.0984 +03:02:35 4.1170 +03:02:40 4.1115 +03:02:45 4.1782 +03:02:50 4.1394 +03:02:55 4.0243 +03:03:00 4.1398 +03:03:05 4.1272 +03:03:10 4.1218 +03:03:15 4.1797 +03:03:20 4.0746 +03:03:25 4.0411 +03:03:30 4.0641 +03:03:35 4.0930 +03:03:40 4.1379 +03:03:45 4.0847 +03:03:50 4.0956 +03:03:55 4.1175 +03:04:00 4.1251 +03:04:05 4.1154 +03:04:10 4.1951 +03:04:15 4.1457 +03:04:20 4.0859 +03:04:25 4.1019 +03:04:30 4.2113 +03:04:35 4.1909 +03:04:40 4.0402 +03:04:45 4.0572 +03:04:50 4.0732 +03:04:55 4.0113 +03:05:00 4.0661 +03:05:05 4.2068 +03:05:10 4.1588 +03:05:15 4.1435 +03:05:20 4.1476 +03:05:25 4.0680 +03:05:30 4.0687 +03:05:35 4.1901 +03:05:40 4.2462 +03:05:45 4.1328 +03:05:50 4.1404 +03:05:55 4.1439 +03:06:00 4.1157 +03:06:05 4.1096 +03:06:10 4.1543 +03:06:15 4.1000 +03:06:20 4.1197 +03:06:25 4.1629 +03:06:30 4.1778 +03:06:35 4.1347 +03:06:40 4.1694 +03:06:45 4.0586 +03:06:50 4.1128 +03:06:55 4.1279 +03:07:00 4.1506 +03:07:05 4.1491 +03:07:10 4.1245 +03:07:15 4.1581 +03:07:20 4.1156 +03:07:25 4.0766 +03:07:30 4.1468 +03:07:35 4.1760 +03:07:40 4.1077 +03:07:45 4.1175 +03:07:50 4.1451 +03:07:55 4.0797 +03:08:00 4.1240 +03:08:05 4.1027 +03:08:10 4.1440 +03:08:15 4.1360 +03:08:20 4.0976 +03:08:25 4.0771 +03:08:30 4.0802 +03:08:35 4.1210 +03:08:40 4.1056 +03:08:45 4.1336 +03:08:50 4.1100 +03:08:55 4.0467 +03:09:00 4.0225 +03:09:05 4.0678 +03:09:10 4.0075 +03:09:15 4.0357 +03:09:20 4.0428 +03:09:25 4.0406 +03:09:30 4.0600 +03:09:35 4.0433 +03:09:40 4.1408 +03:09:45 4.1136 +03:09:50 4.1204 +03:09:55 4.0246 +03:10:00 4.0828 +03:10:05 4.1390 +03:10:10 4.0488 +03:10:15 4.0668 +03:10:20 4.0832 +03:10:25 4.1146 +03:10:30 4.0666 +03:10:35 4.0692 +03:10:40 4.1054 +03:10:45 4.0989 +03:10:50 4.0424 +03:10:55 4.0947 +03:11:00 4.0327 +03:11:05 3.9934 +03:11:10 4.0688 +03:11:15 4.0088 +03:11:20 4.1014 +03:11:25 4.0932 +03:11:30 4.0402 +03:11:35 4.1291 +03:11:40 4.1372 +03:11:45 4.0839 +03:11:50 4.0783 +03:11:55 4.1791 +03:12:00 4.1564 +03:12:05 4.0737 +03:12:10 4.1292 +03:12:15 4.1274 +03:12:20 4.1224 +03:12:25 4.0596 +03:12:30 4.0650 +03:12:35 4.0860 +03:12:40 4.0893 +03:12:45 4.1373 +03:12:50 4.1264 +03:12:55 4.1000 +03:13:00 4.1198 +03:13:05 4.0719 +03:13:10 4.0167 +03:13:15 4.1536 +03:13:20 4.1055 +03:13:25 4.0760 +03:13:30 4.0403 +03:13:35 4.0570 +03:13:40 4.0695 +03:13:45 4.0837 +03:13:50 4.1057 +03:13:55 4.1097 +03:14:00 4.1634 +03:14:05 4.0933 +03:14:10 4.0823 +03:14:15 4.0529 +03:14:20 4.1354 +03:14:25 4.1684 +03:14:30 4.2188 +03:14:35 4.1617 +03:14:40 4.0973 +03:14:45 4.0808 +03:14:50 4.0971 +03:14:55 4.1060 +03:15:00 4.1402 +03:15:05 4.0381 +03:15:10 4.0710 +03:15:15 4.1055 +03:15:20 4.1075 +03:15:25 4.1232 +03:15:30 4.0364 +03:15:35 4.0091 +03:15:40 3.9933 +03:15:45 3.8853 +03:15:50 4.0392 +03:15:55 4.0995 +03:16:00 4.1262 +03:16:05 4.0573 +03:16:10 4.0465 +03:16:15 3.9943 +03:16:20 4.0505 +03:16:25 4.1423 +03:16:30 4.2078 +03:16:35 4.1614 +03:16:40 4.1824 +03:16:45 4.1035 +03:16:50 4.0936 +03:16:55 4.0976 +03:17:00 4.0418 +03:17:05 4.1097 +03:17:10 4.1738 +03:17:15 4.1959 +03:17:20 4.1691 +03:17:25 4.1501 +03:17:30 4.0586 +03:17:35 4.0942 +03:17:40 4.1271 +03:17:45 4.0632 +03:17:50 4.0544 +03:17:55 4.1258 +03:18:00 4.1190 +03:18:05 4.0506 +03:18:10 4.0809 +03:18:15 4.1472 +03:18:20 4.1121 +03:18:25 4.1344 +03:18:30 4.1406 +03:18:35 4.1596 +03:18:40 4.1191 +03:18:45 4.1143 +03:18:50 4.1009 +03:18:55 4.1065 +03:19:00 4.0991 +03:19:05 4.0660 +03:19:10 4.1063 +03:19:15 4.1200 +03:19:20 4.0719 +03:19:25 4.1635 +03:19:30 4.1574 +03:19:35 4.1018 +03:19:40 4.1351 +03:19:45 4.1640 +03:19:50 4.2062 +03:19:55 4.0675 +03:20:00 4.0413 +03:20:05 4.1516 +03:20:10 4.1455 +03:20:15 4.0872 +03:20:20 4.0921 +03:20:25 4.1274 +03:20:30 4.1323 +03:20:35 4.0566 +03:20:40 4.0706 +03:20:45 4.0695 +03:20:50 4.0917 +03:20:55 4.1306 +03:21:00 4.0980 +03:21:05 4.0703 +03:21:10 4.0007 +03:21:15 4.0700 +03:21:20 4.1399 +03:21:25 4.1356 +03:21:30 4.1054 +03:21:35 4.1969 +03:21:40 4.1810 +03:21:45 4.1381 +03:21:50 4.0862 +03:21:55 4.0516 +03:22:00 4.0927 +03:22:05 4.1543 +03:22:10 4.1429 +03:22:15 4.0967 +03:22:20 4.1220 +03:22:25 4.1505 +03:22:30 4.0861 +03:22:35 4.0619 +03:22:40 4.1568 +03:22:45 4.0920 +03:22:50 4.0637 +03:22:55 4.0182 +03:23:00 4.0144 +03:23:05 4.0843 +03:23:10 4.0707 +03:23:15 4.1107 +03:23:20 4.0430 +03:23:25 4.0661 +03:23:30 4.0051 +03:23:35 3.9915 +03:23:40 4.0533 +03:23:45 4.0918 +03:23:50 4.1738 +03:23:55 4.1084 +03:24:00 4.0252 +03:24:05 4.1564 +03:24:10 4.1298 +03:24:15 4.1455 +03:24:20 4.1364 +03:24:25 4.0543 +03:24:30 4.0216 +03:24:35 4.1261 +03:24:40 4.1728 +03:24:45 4.0437 +03:24:50 4.0671 +03:24:55 4.1263 +03:25:00 4.0551 +03:25:05 4.0718 +03:25:10 4.1349 +03:25:15 4.0825 +03:25:20 4.0279 +03:25:25 4.0257 +03:25:30 4.1480 +03:25:35 4.1636 +03:25:40 4.1250 +03:25:45 4.1213 +03:25:50 4.0553 +03:25:55 4.1455 +03:26:00 4.2220 +03:26:05 4.1360 +03:26:10 4.0771 +03:26:15 4.0674 +03:26:20 4.1989 +03:26:25 4.1933 +03:26:30 4.2114 +03:26:35 4.0949 +03:26:40 4.0010 +03:26:45 4.0408 +03:26:50 3.9995 +03:26:55 4.0116 +03:27:00 4.0847 +03:27:05 4.0871 +03:27:10 4.0559 +03:27:15 4.0309 +03:27:20 4.0139 +03:27:25 4.0344 +03:27:30 4.0061 +03:27:35 3.9770 +03:27:40 4.0691 +03:27:45 4.0890 +03:27:50 4.1624 +03:27:55 4.1714 +03:28:00 4.1638 +03:28:05 4.0514 +03:28:10 3.9973 +03:28:15 4.0042 +03:28:20 4.0733 +03:28:25 4.1493 +03:28:30 4.2131 +03:28:35 4.1347 +03:28:40 4.0391 +03:28:45 3.9889 +03:28:50 4.0210 +03:28:55 4.0736 +03:29:00 4.1372 +03:29:05 4.1210 +03:29:10 4.1327 +03:29:15 4.1271 +03:29:20 4.1268 +03:29:25 4.0642 +03:29:30 4.0466 +03:29:35 4.1035 +03:29:40 4.1575 +03:29:45 4.1185 +03:29:50 4.1214 +03:29:55 4.1195 +03:30:00 4.1219 +03:30:05 4.1849 +03:30:10 4.2003 +03:30:15 4.1411 +03:30:20 4.1188 +03:30:25 4.1439 +03:30:30 4.1558 +03:30:35 4.1287 +03:30:40 4.0971 +03:30:45 4.1250 +03:30:50 4.0095 +03:30:55 4.0387 +03:31:00 4.0435 +03:31:05 4.0927 +03:31:10 4.0469 +03:31:15 4.1214 +03:31:20 4.1057 +03:31:25 4.0247 +03:31:30 4.0024 +03:31:35 4.0644 +03:31:40 4.1388 +03:31:45 4.0509 +03:31:50 4.0903 +03:31:55 4.0752 +03:32:00 4.1232 +03:32:05 4.2227 +03:32:10 4.0351 +03:32:15 4.0138 +03:32:20 4.0174 +03:32:25 4.0164 +03:32:30 4.0900 +03:32:35 4.0515 +03:32:40 4.0767 +03:32:45 4.1111 +03:32:50 4.0863 +03:32:55 4.1281 +03:33:00 4.1409 +03:33:05 4.1926 +03:33:10 4.1241 +03:33:15 4.0211 +03:33:20 4.1523 +03:33:25 4.0626 +03:33:30 4.0456 +03:33:35 4.1214 +03:33:40 4.0988 +03:33:45 4.1106 +03:33:50 4.0771 +03:33:55 4.1017 +03:34:00 4.0340 +03:34:05 4.1042 +03:34:10 4.1357 +03:34:15 4.1079 +03:34:20 4.0983 +03:34:25 4.1115 +03:34:30 4.0105 +03:34:35 4.0296 +03:34:40 4.0804 +03:34:45 4.1351 +03:34:50 4.1214 +03:34:55 4.0661 +03:35:00 4.0082 +03:35:05 4.0327 +03:35:10 4.0934 +03:35:15 4.0779 +03:35:20 4.0803 +03:35:25 4.1371 +03:35:30 4.1480 +03:35:35 4.1340 +03:35:40 4.0830 +03:35:45 4.1148 +03:35:50 4.1593 +03:35:55 4.0933 +03:36:00 4.0524 +03:36:05 4.0881 +03:36:10 4.1333 +03:36:15 4.1515 +03:36:20 4.0873 +03:36:25 4.1275 +03:36:30 4.1389 +03:36:35 4.0835 +03:36:40 4.0352 +03:36:45 4.0414 +03:36:50 4.0521 +03:36:55 4.0319 +03:37:00 4.1085 +03:37:05 4.1735 +03:37:10 4.1726 +03:37:15 4.0721 +03:37:20 4.0644 +03:37:25 4.0078 +03:37:30 4.0888 +03:37:35 4.0549 +03:37:40 3.9830 +03:37:45 3.9840 +03:37:50 4.1003 +03:37:55 4.1493 +03:38:00 4.1249 +03:38:05 4.1075 +03:38:10 4.1733 +03:38:15 4.1136 +03:38:20 4.0827 +03:38:25 4.1236 +03:38:30 4.1258 +03:38:35 4.0725 +03:38:40 4.0357 +03:38:45 3.9690 +03:38:50 4.1165 +03:38:55 4.1759 +03:39:00 4.1197 +03:39:05 4.1025 +03:39:10 4.0569 +03:39:15 4.0400 +03:39:20 3.9677 +03:39:25 3.9899 +03:39:30 4.0343 +03:39:35 4.0667 +03:39:40 4.1553 +03:39:45 4.1993 +03:39:50 4.0512 +03:39:55 4.0495 +03:40:00 4.2104 +03:40:05 4.1452 +03:40:10 4.0911 +03:40:15 4.1267 +03:40:20 4.0580 +03:40:25 4.0931 +03:40:30 4.2129 +03:40:35 4.1885 +03:40:40 4.1646 +03:40:45 4.1272 +03:40:50 4.0508 +03:40:55 4.0526 +03:41:00 4.0093 +03:41:05 3.9395 +03:41:10 4.0097 +03:41:15 4.1250 +03:41:20 4.0749 +03:41:25 4.0373 +03:41:30 4.0265 +03:41:35 3.9805 +03:41:40 4.0195 +03:41:45 3.9868 +03:41:50 4.0456 +03:41:55 4.1267 +03:42:00 4.1139 +03:42:05 4.1279 +03:42:10 4.1334 +03:42:15 4.0094 +03:42:20 4.0214 +03:42:25 4.0687 +03:42:30 3.9961 +03:42:35 4.0201 +03:42:40 4.0599 +03:42:45 4.0127 +03:42:50 3.9634 +03:42:55 4.0015 +03:43:00 4.0272 +03:43:05 4.1380 +03:43:10 4.2216 +03:43:15 4.1309 +03:43:20 4.0389 +03:43:25 3.9899 +03:43:30 3.9616 +03:43:35 4.0915 +03:43:40 4.0850 +03:43:45 4.0882 +03:43:50 4.0155 +03:43:55 4.0403 +03:44:00 4.0989 +03:44:05 4.0829 +03:44:10 4.0665 +03:44:15 4.0139 +03:44:20 3.9861 +03:44:25 4.0388 +03:44:30 4.0862 +03:44:35 4.0795 +03:44:40 4.1346 +03:44:45 4.1377 +03:44:50 4.0644 +03:44:55 4.1428 +03:45:00 4.1881 +03:45:05 4.1589 +03:45:10 4.0610 +03:45:15 4.0326 +03:45:20 4.1169 +03:45:25 4.1220 +03:45:30 4.1094 +03:45:35 4.0821 +03:45:40 4.1222 +03:45:45 4.1364 +03:45:50 4.1695 +03:45:55 4.0808 +03:46:00 4.0312 +03:46:05 4.0693 +03:46:10 4.0875 +03:46:15 4.1168 +03:46:20 4.1181 +03:46:25 4.0864 +03:46:30 4.0701 +03:46:35 4.1188 +03:46:40 4.0679 +03:46:45 4.0386 +03:46:50 4.0968 +03:46:55 4.0646 +03:47:00 4.0605 +03:47:05 4.1020 +03:47:10 4.0454 +03:47:15 4.0461 +03:47:20 4.0385 +03:47:25 4.1900 +03:47:30 4.1727 +03:47:35 4.0939 +03:47:40 4.0743 +03:47:45 4.0681 +03:47:50 3.9906 +03:47:55 4.0196 +03:48:00 4.0161 +03:48:05 4.0575 +03:48:10 4.1365 +03:48:15 4.1526 +03:48:20 4.1624 +03:48:25 4.2395 +03:48:30 4.1461 +03:48:35 4.0544 +03:48:40 4.1201 +03:48:45 4.1805 +03:48:50 4.0824 +03:48:55 4.0525 +03:49:00 3.9447 +03:49:05 3.9959 +03:49:10 4.0445 +03:49:15 4.0889 +03:49:20 4.0726 +03:49:25 4.1557 +03:49:30 4.1940 +03:49:35 4.1565 +03:49:40 4.0872 +03:49:45 4.0207 +03:49:50 4.0388 +03:49:55 4.0634 +03:50:00 4.1249 +03:50:05 4.0759 +03:50:10 4.0690 +03:50:15 4.0278 +03:50:20 4.0300 +03:50:25 4.0809 +03:50:30 4.0543 +03:50:35 3.9684 +03:50:40 3.9701 +03:50:45 4.0408 +03:50:50 4.0582 +03:50:55 3.9921 +03:51:00 3.9987 +03:51:05 4.0121 +03:51:10 4.0869 +03:51:15 4.0599 +03:51:20 3.9479 +03:51:25 3.9299 +03:51:30 3.9944 +03:51:35 4.0353 +03:51:40 4.1287 +03:51:45 4.2029 +03:51:50 4.1889 +03:51:55 4.0635 +03:52:00 4.0499 +03:52:05 4.0087 +03:52:10 4.0939 +03:52:15 4.0038 +03:52:20 4.1144 +03:52:25 4.1320 +03:52:30 4.0804 +03:52:35 4.0936 +03:52:40 4.1385 +03:52:45 4.1975 +03:52:50 4.1150 +03:52:55 4.0685 +03:53:00 4.0558 +03:53:05 4.0661 +03:53:10 4.1773 +03:53:15 4.1220 +03:53:20 4.0951 +03:53:25 4.1330 +03:53:30 3.9785 +03:53:35 4.0393 +03:53:40 4.0464 +03:53:45 4.0887 +03:53:50 4.0785 +03:53:55 4.0773 +03:54:00 4.0992 +03:54:05 4.0371 +03:54:10 4.0279 +03:54:15 4.0739 +03:54:20 4.0866 +03:54:25 4.1586 +03:54:30 4.1835 +03:54:35 4.0854 +03:54:40 4.0849 +03:54:45 4.1371 +03:54:50 4.1244 +03:54:55 4.0534 +03:55:00 4.0000 +03:55:05 4.1373 +03:55:10 4.1107 +03:55:15 4.1157 +03:55:20 4.2205 +03:55:25 4.0826 +03:55:30 4.0982 +03:55:35 4.0992 +03:55:40 4.0231 +03:55:45 4.1023 +03:55:50 4.0665 +03:55:55 4.1014 +03:56:00 4.1579 +03:56:05 4.0374 +03:56:10 3.9536 +03:56:15 3.9601 +03:56:20 4.0342 +03:56:25 4.0892 +03:56:30 4.1104 +03:56:35 4.0786 +03:56:40 4.0338 +03:56:45 4.0734 +03:56:50 4.1287 +03:56:55 4.1460 +03:57:00 4.1107 +03:57:05 3.9202 +03:57:10 3.9638 +03:57:15 4.0306 +03:57:20 4.0301 +03:57:25 3.9349 +03:57:30 3.8937 +03:57:35 3.9744 +03:57:40 4.0193 +03:57:45 4.0096 +03:57:50 4.1072 +03:57:55 4.1041 +03:58:00 4.0964 +03:58:05 4.0788 +03:58:10 4.1031 +03:58:15 4.1508 +03:58:20 4.1031 +03:58:25 4.0651 +03:58:30 4.0691 +03:58:35 4.0602 +03:58:40 4.0623 +03:58:45 4.0295 +03:58:50 4.0300 +03:58:55 4.1210 +03:59:00 4.1525 +03:59:05 4.1610 +03:59:10 4.0985 +03:59:15 4.1171 +03:59:20 4.1181 +03:59:25 4.0968 +03:59:30 4.0856 +03:59:35 4.0566 +03:59:40 4.0068 +03:59:45 4.0150 +03:59:50 4.0226 +03:59:55 4.0169 +04:00:00 4.0679 +04:00:05 4.1281 +04:00:10 4.0493 +04:00:15 4.0775 +04:00:20 4.0697 +04:00:25 4.0661 +04:00:30 4.0754 +04:00:35 4.0200 +04:00:40 4.0925 +04:00:45 4.0704 +04:00:50 4.1350 +04:00:55 4.1498 +04:01:00 4.0286 +04:01:05 4.0097 +04:01:10 4.0457 +04:01:15 4.0688 +04:01:20 4.0411 +04:01:25 4.0013 +04:01:30 4.0320 +04:01:35 4.0575 +04:01:40 4.0417 +04:01:45 4.0236 +04:01:50 4.0083 +04:01:55 4.0328 +04:02:00 4.0877 +04:02:05 4.0233 +04:02:10 4.0447 +04:02:15 4.0790 +04:02:20 4.0779 +04:02:25 4.1271 +04:02:30 4.0494 +04:02:35 4.0827 +04:02:40 4.0855 +04:02:45 3.9795 +04:02:50 3.9926 +04:02:55 3.9872 +04:03:00 3.9898 +04:03:15 4.1434 +04:03:20 4.0862 +04:03:25 4.0710 +04:03:30 4.1321 +04:03:35 4.0485 +04:03:40 4.0912 +04:03:45 4.1749 +04:03:50 4.1120 +04:03:55 4.0377 +04:04:00 4.0652 +04:04:05 4.1590 +04:04:10 4.1452 +04:04:15 4.0786 +04:04:20 4.0665 +04:04:25 4.0450 +04:04:30 4.0794 +04:04:35 4.1068 +04:04:40 4.0494 +04:04:45 4.1125 +04:04:50 4.1474 +04:04:55 4.0877 +04:05:00 4.0910 +04:05:05 4.1541 +04:05:10 4.0742 +04:05:15 4.0808 +04:05:20 4.1614 +04:05:25 4.0797 +04:05:30 4.0938 +04:05:35 4.0854 +04:05:40 4.0572 +04:05:45 4.1223 +04:05:50 4.0129 +04:05:55 4.0117 +04:06:00 4.0416 +04:06:05 4.0288 +04:06:10 4.0170 +04:06:15 4.0264 +04:06:20 3.9962 +04:06:25 4.0271 +04:06:30 4.0211 +04:06:35 3.9995 +04:06:40 4.0868 +04:06:45 4.0743 +04:06:50 4.1167 +04:06:55 4.0344 +04:07:00 4.0152 +04:07:05 4.0559 +04:07:10 4.0426 +04:07:15 4.0358 +04:07:20 4.0760 +04:07:25 4.0081 +04:07:30 3.9883 +04:07:35 4.0747 +04:07:40 4.1201 +04:07:45 4.1002 +04:07:50 3.9981 +04:07:55 4.0132 +04:08:00 3.9810 +04:08:05 3.9970 +04:08:10 4.0591 +04:08:15 4.0192 +04:08:20 4.0776 +04:08:25 3.9858 +04:08:30 3.9701 +04:08:35 4.0298 +04:08:40 4.0461 +04:08:45 4.1026 +04:08:50 4.0422 +04:08:55 4.0782 +04:09:00 4.1013 +04:09:05 4.0557 +04:09:10 4.0287 +04:09:15 4.0880 +04:09:20 4.0791 +04:09:25 4.0986 +04:09:30 4.1017 +04:09:35 4.0603 +04:09:40 4.0401 +04:09:45 4.0433 +04:09:50 4.0454 +04:09:55 4.0437 +04:10:00 4.0338 +04:10:05 4.1308 +04:10:10 4.0189 +04:10:15 4.0419 +04:10:20 4.0833 +04:10:25 4.0988 +04:10:30 4.1208 +04:10:35 4.0149 +04:10:40 4.0414 +04:10:45 4.0626 +04:10:50 4.0934 +04:10:55 3.9615 +04:11:00 4.0348 +04:11:05 4.0945 +04:11:10 4.0446 +04:11:15 4.0064 +04:11:20 4.0353 +04:11:25 4.0910 +04:11:30 4.0711 +04:11:35 4.0977 +04:11:40 4.0508 +04:11:45 4.0511 +04:11:50 4.0279 +04:11:55 4.0479 +04:12:00 4.0377 +04:12:05 4.0103 +04:12:10 3.9387 +04:12:15 3.9166 +04:12:20 3.9891 +04:12:25 4.0158 +04:12:30 4.0790 +04:12:35 4.1182 +04:12:40 4.0019 +04:12:45 3.8491 +04:12:50 4.0451 +04:12:55 4.1162 +04:13:00 4.0950 +04:13:05 4.1231 +04:13:10 4.0735 +04:13:15 4.0462 +04:13:20 4.0216 +04:13:25 4.0860 +04:13:30 4.0824 +04:13:35 4.0969 +04:13:40 4.0873 +04:13:45 3.9946 +04:13:50 4.0223 +04:13:55 3.9728 +04:14:00 4.0428 +04:14:05 4.0623 +04:14:10 4.0626 +04:14:15 4.1187 +04:14:20 4.0752 +04:14:25 4.0912 +04:14:30 4.0005 +04:14:35 4.0054 +04:14:40 4.0669 +04:14:45 4.1372 +04:14:50 4.0446 +04:14:55 4.0436 +04:15:00 4.0354 +04:15:05 4.0954 +04:15:10 4.0800 +04:15:15 4.0128 +04:15:20 4.0240 +04:15:25 4.0094 +04:15:30 4.1446 +04:15:35 4.1474 +04:15:40 4.0492 +04:15:45 4.0400 +04:15:50 3.9749 +04:15:55 4.0986 +04:16:00 4.0816 +04:16:05 4.0116 +04:16:10 4.1562 +04:16:15 4.0844 +04:16:20 4.0762 +04:16:25 4.0181 +04:16:30 4.0866 +04:16:35 4.0120 +04:16:40 3.9740 +04:16:45 4.0272 +04:16:50 4.0410 +04:16:55 4.0270 +04:17:00 3.9885 +04:17:05 3.8586 +04:17:10 3.8953 +04:17:15 4.0647 +04:17:20 4.0308 +04:17:25 4.1195 +04:17:30 4.1192 +04:17:35 4.0678 +04:17:40 4.0904 +04:17:45 4.0457 +04:17:50 4.0033 +04:17:55 4.0700 +04:18:00 4.0485 +04:18:05 4.0782 +04:18:10 3.9996 +04:18:15 4.0400 +04:18:20 4.0871 +04:18:25 4.1521 +04:18:30 4.1135 +04:18:35 4.0407 +04:18:40 4.0883 +04:18:45 4.1141 +04:18:50 4.0726 +04:18:55 4.1482 +04:19:00 4.0820 +04:19:05 4.0976 +04:19:10 4.0710 +04:19:15 4.0883 +04:19:20 4.0456 +04:19:25 3.9887 +04:19:30 4.0172 +04:19:35 4.1133 +04:19:40 4.1168 +04:19:45 4.0405 +04:19:50 3.9421 +04:19:55 3.9902 +04:20:00 4.0676 +04:20:05 4.0501 +04:20:10 4.0215 +04:20:15 4.0471 +04:20:20 4.1573 +04:20:25 4.1254 +04:20:30 4.1137 +04:20:35 4.0218 +04:20:40 4.0644 +04:20:45 4.1014 +04:20:50 4.0293 +04:20:55 4.0704 +04:21:00 4.0560 +04:21:05 3.9641 +04:21:10 4.1124 +04:21:15 4.1308 +04:21:20 4.0865 +04:21:25 4.1106 +04:21:30 4.0873 +04:21:35 4.0260 +04:21:40 4.1024 +04:21:45 4.1577 +04:21:50 4.1546 +04:21:55 4.0419 +04:22:00 3.9838 +04:22:05 3.9895 +04:22:10 4.0644 +04:22:15 4.0941 +04:22:20 4.0800 +04:22:25 4.1094 +04:22:30 4.0694 +04:22:35 4.0247 +04:22:40 4.0559 +04:22:45 4.0296 +04:22:50 3.9198 +04:22:55 3.9093 +04:23:00 4.0556 +04:23:05 4.0296 +04:23:10 4.0937 +04:23:15 4.0862 +04:23:20 4.1121 +04:23:25 4.0896 +04:23:30 4.1232 +04:23:35 4.0697 +04:23:40 4.0028 +04:23:45 4.0742 +04:23:50 4.1085 +04:23:55 4.1121 +04:24:00 4.0926 +04:24:05 4.0860 +04:24:10 4.1171 +04:24:15 4.0818 +04:24:20 4.0521 +04:24:25 4.1181 +04:24:30 4.1291 +04:24:35 4.1055 +04:24:40 4.0621 +04:24:45 4.0248 +04:24:50 4.0452 +04:24:55 4.0003 +04:25:00 3.9861 +04:25:05 4.0202 +04:25:10 4.0330 +04:25:15 4.1033 +04:25:20 4.0639 +04:25:25 4.0326 +04:25:30 4.0411 +04:25:35 4.0748 +04:25:40 4.0267 +04:25:45 3.8942 +04:25:50 3.9230 +04:25:55 3.9923 +04:26:00 4.0184 +04:26:05 4.1140 +04:26:10 4.1631 +04:26:15 4.1512 +04:26:20 4.1172 +04:26:25 3.9456 +04:26:30 3.9539 +04:26:35 3.9670 +04:26:40 3.9803 +04:26:45 4.0176 +04:26:50 4.0582 +04:26:55 4.0807 +04:27:00 4.0090 +04:27:05 3.9903 +04:27:10 4.0705 +04:27:15 4.1370 +04:27:20 4.1659 +04:27:25 4.1442 +04:27:30 4.0738 +04:27:35 3.9483 +04:27:40 4.0582 +04:27:45 4.0499 +04:27:50 4.0891 +04:27:55 4.1162 +04:28:00 4.0254 +04:28:05 4.0193 +04:28:10 4.0301 +04:28:15 4.0661 +04:28:20 4.0938 +04:28:25 4.1089 +04:28:30 4.1480 +04:28:35 4.1721 +04:28:40 4.1785 +04:28:45 4.2213 +04:28:50 4.0574 +04:28:55 3.9775 +04:29:00 3.9938 +04:29:05 4.0620 +04:29:10 4.0487 +04:29:15 3.9717 +04:29:20 3.9880 +04:29:25 4.0630 +04:29:30 4.0240 +04:29:35 3.9478 +04:29:40 4.0095 +04:29:45 4.0468 +04:29:50 4.0174 +04:29:55 3.9363 +04:30:00 3.9969 +04:30:05 3.9682 +04:30:10 4.0608 +04:30:15 4.0622 +04:30:20 3.9997 +04:30:25 4.1280 +04:30:30 4.1360 +04:30:35 4.0142 +04:30:40 4.0664 +04:30:45 4.1031 +04:30:50 4.0027 +04:30:55 3.9513 +04:31:00 4.1246 +04:31:05 4.0150 +04:31:10 3.9790 +04:31:15 4.1175 +04:31:20 4.1218 +04:31:25 4.1292 +04:31:30 4.0749 +04:31:35 3.9309 +04:31:40 3.9193 +04:31:45 3.9333 +04:31:50 3.9845 +04:31:55 4.0423 +04:32:00 3.9989 +04:32:05 3.9556 +04:32:10 4.0899 +04:32:15 4.1073 +04:32:20 4.0402 +04:32:25 4.0683 +04:32:30 4.1364 +04:32:35 4.1568 +04:32:40 4.0797 +04:32:45 4.0491 +04:32:50 4.0471 +04:32:55 3.9937 +04:33:00 4.0290 +04:33:05 4.0669 +04:33:10 4.0924 +04:33:15 3.9666 +04:33:20 4.0217 +04:33:25 4.1640 +04:33:30 4.1051 +04:33:35 4.0035 +04:33:40 4.0447 +04:33:45 4.0698 +04:33:50 4.0754 +04:33:55 4.0518 +04:34:00 4.0559 +04:34:05 4.0504 +04:34:10 3.9753 +04:34:15 4.0496 +04:34:20 4.0948 +04:34:25 4.0597 +04:34:30 3.9724 +04:34:35 3.9955 +04:34:40 3.9453 +04:34:45 3.9633 +04:34:50 4.0974 +04:34:55 4.0312 +04:35:00 3.9932 +04:35:05 3.9990 +04:35:10 4.0152 +04:35:15 4.0381 +04:35:20 4.0324 +04:35:25 4.0169 +04:35:30 3.9831 +04:35:35 4.0750 +04:35:40 4.1812 +04:35:45 4.0404 +04:35:50 3.9617 +04:35:55 4.1232 +04:36:00 4.1599 +04:36:05 4.0862 +04:36:10 4.0233 +04:36:15 3.9876 +04:36:20 3.9762 +04:36:25 3.9575 +04:36:30 4.0419 +04:36:35 4.0650 +04:36:40 4.0659 +04:36:45 4.0091 +04:36:50 4.0967 +04:36:55 4.1526 +04:37:00 4.1363 +04:37:05 4.1243 +04:37:10 4.0998 +04:37:15 4.0470 +04:37:20 4.0278 +04:37:25 4.0642 +04:37:30 4.0673 +04:37:35 4.0763 +04:37:40 4.0777 +04:37:45 4.0406 +04:37:50 4.0179 +04:37:55 4.0860 +04:38:00 4.0051 +04:38:05 4.0037 +04:38:10 3.9706 +04:38:15 4.0890 +04:38:20 4.1843 +04:38:25 4.0746 +04:38:30 4.0455 +04:38:35 4.1092 +04:38:40 4.1336 +04:38:45 4.1179 +04:38:50 4.0065 +04:38:55 4.1217 +04:39:00 4.1688 +04:39:05 4.0801 +04:39:10 4.1168 +04:39:15 4.1245 +04:39:20 4.0489 +04:39:25 4.0563 +04:39:30 4.0822 +04:39:35 4.0695 +04:39:40 4.0185 +04:39:45 4.0599 +04:39:50 4.0696 +04:39:55 3.9724 +04:40:00 3.9328 +04:40:05 4.0173 +04:40:10 3.9281 +04:40:15 3.9980 +04:40:20 4.0663 +04:40:25 4.0474 +04:40:30 4.0927 +04:40:35 4.1530 +04:40:40 4.0857 +04:40:45 3.9822 +04:40:50 3.9937 +04:40:55 4.0347 +04:41:00 3.9632 +04:41:05 3.9870 +04:41:10 4.0363 +04:41:15 4.0968 +04:41:20 4.0706 +04:41:25 4.0496 +04:41:30 4.0511 +04:41:35 4.0849 +04:41:40 4.0673 +04:41:45 4.0049 +04:41:50 3.9664 +04:41:55 4.0196 +04:42:00 4.1047 +04:42:05 4.1748 +04:42:10 4.0686 +04:42:15 4.0325 +04:42:20 4.1578 +04:42:25 4.0263 +04:42:30 4.0201 +04:42:35 4.0448 +04:42:40 4.1217 +04:42:45 4.1234 +04:42:50 4.0319 +04:42:55 3.9412 +04:43:00 4.0178 +04:43:05 4.0216 +04:43:10 3.9699 +04:43:15 4.0257 +04:43:20 3.9940 +04:43:25 3.9998 +04:43:30 3.9991 +04:43:35 4.0872 +04:43:40 4.0749 +04:43:45 3.9980 +04:43:50 4.0289 +04:43:55 4.1160 +04:44:00 4.0755 +04:44:05 3.9337 +04:44:10 3.9097 +04:44:15 3.9304 +04:44:20 3.9577 +04:44:25 4.0328 +04:44:30 4.0767 +04:44:35 4.0620 +04:44:40 4.0071 +04:44:45 3.9375 +04:44:50 3.9684 +04:44:55 3.9330 +04:45:00 4.0307 +04:45:05 4.0880 +04:45:10 4.1111 +04:45:15 4.0458 +04:45:20 4.1208 +04:45:25 4.0728 +04:45:30 4.0772 +04:45:35 4.0650 +04:45:40 4.0691 +04:45:45 4.0325 +04:45:50 4.0589 +04:45:55 3.9878 +04:46:00 3.9400 +04:46:05 4.0024 +04:46:10 4.0577 +04:46:15 4.0792 +04:46:20 4.0263 +04:46:25 4.0433 +04:46:30 4.0115 +04:46:35 4.0174 +04:46:40 3.9451 +04:46:45 4.0304 +04:46:50 4.1312 +04:46:55 4.0724 +04:47:00 4.0202 +04:47:05 4.0692 +04:47:10 4.0563 +04:47:15 4.0069 +04:47:20 3.9528 +04:47:25 4.0735 +04:47:30 4.1346 +04:47:35 4.0450 +04:47:40 4.0102 +04:47:45 4.0361 +04:47:50 4.0086 +04:47:55 3.9418 +04:48:00 3.9217 +04:48:05 3.9529 +04:48:10 3.9716 +04:48:15 3.9808 +04:48:20 3.9298 +04:48:25 3.9863 +04:48:30 3.9945 +04:48:35 4.0286 +04:48:40 4.0448 +04:48:45 3.9649 +04:48:50 3.9530 +04:48:55 4.0075 +04:49:00 4.0545 +04:49:05 4.0567 +04:49:10 4.0356 +04:49:15 3.9127 +04:49:20 3.9304 +04:49:25 4.0614 +04:49:30 4.1176 +04:49:35 4.0574 +04:49:40 4.1142 +04:49:45 3.9921 +04:49:50 4.0908 +04:49:55 4.0824 +04:50:00 4.0464 +04:50:05 4.0131 +04:50:10 4.0662 +04:50:15 4.0134 +04:50:20 3.9700 +04:50:25 3.9606 +04:50:30 4.0021 +04:50:35 4.0559 +04:50:40 4.0254 +04:50:45 4.0097 +04:50:50 4.0800 +04:50:55 4.0495 +04:51:00 3.9839 +04:51:05 3.9539 +04:51:10 3.9653 +04:51:15 3.9804 +04:51:20 3.9662 +04:51:25 4.0419 +04:51:30 4.0276 +04:51:35 3.9820 +04:51:40 3.9505 +04:51:45 4.0233 +04:51:50 4.0825 +04:51:55 4.0070 +04:52:00 3.9917 +04:52:05 4.0483 +04:52:10 4.0865 +04:52:15 4.1142 +04:52:20 4.0097 +04:52:25 4.0742 +04:52:30 4.0780 +04:52:35 4.0742 +04:52:40 4.0592 +04:52:45 3.9919 +04:52:50 4.0200 +04:52:55 4.0358 +04:53:00 3.9553 +04:53:05 4.1021 +04:53:10 4.1821 +04:53:15 4.0040 +04:53:20 4.0133 +04:53:25 4.0050 +04:53:30 4.0187 +04:53:35 4.0102 +04:53:40 4.0105 +04:53:45 4.1021 +04:53:50 4.2023 +04:53:55 4.1127 +04:54:00 4.0408 +04:54:05 4.0162 +04:54:10 3.9951 +04:54:15 4.0203 +04:54:20 4.0174 +04:54:25 4.0912 +04:54:30 4.0568 +04:54:35 4.1196 +04:54:40 4.0730 +04:54:45 4.0207 +04:54:50 4.0810 +04:54:55 4.0254 +04:55:00 4.0795 +04:55:05 4.0982 +04:55:10 4.0606 +04:55:15 4.0646 +04:55:20 4.0625 +04:55:25 3.9119 +04:55:30 3.9269 +04:55:35 4.0132 +04:55:40 4.0285 +04:55:45 4.0033 +04:55:50 4.0155 +04:55:55 4.1137 +04:56:00 4.0598 +04:56:05 3.9974 +04:56:10 3.9730 +04:56:15 3.9831 +04:56:20 4.0056 +04:56:25 4.0557 +04:56:30 4.1451 +04:56:35 4.0730 +04:56:40 4.1052 +04:56:45 4.0564 +04:56:50 4.0280 +04:56:55 3.9777 +04:57:00 3.9634 +04:57:05 4.0319 +04:57:10 3.9843 +04:57:15 3.9357 +04:57:20 4.1220 +04:57:25 4.1077 +04:57:30 4.1096 +04:57:35 4.0185 +04:57:40 3.9911 +04:57:45 4.0613 +04:57:50 3.9361 +04:57:55 3.9882 +04:58:00 3.9713 +04:58:05 3.9836 +04:58:10 4.0431 +04:58:15 4.0119 +04:58:20 3.9807 +04:58:25 4.0440 +04:58:30 4.0461 +04:58:35 3.9829 +04:58:40 3.9985 +04:58:45 4.0120 +04:58:50 3.9697 +04:58:55 4.0349 +04:59:00 3.9237 +04:59:05 3.9496 +04:59:10 3.9637 +04:59:15 3.9592 +04:59:20 3.9284 +04:59:25 4.0911 +04:59:30 4.1203 +04:59:35 4.0178 +04:59:40 4.0563 +04:59:45 4.0422 +04:59:50 4.1392 +04:59:55 4.0839 +05:00:00 4.1005 +05:00:05 4.0823 +05:00:10 4.0330 +05:00:15 3.9789 +05:00:20 4.1093 +05:00:25 4.1192 +05:00:30 4.0097 +05:00:35 3.9476 +05:00:40 3.9867 +05:00:45 4.0762 +05:00:50 4.1117 +05:00:55 4.1008 +05:01:00 4.1118 +05:01:05 4.0646 +05:01:10 4.0719 +05:01:15 3.9779 +05:01:20 3.9895 +05:01:25 3.9001 +05:01:30 3.9116 +05:01:35 4.1276 +05:01:40 4.0535 +05:01:45 4.0223 +05:01:50 4.0789 +05:01:55 4.0723 +05:02:00 4.0670 +05:02:05 4.0946 +05:02:10 3.9596 +05:02:15 3.9062 +05:02:20 4.0096 +05:02:25 3.9794 +05:02:30 3.9969 +05:02:35 4.0688 +05:02:40 4.1149 +05:02:45 4.0991 +05:02:50 4.0563 +05:02:55 4.0717 +05:03:00 3.9942 +05:03:05 4.0824 +05:03:10 4.1180 +05:03:15 4.1147 +05:03:20 4.0025 +05:03:25 4.0557 +05:03:30 4.0616 +05:03:35 3.9961 +05:03:40 3.9924 +05:03:45 4.0248 +05:03:50 3.9910 +05:03:55 4.0633 +05:04:00 3.9836 +05:04:05 3.9526 +05:04:10 4.0025 +05:04:15 4.0381 +05:04:20 4.0244 +05:04:25 4.0230 +05:04:30 3.9620 +05:04:35 3.9785 +05:04:40 4.0251 +05:04:45 4.0341 +05:04:50 4.0953 +05:04:55 4.0845 +05:05:00 4.0608 +05:05:05 3.9270 +05:05:10 3.9282 +05:05:15 3.9512 +05:05:20 3.9260 +05:05:25 3.9832 +05:05:30 3.9713 +05:05:35 3.9984 +05:05:40 3.9281 +05:05:45 3.9784 +05:05:50 3.9317 +05:05:55 3.9634 +05:06:00 4.0001 +05:06:05 4.0738 +05:06:10 4.1557 +05:06:15 4.1455 +05:06:20 4.1086 +05:06:25 4.1060 +05:06:30 4.2315 +05:06:35 4.1571 +05:06:40 3.9597 +05:06:45 4.0078 +05:06:50 4.0029 +05:06:55 3.9877 +05:07:00 3.9628 +05:07:05 3.9551 +05:07:10 4.0480 +05:07:15 4.1010 +05:07:20 4.0584 +05:07:25 4.0790 +05:07:30 4.0826 +05:07:35 4.0263 +05:07:40 4.0443 +05:07:45 4.0782 +05:07:50 4.0182 +05:07:55 4.0530 +05:08:00 4.0537 +05:08:05 3.9777 +05:08:10 3.9180 +05:08:15 3.9743 +05:08:20 3.9598 +05:08:25 3.9713 +05:08:30 4.0212 +05:08:35 4.0200 +05:08:40 3.9919 +05:08:45 3.9922 +05:08:50 3.9178 +05:08:55 3.9291 +05:09:00 4.0094 +05:09:05 3.9609 +05:09:10 3.9438 +05:09:15 4.0234 +05:09:20 4.1200 +05:09:25 4.1329 +05:09:30 4.1652 +05:09:35 4.1415 +05:09:40 4.0762 +05:09:45 4.1329 +05:09:50 4.0953 +05:09:55 3.8990 +05:10:00 3.8846 +05:10:05 4.0111 +05:10:10 3.9337 +05:10:15 4.0101 +05:10:20 4.0262 +05:10:25 3.9887 +05:10:30 4.0008 +05:10:35 4.0902 +05:10:40 4.1082 +05:10:45 4.0730 +05:10:50 4.0820 +05:10:55 4.0312 +05:11:00 4.1161 +05:11:05 4.1682 +05:11:10 4.0720 +05:11:15 4.0744 +05:11:20 4.1188 +05:11:25 4.1617 +05:11:30 4.1923 +05:11:35 4.0848 +05:11:40 4.0537 +05:11:45 4.0036 +05:11:50 4.0755 +05:11:55 4.1602 +05:12:00 4.1596 +05:12:05 4.0492 +05:12:10 3.9942 +05:12:15 4.0317 +05:12:20 3.9840 +05:12:25 3.9928 +05:12:30 4.0829 +05:12:35 4.2336 +05:12:40 4.1728 +05:12:45 3.9226 +05:12:50 3.9484 +05:12:55 4.1295 +05:13:00 4.0784 +05:13:05 3.9632 +05:13:10 4.0279 +05:13:15 3.9690 +05:13:20 4.0670 +05:13:25 4.0654 +05:13:30 4.1157 +05:13:35 4.0596 +05:13:40 4.0237 +05:13:45 3.9491 +05:13:50 3.9518 +05:13:55 3.8894 +05:14:00 3.8565 +05:14:05 3.9942 +05:14:10 4.1227 +05:14:15 4.1769 +05:14:20 4.1755 +05:14:25 4.1414 +05:14:30 3.9659 +05:14:35 4.0244 +05:14:40 4.0729 +05:14:45 3.9686 +05:14:50 4.0504 +05:14:55 4.0340 +05:15:00 4.0612 +05:15:05 4.0373 +05:15:10 4.1597 +05:15:15 4.1397 +05:15:20 4.2113 +05:15:25 4.0832 +05:15:30 3.9224 +05:15:35 3.9244 +05:15:40 3.9591 +05:15:45 4.0134 +05:15:50 4.1359 +05:15:55 4.1176 +05:16:00 4.0698 +05:16:05 4.0724 +05:16:10 4.0516 +05:16:15 3.9557 +05:16:20 3.9562 +05:16:25 4.0732 +05:16:30 4.1650 +05:16:35 4.1153 +05:16:40 3.9765 +05:16:45 3.9362 +05:16:50 3.9534 +05:16:55 3.9735 +05:17:00 4.0370 +05:17:05 3.9799 +05:17:10 4.0066 +05:17:15 4.0314 +05:17:20 4.0204 +05:17:25 3.9923 +05:17:30 4.0754 +05:17:35 4.1158 +05:17:40 4.0750 +05:17:45 3.9841 +05:17:50 3.9588 +05:17:55 4.0754 +05:18:00 4.0409 +05:18:05 4.0354 +05:18:10 4.0434 +05:18:15 4.0971 +05:18:20 4.0848 +05:18:25 4.0453 +05:18:30 4.0234 +05:18:35 4.0434 +05:18:40 3.9350 +05:18:45 3.9637 +05:18:50 4.0434 +05:18:55 4.0813 +05:19:00 4.0989 +05:19:05 4.0604 +05:19:10 4.0595 +05:19:15 3.9402 +05:19:20 3.9722 +05:19:25 4.0588 +05:19:30 3.8899 +05:19:35 3.9005 +05:19:40 4.0755 +05:19:45 4.0624 +05:19:50 4.0787 +05:19:55 4.0361 +05:20:00 3.9414 +05:20:05 3.8973 +05:20:10 4.0548 +05:20:15 4.1327 +05:20:20 4.0757 +05:20:25 4.0434 +05:20:30 4.0666 +05:20:35 4.1163 +05:20:40 4.0652 +05:20:45 4.0386 +05:20:50 4.1065 +05:20:55 4.0857 +05:21:00 4.0233 +05:21:05 4.0483 +05:21:10 3.9654 +05:21:15 3.9281 +05:21:20 4.0686 +05:21:25 4.1152 +05:21:30 4.0880 +05:21:35 4.0649 +05:21:40 4.0110 +05:21:45 4.0514 +05:21:50 4.1078 +05:21:55 4.0576 +05:22:00 4.0856 +05:22:05 4.0073 +05:22:10 3.8719 +05:22:15 3.9039 +05:22:20 4.0179 +05:22:25 4.0596 +05:22:30 3.9878 +05:22:35 3.9312 +05:22:40 4.0146 +05:22:45 4.1367 +05:22:50 4.1125 +05:22:55 4.0238 +05:23:00 4.0405 +05:23:05 4.0863 +05:23:10 4.0653 +05:23:15 3.9480 +05:23:20 3.9509 +05:23:25 4.0156 +05:23:30 4.1546 +05:23:35 4.0237 +05:23:40 3.9349 +05:23:45 3.9310 +05:23:50 4.0000 +05:23:55 4.1101 +05:24:00 4.0802 +05:24:05 4.0400 +05:24:10 3.9909 +05:24:15 4.0744 +05:24:20 4.1465 +05:24:25 4.0202 +05:24:30 4.0184 +05:24:35 4.1569 +05:24:40 4.1560 +05:24:45 4.0571 +05:24:50 4.0274 +05:24:55 3.9574 +05:25:00 4.0683 +05:25:05 4.0198 +05:25:10 4.0666 +05:25:15 4.0840 +05:25:20 4.0627 +05:25:25 4.0795 +05:25:30 4.0956 +05:25:35 4.0872 +05:25:40 4.0169 +05:25:45 4.0092 +05:25:50 3.9868 +05:25:55 4.1247 +05:26:00 3.9962 +05:26:05 4.0233 +05:26:10 4.0705 +05:26:15 4.0153 +05:26:20 4.0387 +05:26:25 4.0720 +05:26:30 4.0567 +05:26:35 4.0355 +05:26:40 4.0126 +05:26:45 4.0193 +05:26:50 4.0335 +05:26:55 4.0248 +05:27:00 4.0045 +05:27:05 3.9814 +05:27:10 4.0175 +05:27:15 4.0857 +05:27:20 3.9838 +05:27:25 3.9323 +05:27:30 3.9683 +05:27:35 4.0118 +05:27:40 4.0353 +05:27:45 4.0634 +05:27:50 3.9777 +05:27:55 4.0892 +05:28:00 4.0480 +05:28:05 3.9904 +05:28:10 3.9814 +05:28:15 4.0214 +05:28:20 3.9256 +05:28:25 3.9723 +05:28:30 3.9705 +05:28:35 3.9578 +05:28:40 3.9923 +05:28:45 4.0150 +05:28:50 4.0643 +05:28:55 4.0808 +05:29:00 4.0285 +05:29:05 4.1046 +05:29:10 4.1040 +05:29:15 4.0242 +05:29:20 4.0321 +05:29:25 4.0391 +05:29:30 4.0092 +05:29:35 4.0503 +05:29:40 4.1086 +05:29:45 4.1444 +05:29:50 4.0605 +05:29:55 4.0231 +05:30:00 3.9905 +05:30:05 4.0091 +05:30:10 3.9994 +05:30:15 4.0265 +05:30:20 4.0502 +05:30:25 4.0520 +05:30:30 3.9881 +05:30:35 4.0149 +05:30:40 3.9681 +05:30:45 4.0283 +05:30:50 3.9902 +05:30:55 4.1023 +05:31:00 4.1816 +05:31:05 3.9594 +05:31:10 4.0310 +05:31:15 4.0230 +05:31:20 4.0737 +05:31:25 3.9949 +05:31:30 4.0291 +05:31:35 3.9467 +05:31:40 4.0530 +05:31:45 4.0110 +05:31:50 4.0133 +05:31:55 4.0921 +05:32:00 3.9730 +05:32:05 3.9922 +05:32:10 3.9897 +05:32:15 3.9340 +05:32:20 3.9980 +05:32:25 3.9750 +05:32:30 4.0471 +05:32:35 4.0378 +05:32:40 4.0781 +05:32:45 4.0486 +05:32:50 3.9735 +05:32:55 4.0491 +05:33:00 4.0852 +05:33:05 4.0811 +05:33:10 4.0820 +05:33:15 4.0845 +05:33:20 4.0546 +05:33:25 4.1458 +05:33:30 3.9829 +05:33:35 3.9757 +05:33:40 4.0239 +05:33:45 3.9886 +05:33:50 4.0253 +05:33:55 4.0371 +05:34:00 3.9402 +05:34:05 4.0037 +05:34:10 4.0686 +05:34:15 4.0809 +05:34:20 4.0600 +05:34:25 4.0044 +05:34:30 4.0283 +05:34:35 4.0178 +05:34:40 4.0708 +05:34:45 4.1020 +05:34:50 4.0662 +05:34:55 4.0643 +05:35:00 4.0689 +05:35:05 4.0659 +05:35:10 3.9862 +05:35:15 4.0853 +05:35:20 4.0073 +05:35:25 3.9596 +05:35:30 4.0488 +05:35:35 4.0862 +05:35:40 4.0254 +05:35:45 4.0315 +05:35:50 4.0298 +05:35:55 4.0169 +05:36:00 4.0667 +05:36:05 4.1020 +05:36:10 4.1282 +05:36:15 4.1660 +05:36:20 4.1609 +05:36:25 4.0417 +05:36:30 4.0510 +05:36:35 4.0294 +05:36:40 3.9828 +05:36:45 4.0479 +05:36:50 4.0842 +05:36:55 4.0757 +05:37:00 4.0737 +05:37:05 3.9804 +05:37:10 4.0315 +05:37:15 4.0639 +05:37:20 4.0562 +05:37:25 3.9678 +05:37:30 4.0074 +05:37:35 3.9024 +05:37:40 3.9283 +05:37:45 3.9981 +05:37:50 3.9695 +05:37:55 4.0160 +05:38:00 4.0826 +05:38:05 3.9864 +05:38:10 3.8747 +05:38:15 3.9072 +05:38:20 3.9908 +05:38:25 3.9917 +05:38:30 3.9780 +05:38:35 3.9940 +05:38:40 3.9975 +05:38:45 3.9652 +05:38:50 3.9325 +05:38:55 4.0795 +05:39:00 4.0939 +05:39:05 4.0404 +05:39:10 3.9968 +05:39:15 3.9670 +05:39:20 4.0181 +05:39:25 3.9806 +05:39:30 3.9411 +05:39:35 3.9074 +05:39:40 3.9909 +05:39:45 4.0188 +05:39:50 4.0338 +05:39:55 4.1087 +05:40:00 4.1136 +05:40:05 4.0293 +05:40:10 3.9057 +05:40:15 3.8556 +05:40:20 3.9532 +05:40:25 4.0547 +05:40:30 4.0134 +05:40:35 4.0241 +05:40:40 4.0298 +05:40:45 4.0863 +05:40:50 4.0795 +05:40:55 4.0180 +05:41:00 3.9222 +05:41:05 4.0064 +05:41:10 4.0968 +05:41:15 4.0550 +05:41:20 4.0259 +05:41:25 4.0079 +05:41:30 4.0487 +05:41:35 4.0482 +05:41:40 4.0365 +05:41:45 4.0293 +05:41:50 4.0333 +05:41:55 3.9853 +05:42:00 3.9389 +05:42:05 3.8535 +05:42:10 3.8987 +05:42:15 3.9676 +05:42:20 3.9482 +05:42:25 3.9365 +05:42:30 4.0422 +05:42:35 4.1327 +05:42:40 4.0429 +05:42:45 4.0438 +05:42:50 4.0842 +05:42:55 4.0562 +05:43:00 3.9240 +05:43:05 3.9118 +05:43:10 4.1380 +05:43:15 4.0341 +05:43:20 3.9054 +05:43:25 3.9613 +05:43:30 4.1454 +05:43:35 4.0313 +05:43:40 4.0300 +05:43:45 4.0106 +05:43:50 4.0083 +05:43:55 4.0324 +05:44:00 4.0789 +05:44:05 4.1212 +05:44:10 4.0296 +05:44:15 4.0418 +05:44:20 4.0538 +05:44:25 4.0184 +05:44:30 4.0524 +05:44:35 4.0374 +05:44:40 4.0485 +05:44:45 4.0335 +05:44:50 4.0416 +05:44:55 3.9450 +05:45:00 3.9253 +05:45:05 3.9846 +05:45:10 4.0010 +05:45:15 4.0776 +05:45:20 3.9868 +05:45:25 3.9960 +05:45:30 4.0521 +05:45:35 4.0802 +05:45:40 4.0759 +05:45:45 3.9252 +05:45:50 4.0595 +05:45:55 4.1202 +05:46:00 4.0560 +05:46:05 4.0504 +05:46:10 3.9597 +05:46:15 3.9817 +05:46:20 4.0705 +05:46:25 4.1000 +05:46:30 4.0613 +05:46:35 3.9375 +05:46:40 3.9097 +05:46:45 3.9326 +05:46:50 4.0195 +05:46:55 3.9969 +05:47:00 4.0152 +05:47:05 4.0234 +05:47:10 4.0862 +05:47:15 4.1406 +05:47:20 3.9848 +05:47:25 3.9162 +05:47:30 3.9939 +05:47:35 3.9839 +05:47:40 3.9383 +05:47:45 3.9617 +05:47:50 3.9759 +05:47:55 4.0162 +05:48:00 4.1114 +05:48:05 4.1442 +05:48:10 4.0080 +05:48:15 4.0580 +05:48:20 4.0782 +05:48:25 3.9457 +05:48:30 3.8404 +05:48:35 3.9785 +05:48:40 4.0268 +05:48:45 4.0039 +05:48:50 3.9964 +05:48:55 4.0408 +05:49:00 4.0219 +05:49:05 3.9825 +05:49:10 4.0371 +05:49:15 4.0372 +05:49:20 3.9748 +05:49:25 3.9560 +05:49:30 4.1484 +05:49:35 4.0574 +05:49:40 4.0345 +05:49:45 4.1272 +05:49:50 4.0986 +05:49:55 4.0086 +05:50:00 4.0381 +05:50:05 4.0692 +05:50:10 3.9905 +05:50:15 3.9711 +05:50:20 4.0155 +05:50:25 4.1440 +05:50:30 3.9857 +05:50:35 4.0101 +05:50:40 3.8332 +05:50:45 4.0081 +05:50:50 4.0484 +05:50:55 3.9693 +05:51:00 4.0239 +05:51:05 4.0665 +05:51:10 3.9896 +05:51:15 3.9873 +05:51:20 4.0120 +05:51:25 3.9885 +05:51:30 4.0110 +05:51:35 4.0517 +05:51:40 4.0226 +05:51:45 3.9928 +05:51:50 4.0513 +05:51:55 3.9864 +05:52:00 4.0096 +05:52:05 3.9893 +05:52:10 4.0864 +05:52:15 3.9491 +05:52:20 4.0037 +05:52:25 3.9779 +05:52:30 4.0903 +05:52:35 4.0606 +05:52:40 4.0247 +05:52:45 4.0076 +05:52:50 4.1525 +05:52:55 4.0896 +05:53:00 4.0238 +05:53:05 4.0584 +05:53:10 4.0828 +05:53:15 4.0653 +05:53:20 4.0665 +05:53:25 4.0082 +05:53:30 4.0509 +05:53:35 4.1037 +05:53:40 4.1212 +05:53:45 4.1162 +05:53:50 4.0823 +05:53:55 4.0813 +05:54:00 4.1238 +05:54:05 4.1565 +05:54:10 4.1289 +05:54:15 4.0658 +05:54:20 3.9914 +05:54:25 3.9883 +05:54:30 3.9539 +05:54:35 3.9636 +05:54:40 3.9748 +05:54:45 4.0288 +05:54:50 4.0021 +05:54:55 4.0125 +05:55:00 4.0139 +05:55:05 3.9366 +05:55:10 3.9871 +05:55:15 4.0757 +05:55:20 4.1867 +05:55:25 4.0775 +05:55:30 4.0470 +05:55:35 4.0716 +05:55:40 3.9972 +05:55:45 3.9263 +05:55:50 4.0035 +05:55:55 4.0283 +05:56:00 4.0104 +05:56:05 4.0255 +05:56:10 3.9939 +05:56:15 4.0421 +05:56:20 3.9902 +05:56:25 4.1271 +05:56:30 4.0322 +05:56:35 3.9803 +05:56:40 3.9694 +05:56:45 3.9638 +05:56:50 3.9507 +05:56:55 3.9549 +05:57:00 4.0321 +05:57:05 4.0143 +05:57:10 4.0883 +05:57:15 3.9813 +05:57:20 4.0304 +05:57:25 3.8573 +05:57:30 4.0400 +05:57:35 3.9688 +05:57:40 4.1615 +05:57:45 3.9676 +05:57:50 4.0420 +05:57:55 4.0141 +05:58:00 4.0613 +05:58:05 3.9247 +05:58:10 3.9690 +05:58:15 4.0550 +05:58:20 3.9891 +05:58:25 4.1317 +05:58:30 4.0304 +05:58:35 4.2440 +05:58:40 4.1693 +05:58:45 3.8674 +05:58:50 4.1601 +05:58:55 4.3482 +05:59:00 4.3380 +05:59:05 4.2261 +05:59:10 4.0161 +05:59:15 3.7786 +05:59:20 4.0456 +05:59:25 4.1072 +05:59:30 3.8229 +05:59:35 4.1354 +05:59:40 3.9949 +05:59:45 3.7738 +05:59:50 4.0424 +05:59:55 4.1347 +06:00:00 3.8138 +06:00:05 4.0091 +06:00:10 3.8842 +06:00:15 4.0465 +06:00:20 3.8646 +06:00:25 3.9424 +06:00:30 4.0879 +06:00:35 3.7941 +06:00:40 3.9625 +06:00:45 4.1684 +06:00:50 3.8858 +06:00:55 3.8894 +06:01:00 4.1800 +06:01:05 4.1976 +06:01:10 3.9712 +06:01:15 3.7711 +06:01:20 3.9812 +06:01:25 4.2501 +06:01:30 4.0721 +06:01:35 3.7665 +06:01:40 3.9385 +06:01:45 4.2517 +06:01:50 4.3196 +06:01:55 4.1565 +06:02:00 3.8765 +06:02:05 3.7777 +06:02:10 4.0914 +06:02:15 4.0697 +06:02:20 3.8624 +06:02:25 4.1952 +06:02:30 4.0316 +06:02:35 3.9369 +06:02:40 4.2495 +06:02:45 3.9883 +06:02:50 4.1001 +06:02:55 3.9869 +06:03:00 4.1206 +06:03:05 4.0776 +06:03:10 4.0767 +06:03:15 4.0731 +06:03:20 4.0797 +06:03:25 3.9095 +06:03:30 4.1020 +06:03:35 4.0972 +06:03:40 4.0048 +06:03:45 4.0338 +06:03:50 3.9782 +06:03:55 3.9568 +06:04:00 4.0195 +06:04:05 4.0047 +06:04:10 3.9893 +06:04:15 3.9731 +06:04:20 4.0429 +06:04:25 3.9806 +06:04:30 3.9913 +06:04:35 4.0624 +06:04:40 4.1403 +06:04:45 4.0663 +06:04:50 4.1456 +06:04:55 4.0777 +06:05:00 3.9606 +06:05:05 4.0521 +06:05:10 4.0187 +06:05:15 3.8738 +06:05:20 3.9367 +06:05:25 4.0593 +06:05:30 4.0309 +06:05:35 4.0096 +06:05:40 3.9774 +06:05:45 4.0143 +06:05:50 4.0059 +06:05:55 3.9602 +06:06:00 3.9879 +06:06:05 4.0893 +06:06:10 4.1146 +06:06:15 4.0646 +06:06:20 3.9981 +06:06:25 4.0240 +06:06:30 4.0364 +06:06:35 4.0008 +06:06:40 3.9995 +06:06:45 4.1008 +06:06:50 4.1579 +06:06:55 4.0629 +06:07:00 3.9330 +06:07:05 4.1331 +06:07:10 4.0083 +06:07:15 3.8909 +06:07:20 3.9585 +06:07:25 4.0487 +06:07:30 4.0272 +06:07:35 4.1226 +06:07:40 4.0669 +06:07:45 3.9678 +06:07:50 4.0382 +06:07:55 4.0324 +06:08:00 4.0213 +06:08:05 3.9802 +06:08:10 3.9809 +06:08:15 4.0578 +06:08:20 3.9545 +06:08:25 4.0086 +06:08:30 4.0553 +06:08:35 3.9466 +06:08:40 4.0394 +06:08:45 4.1302 +06:08:50 4.1209 +06:08:55 3.9321 +06:09:00 4.0294 +06:09:05 3.9726 +06:09:10 3.9885 +06:09:15 4.0039 +06:09:20 4.0628 +06:09:25 4.0929 +06:09:30 4.1167 +06:09:35 4.0593 +06:09:40 4.0418 +06:09:45 3.9812 +06:09:50 3.9876 +06:09:55 3.9265 +06:10:00 4.0799 +06:10:05 3.9188 +06:10:10 3.9254 +06:10:15 4.1133 +06:10:20 4.1515 +06:10:25 4.1022 +06:10:30 4.0136 +06:10:35 4.0075 +06:10:40 3.9264 +06:10:45 3.9674 +06:10:50 4.0547 +06:10:55 4.1296 +06:11:00 4.0505 +06:11:05 3.8903 +06:11:10 3.9230 +06:11:15 4.0097 +06:11:20 3.9845 +06:11:25 3.9805 +06:11:30 4.0357 +06:11:35 4.0182 +06:11:40 4.0370 +06:11:45 3.9872 +06:11:50 3.9934 +06:11:55 4.0155 +06:12:00 4.0425 +06:12:05 3.9948 +06:12:10 4.0033 +06:12:15 4.0513 +06:12:20 4.0196 +06:12:25 4.0342 +06:12:30 4.0350 +06:12:35 4.0833 +06:12:40 4.0915 +06:12:45 4.0796 +06:12:50 3.9381 +06:12:55 3.9408 +06:13:00 3.9292 +06:13:05 3.9622 +06:13:10 4.0420 +06:13:15 3.9674 +06:13:20 3.9889 +06:13:25 4.0358 +06:13:30 4.0834 +06:13:35 3.9950 +06:13:40 3.9830 +06:13:45 4.0502 +06:13:50 4.0534 +06:13:55 4.0837 +06:14:00 4.0505 +06:14:05 4.0335 +06:14:10 3.9288 +06:14:15 3.9730 +06:14:20 3.9804 +06:14:25 4.0327 +06:14:30 4.0537 +06:14:35 4.0352 +06:14:40 4.0054 +06:14:45 4.0995 +06:14:50 4.0636 +06:14:55 4.1006 +06:15:00 4.0644 +06:15:05 3.9299 +06:15:10 4.0545 +06:15:15 4.0474 +06:15:20 3.9872 +06:15:25 4.0708 +06:15:30 3.9799 +06:15:35 4.0184 +06:15:40 3.9290 +06:15:45 3.8605 +06:15:50 3.9508 +06:15:55 4.0805 +06:16:00 4.0116 +06:16:05 3.8933 +06:16:10 4.0197 +06:16:15 4.0638 +06:16:20 4.0323 +06:16:25 4.0504 +06:16:30 4.0453 +06:16:35 3.9988 +06:16:40 3.9216 +06:16:45 3.9930 +06:16:50 4.0450 +06:16:55 3.9851 +06:17:00 3.9977 +06:17:05 4.0020 +06:17:10 4.0208 +06:17:15 3.9797 +06:17:20 3.9730 +06:17:25 3.9775 +06:17:30 3.9990 +06:17:35 4.0467 +06:17:40 3.9976 +06:17:45 4.0432 +06:17:50 4.1148 +06:17:55 4.1437 +06:18:00 4.0544 +06:18:05 4.0317 +06:18:10 4.0332 +06:18:15 4.0739 +06:18:20 4.0135 +06:18:25 3.9700 +06:18:30 3.9524 +06:18:35 4.0156 +06:18:40 4.0926 +06:18:45 3.9357 +06:18:50 3.9813 +06:18:55 3.9703 +06:19:00 4.1161 +06:19:05 4.0710 +06:19:10 4.0411 +06:19:15 4.1033 +06:19:20 4.1593 +06:19:25 3.9931 +06:19:30 3.9826 +06:19:35 3.9897 +06:19:40 3.9919 +06:19:45 4.0720 +06:19:50 4.1227 +06:19:55 4.0834 +06:20:00 4.0475 +06:20:05 3.9274 +06:20:10 3.9668 +06:20:15 4.0320 +06:20:20 4.0473 +06:20:25 4.0008 +06:20:30 4.0679 +06:20:35 4.1393 +06:20:40 4.0428 +06:20:45 3.9793 +06:20:50 4.0347 +06:20:55 4.0680 +06:21:00 4.0613 +06:21:05 4.0313 +06:21:10 4.0692 +06:21:15 4.0599 +06:21:20 4.0042 +06:21:25 3.8952 +06:21:30 3.9207 +06:21:35 3.9797 +06:21:40 3.9868 +06:21:45 4.0658 +06:21:50 4.1330 +06:21:55 4.0715 +06:22:00 4.0715 +06:22:05 4.0936 +06:22:10 4.0112 +06:22:15 4.1013 +06:22:20 4.1534 +06:22:25 4.1277 +06:22:30 4.0573 +06:22:35 4.0131 +06:22:40 4.1352 +06:22:45 4.0525 +06:22:50 4.0031 +06:22:55 3.9824 +06:23:00 3.9629 +06:23:05 3.9879 +06:23:10 4.0359 +06:23:15 4.0483 +06:23:20 4.0434 +06:23:25 3.9926 +06:23:30 4.0077 +06:23:35 4.0834 +06:23:40 4.0062 +06:23:45 3.9782 +06:23:50 4.0037 +06:23:55 3.9103 +06:24:00 3.8524 +06:24:05 3.9231 +06:24:10 3.9172 +06:24:15 3.8818 +06:24:20 3.8964 +06:24:25 3.9317 +06:24:30 4.0464 +06:24:35 4.1698 +06:24:40 3.9657 +06:24:45 3.8814 +06:24:50 4.0269 +06:24:55 3.9641 +06:25:00 3.9458 +06:25:05 3.9963 +06:25:10 4.1203 +06:25:15 4.0289 +06:25:20 3.9439 +06:25:25 3.9701 +06:25:30 4.0381 +06:25:35 3.9808 +06:25:40 3.9464 +06:25:45 4.0152 +06:25:50 4.0383 +06:25:55 4.0020 +06:26:00 3.9840 +06:26:05 3.9859 +06:26:10 3.9492 +06:26:15 4.0026 +06:26:20 4.0466 +06:26:25 4.0205 +06:26:30 3.9759 +06:26:35 4.0363 +06:26:40 4.0835 +06:26:45 4.0650 +06:26:50 3.9495 +06:26:55 4.0020 +06:27:00 4.0378 +06:27:05 3.9929 +06:27:10 3.9894 +06:27:15 4.0142 +06:27:20 3.9368 +06:27:25 3.9272 +06:27:30 3.9602 +06:27:35 3.9395 +06:27:40 4.0158 +06:27:45 3.9678 +06:27:50 4.0151 +06:27:55 4.0905 +06:28:00 4.0269 +06:28:05 4.0996 +06:28:10 4.1586 +06:28:15 4.1555 +06:28:20 4.0571 +06:28:25 4.0996 +06:28:30 4.0343 +06:28:35 3.9772 +06:28:40 3.9756 +06:28:45 3.9715 +06:28:50 3.9966 +06:28:55 4.1019 +06:29:00 3.9966 +06:29:05 4.0712 +06:29:10 4.0458 +06:29:15 3.9927 +06:29:20 3.9587 +06:29:25 4.0979 +06:29:30 4.0072 +06:29:35 4.0082 +06:29:40 4.0524 +06:29:45 4.0697 +06:29:50 4.0533 +06:29:55 4.0725 +06:30:00 4.1108 +06:30:05 4.0082 +06:30:10 3.9808 +06:30:15 4.0699 +06:30:20 3.9749 +06:30:25 3.9944 +06:30:30 4.0641 +06:30:35 4.1012 +06:30:40 4.0091 +06:30:45 4.0115 +06:30:50 3.9490 +06:30:55 3.9773 +06:31:00 4.0031 +06:31:05 3.9464 +06:31:10 4.0065 +06:31:15 4.0236 +06:31:20 3.9699 +06:31:25 4.0255 +06:31:30 4.0504 +06:31:35 4.0139 +06:31:40 3.9802 +06:31:45 3.9900 +06:31:50 3.9958 +06:31:55 3.9918 +06:32:00 3.9307 +06:32:05 4.0192 +06:32:10 4.0102 +06:32:15 3.9907 +06:32:20 4.0055 +06:32:25 3.9888 +06:32:30 4.0779 +06:32:35 4.1182 +06:32:40 4.0371 +06:32:45 3.9783 +06:32:50 4.0427 +06:32:55 4.0590 +06:33:00 4.0814 +06:33:05 4.1308 +06:33:10 4.0686 +06:33:15 4.0238 +06:33:20 4.0449 +06:33:25 4.0385 +06:33:30 3.9259 +06:33:35 3.8935 +06:33:40 3.9582 +06:33:45 4.0289 +06:33:50 4.0976 +06:33:55 4.0776 +06:34:00 4.0640 +06:34:05 4.0025 +06:34:10 3.9537 +06:34:15 4.0068 +06:34:20 4.0388 +06:34:25 4.1455 +06:34:30 4.0271 +06:34:35 3.9976 +06:34:40 3.9965 +06:34:45 4.0808 +06:34:50 4.1517 +06:34:55 4.0567 +06:35:00 4.0359 +06:35:05 4.0113 +06:35:10 3.9532 +06:35:15 4.0300 +06:35:20 4.0281 +06:35:25 4.0480 +06:35:30 4.0662 +06:35:35 4.0586 +06:35:40 4.0679 +06:35:45 3.9804 +06:35:50 4.0005 +06:35:55 4.0137 +06:36:00 4.1174 +06:36:05 4.0850 +06:36:10 3.9586 +06:36:15 3.9372 +06:36:20 3.9295 +06:36:25 4.0010 +06:36:30 4.0006 +06:36:35 3.9443 +06:36:40 4.0495 +06:36:45 3.9798 +06:36:50 4.0384 +06:36:55 4.0338 +06:37:00 4.0468 +06:37:05 4.0395 +06:37:10 3.9745 +06:37:15 4.1172 +06:37:20 4.0751 +06:37:25 3.9681 +06:37:30 3.9864 +06:37:35 4.0490 +06:37:40 4.0410 +06:37:45 3.9037 +06:37:50 3.9332 +06:37:55 3.9749 +06:38:00 4.0355 +06:38:05 4.0879 +06:38:10 4.0711 +06:38:15 4.0833 +06:38:20 4.0838 +06:38:25 4.1429 +06:38:30 4.1355 +06:38:35 3.9725 +06:38:40 4.0009 +06:38:45 4.0159 +06:38:50 4.0639 +06:38:55 4.0691 +06:39:00 3.9039 +06:39:05 3.9397 +06:39:10 3.9629 +06:39:15 4.0702 +06:39:20 4.0202 +06:39:25 4.0870 +06:39:30 4.0470 +06:39:35 3.9035 +06:39:40 3.9427 +06:39:45 4.0009 +06:39:50 3.9188 +06:39:55 3.9683 +06:40:00 3.9317 +06:40:05 3.8776 +06:40:10 3.9272 +06:40:15 3.9511 +06:40:20 3.9787 +06:40:25 4.0614 +06:40:30 4.0323 +06:40:35 4.0462 +06:40:40 4.0632 +06:40:45 4.0148 +06:40:50 3.9354 +06:40:55 3.8587 +06:41:00 3.9297 +06:41:05 4.0154 +06:41:10 4.0309 +06:41:15 3.9076 +06:41:20 3.8425 +06:41:25 3.9678 +06:41:30 3.9994 +06:41:35 3.9514 +06:41:40 3.8752 +06:41:45 3.9188 +06:41:50 4.0335 +06:41:55 3.9698 +06:42:00 3.8869 +06:42:05 3.9570 +06:42:10 3.9939 +06:42:15 4.0065 +06:42:20 3.9378 +06:42:25 3.9644 +06:42:30 4.1126 +06:42:35 4.1333 +06:42:40 4.1117 +06:42:45 4.0763 +06:42:50 3.9416 +06:42:55 4.1359 +06:43:00 3.9159 +06:43:05 3.9935 +06:43:10 4.0022 +06:43:15 3.9571 +06:43:20 3.9977 +06:43:25 4.1070 +06:43:30 3.9932 +06:43:35 3.9097 +06:43:40 3.9839 +06:43:45 4.0938 +06:43:50 4.0176 +06:43:55 4.0039 +06:44:00 4.0405 +06:44:05 4.0810 +06:44:10 3.9846 +06:44:15 3.9213 +06:44:20 4.0476 +06:44:25 4.0070 +06:44:30 4.0001 +06:44:35 3.9969 +06:44:40 4.0824 +06:44:45 3.9960 +06:44:50 3.9373 +06:44:55 3.9570 +06:45:00 3.9755 +06:45:05 3.9827 +06:45:10 4.0924 +06:45:15 4.0019 +06:45:20 3.9615 +06:45:25 3.8977 +06:45:30 3.9703 +06:45:35 3.9808 +06:45:40 4.0556 +06:45:45 4.0945 +06:45:50 4.0766 +06:45:55 3.9836 +06:46:00 4.0565 +06:46:05 4.0331 +06:46:10 3.9955 +06:46:15 4.0104 +06:46:20 4.0255 +06:46:25 3.9976 +06:46:30 3.9362 +06:46:35 3.9559 +06:46:40 3.9848 +06:46:45 4.0349 +06:46:50 4.1207 +06:46:55 4.0099 +06:47:00 4.0102 +06:47:05 4.1017 +06:47:10 4.0967 +06:47:15 3.9915 +06:47:20 4.0579 +06:47:25 4.0007 +06:47:30 3.9833 +06:47:35 4.0522 +06:47:40 4.0015 +06:47:45 3.9836 +06:47:50 3.9660 +06:47:55 4.0128 +06:48:00 4.0877 +06:48:05 4.0973 +06:48:10 3.9909 +06:48:15 3.9149 +06:48:20 3.8896 +06:48:25 3.9363 +06:48:30 3.9740 +06:48:35 4.0081 +06:48:40 3.9416 +06:48:45 3.9349 +06:48:50 3.9669 +06:48:55 4.0102 +06:49:00 3.9837 +06:49:05 4.0245 +06:49:10 4.0829 +06:49:15 3.9924 +06:49:20 3.9646 +06:49:25 4.0084 +06:49:30 4.0494 +06:49:35 4.1198 +06:49:40 4.1607 +06:49:45 4.1463 +06:49:50 4.0824 +06:49:55 4.0940 +06:50:00 4.0684 +06:50:05 3.9402 +06:50:10 3.9368 +06:50:15 3.9505 +06:50:20 3.9956 +06:50:25 3.9391 +06:50:30 3.9975 +06:50:35 4.0344 +06:50:40 3.9680 +06:50:45 3.9830 +06:50:50 4.0807 +06:50:55 4.0149 +06:51:00 3.9959 +06:51:05 3.9729 +06:51:10 3.9622 +06:51:15 3.9055 +06:51:20 3.9722 +06:51:25 4.0455 +06:51:30 4.0666 +06:51:35 4.0557 +06:51:40 4.1153 +06:51:45 4.1206 +06:51:50 4.0225 +06:51:55 4.1170 +06:52:00 4.0962 +06:52:05 4.0770 +06:52:10 4.0262 +06:52:15 3.9813 +06:52:20 3.9521 +06:52:25 4.0269 +06:52:30 3.9999 +06:52:35 3.9486 +06:52:40 4.0238 +06:52:45 4.0770 +06:52:50 4.1414 +06:52:55 4.0131 +06:53:00 4.0697 +06:53:05 4.1102 +06:53:10 3.9915 +06:53:15 3.9090 +06:53:20 4.0242 +06:53:25 4.0915 +06:53:30 4.0556 +06:53:35 4.0612 +06:53:40 4.0513 +06:53:45 3.9826 +06:53:50 4.0115 +06:53:55 3.9892 +06:54:00 3.9874 +06:54:05 4.0737 +06:54:10 4.0885 +06:54:15 4.0771 +06:54:20 4.0311 +06:54:25 3.9413 +06:54:30 3.9437 +06:54:35 4.0125 +06:54:40 4.0390 +06:54:45 4.0535 +06:54:50 3.9141 +06:54:55 3.9560 +06:55:00 3.9341 +06:55:05 3.8642 +06:55:10 4.0236 +06:55:15 4.1296 +06:55:20 4.0704 +06:55:25 3.9924 +06:55:30 3.9740 +06:55:35 3.9602 +06:55:40 3.9654 +06:55:45 3.9701 +06:55:50 4.0623 +06:55:55 4.0142 +06:56:00 4.0448 +06:56:05 4.0820 +06:56:10 4.0365 +06:56:15 3.9266 +06:56:20 3.9595 +06:56:25 3.9843 +06:56:30 3.9427 +06:56:35 4.0404 +06:56:40 4.0518 +06:56:45 3.9705 +06:56:50 3.9764 +06:56:55 3.9667 +06:57:00 3.9202 +06:57:05 3.9962 +06:57:10 3.9640 +06:57:15 4.0297 +06:57:20 4.0456 +06:57:25 4.0210 +06:57:30 4.0652 +06:57:35 4.0877 +06:57:40 4.0350 +06:57:45 4.0749 +06:57:50 4.0401 +06:57:55 4.0858 +06:58:00 3.8732 +06:58:05 4.1284 +06:58:10 3.9439 +06:58:15 4.1414 +06:58:20 4.1725 +06:58:25 3.8822 +06:58:30 4.1414 +06:58:35 4.0606 +06:58:40 3.8751 +06:58:45 4.1245 +06:58:50 3.9912 +06:58:55 4.0360 +06:59:00 3.8316 +06:59:05 4.0849 +06:59:10 4.0422 +06:59:15 3.9732 +06:59:20 3.9548 +06:59:25 3.9863 +06:59:30 3.9323 +06:59:35 4.1113 +06:59:40 3.9223 +06:59:45 4.0607 +06:59:50 3.9225 +06:59:55 4.0887 +07:00:00 4.0021 +07:00:05 3.8956 +07:00:10 3.8939 +07:00:15 4.0065 +07:00:20 3.9460 +07:00:25 4.0282 +07:00:30 3.9262 +07:00:35 4.0558 +07:00:40 3.8442 +07:00:45 4.1191 +07:00:50 4.0505 +07:00:55 3.8881 +07:01:00 4.2165 +07:01:05 4.4364 +07:01:10 4.2761 +07:01:15 3.9200 +07:01:20 3.7595 +07:01:25 4.0905 +07:01:30 4.2338 +07:01:35 3.8811 +07:01:40 3.7836 +07:01:45 4.1002 +07:01:50 4.2713 +07:01:55 4.3623 +07:02:00 4.1961 +07:02:05 4.0100 +07:02:10 3.9146 +07:02:15 3.9197 +07:02:20 4.1280 +07:02:25 4.2488 +07:02:30 4.2734 +07:02:35 4.1033 +07:02:40 3.8098 +07:02:45 3.8033 +07:02:50 4.0034 +07:02:55 4.1943 +07:03:00 4.2435 +07:03:05 4.0413 +07:03:10 3.7552 +07:03:15 3.6299 +07:03:20 3.8943 +07:03:25 4.1199 +07:03:30 4.2890 +07:03:35 4.2126 +07:03:40 3.9081 +07:03:45 3.7447 +07:03:50 3.9934 +07:03:55 4.2337 +07:04:00 3.9429 +07:04:05 3.9860 +07:04:10 4.2142 +07:04:15 3.9811 +07:04:20 3.9730 +07:04:25 4.0290 +07:04:30 3.8894 +07:04:35 4.1036 +07:04:40 3.8654 +07:04:45 3.9687 +07:04:50 4.0000 +07:04:55 3.9777 +07:05:00 4.1081 +07:05:05 3.8545 +07:05:10 4.0181 +07:05:15 3.9000 +07:05:20 4.0466 +07:05:25 4.1665 +07:05:30 4.0166 +07:05:35 4.0035 +07:05:40 4.0258 +07:05:45 4.0089 +07:05:50 4.0075 +07:05:55 3.9764 +07:06:00 3.9795 +07:06:05 4.0110 +07:06:10 3.9717 +07:06:15 4.0161 +07:06:20 3.9992 +07:06:25 3.9963 +07:06:30 3.8992 +07:06:35 3.9401 +07:06:40 3.8914 +07:06:45 3.8475 +07:06:50 3.9455 +07:06:55 4.0914 +07:07:00 4.0761 +07:07:05 3.9602 +07:07:10 3.9436 +07:07:15 4.0284 +07:07:20 4.0401 +07:07:25 4.0354 +07:07:30 4.0235 +07:07:35 4.0186 +07:07:40 4.1100 +07:07:45 4.0619 +07:07:50 3.9841 +07:07:55 3.9825 +07:08:00 4.0345 +07:08:05 4.0511 +07:08:10 3.9097 +07:08:15 3.9040 +07:08:20 4.0121 +07:08:25 4.0235 +07:08:30 4.1118 +07:08:35 3.9372 +07:08:40 3.9651 +07:08:45 3.9828 +07:08:50 3.9190 +07:08:55 4.0002 +07:09:00 3.9916 +07:09:05 4.0053 +07:09:10 4.0468 +07:09:15 4.0359 +07:09:20 4.0615 +07:09:25 4.1190 +07:09:30 4.0953 +07:09:35 4.0763 +07:09:40 4.0802 +07:09:45 4.0419 +07:09:50 3.9986 +07:09:55 4.0180 +07:10:00 3.9805 +07:10:05 4.0203 +07:10:10 3.9882 +07:10:15 4.0479 +07:10:20 4.0775 +07:10:25 3.9537 +07:10:30 3.9204 +07:10:35 3.9866 +07:10:40 4.0419 +07:10:45 4.0032 +07:10:50 4.0119 +07:10:55 4.0037 +07:11:00 3.9049 +07:11:05 4.0066 +07:11:10 4.0241 +07:11:15 4.0357 +07:11:20 3.9752 +07:11:25 3.9674 +07:11:30 4.0493 +07:11:35 4.0752 +07:11:40 4.0649 +07:11:45 4.1014 +07:11:50 4.0570 +07:11:55 3.9770 +07:12:00 3.8638 +07:12:05 3.9677 +07:12:10 3.9955 +07:12:15 4.0086 +07:12:20 4.0094 +07:12:25 4.0401 +07:12:30 3.9904 +07:12:35 3.9253 +07:12:40 3.9437 +07:12:45 3.9038 +07:12:50 3.9281 +07:12:55 3.9605 +07:13:00 4.0345 +07:13:05 3.9894 +07:13:10 3.9407 +07:13:15 4.0352 +07:13:20 4.0161 +07:13:25 3.9214 +07:13:30 3.9612 +07:13:35 4.0088 +07:13:40 3.9789 +07:13:45 3.8100 +07:13:50 3.9050 +07:13:55 3.9802 +07:14:00 3.9545 +07:14:05 3.9441 +07:14:10 4.0335 +07:14:15 4.0708 +07:14:20 3.9522 +07:14:25 3.9250 +07:14:30 3.9487 +07:14:35 3.9084 +07:14:40 3.9318 +07:14:45 4.0237 +07:14:50 3.9868 +07:14:55 3.9472 +07:15:00 3.9503 +07:15:05 3.9841 +07:15:10 4.0961 +07:15:15 3.9755 +07:15:20 3.9384 +07:15:25 4.0561 +07:15:30 4.0838 +07:15:35 4.0970 +07:15:40 4.0984 +07:15:45 3.9954 +07:15:50 3.9506 +07:15:55 3.9681 +07:16:00 4.0432 +07:16:05 3.9754 +07:16:10 3.8893 +07:16:15 4.0057 +07:16:20 3.9681 +07:16:25 3.9913 +07:16:30 4.0296 +07:16:35 3.9910 +07:16:40 3.9067 +07:16:45 3.9687 +07:16:50 4.0883 +07:16:55 4.0373 +07:17:00 4.0075 +07:17:05 4.0365 +07:17:10 3.9870 +07:17:15 3.9532 +07:17:20 3.9243 +07:17:25 3.9507 +07:17:30 3.9186 +07:17:35 3.9382 +07:17:40 3.9989 +07:17:45 4.0520 +07:17:50 4.0157 +07:17:55 3.9938 +07:18:00 3.9427 +07:18:05 3.9664 +07:18:10 3.9941 +07:18:15 4.0214 +07:18:20 3.9435 +07:18:25 3.9988 +07:18:30 4.0244 +07:18:35 4.0577 +07:18:40 3.9294 +07:18:45 4.0254 +07:18:50 4.0947 +07:18:55 3.9741 +07:19:00 3.9534 +07:19:05 3.9919 +07:19:10 3.9832 +07:19:15 4.0292 +07:19:20 4.1013 +07:19:25 4.1093 +07:19:30 4.1770 +07:19:35 4.1204 +07:19:40 4.1057 +07:19:45 4.0171 +07:19:50 4.0160 +07:19:55 4.0301 +07:20:00 3.9289 +07:20:05 4.0411 +07:20:10 4.0901 +07:20:15 4.0731 +07:20:20 4.0908 +07:20:25 4.0364 +07:20:30 3.9304 +07:20:35 3.9396 +07:20:40 4.0353 +07:20:45 4.0732 +07:20:50 3.9827 +07:20:55 3.9217 +07:21:00 4.0017 +07:21:05 4.0271 +07:21:10 3.9928 +07:21:15 3.9567 +07:21:20 3.9800 +07:21:25 4.0148 +07:21:30 3.8643 +07:21:35 3.8715 +07:21:40 3.9855 +07:21:45 4.0287 +07:21:50 3.9610 +07:21:55 3.9697 +07:22:00 3.9684 +07:22:05 4.0167 +07:22:10 4.0327 +07:22:15 3.9339 +07:22:20 3.9977 +07:22:25 4.0495 +07:22:30 3.9838 +07:22:35 4.0027 +07:22:40 4.0191 +07:22:45 3.9655 +07:22:50 4.0148 +07:22:55 4.0439 +07:23:00 4.0178 +07:23:05 4.0526 +07:23:10 4.1629 +07:23:15 4.0272 +07:23:20 4.0832 +07:23:25 4.0104 +07:23:30 4.0908 +07:23:35 4.0209 +07:23:40 3.9428 +07:23:45 3.9996 +07:23:50 4.0410 +07:23:55 3.9502 +07:24:00 4.0052 +07:24:05 3.9415 +07:24:10 4.0241 +07:24:15 4.0320 +07:24:20 3.9461 +07:24:25 4.0098 +07:24:30 3.9996 +07:24:35 4.0095 +07:24:40 4.0557 +07:24:45 4.0464 +07:24:50 4.1141 +07:24:55 4.0095 +07:25:00 4.0552 +07:25:05 3.9365 +07:25:10 4.0027 +07:25:15 3.9614 +07:25:20 3.8990 +07:25:25 3.9918 +07:25:30 3.9295 +07:25:35 3.9185 +07:25:40 3.8429 +07:25:45 3.9452 +07:25:50 3.9674 +07:25:55 4.0543 +07:26:00 4.0635 +07:26:05 3.9589 +07:26:10 3.9988 +07:26:15 4.0060 +07:26:20 3.9787 +07:26:25 3.9225 +07:26:30 3.9579 +07:26:35 3.9296 +07:26:40 3.9466 +07:26:45 4.0616 +07:26:50 4.0153 +07:26:55 4.0670 +07:27:00 3.9542 +07:27:05 3.9738 +07:27:10 3.9815 +07:27:15 3.9423 +07:27:20 3.9826 +07:27:25 4.0080 +07:27:30 3.9068 +07:27:35 3.9858 +07:27:40 4.0180 +07:27:45 4.0122 +07:27:50 3.9957 +07:27:55 3.9369 +07:28:00 3.9561 +07:28:05 4.0006 +07:28:10 3.9932 +07:28:15 4.0848 +07:28:20 4.0206 +07:28:25 4.1064 +07:28:30 4.0370 +07:28:35 3.9362 +07:28:40 3.9839 +07:28:45 4.1243 +07:28:50 4.0571 +07:28:55 4.0604 +07:29:00 3.9417 +07:29:05 3.9615 +07:29:10 3.9935 +07:29:15 4.1409 +07:29:20 4.0063 +07:29:25 4.0181 +07:29:30 4.0890 +07:29:35 4.0001 +07:29:40 3.8651 +07:29:45 3.9902 +07:29:50 4.0165 +07:29:55 4.0559 +07:30:00 4.0068 +07:30:05 4.0026 +07:30:10 3.9786 +07:30:15 4.0030 +07:30:20 3.9984 +07:30:25 4.0523 +07:30:30 4.0215 +07:30:35 4.0036 +07:30:40 4.0055 +07:30:45 3.9802 +07:30:50 4.0121 +07:30:55 3.9387 +07:31:00 4.0273 +07:31:05 4.0339 +07:31:10 4.1256 +07:31:15 4.0952 +07:31:20 4.0309 +07:31:25 3.9858 +07:31:30 3.9388 +07:31:35 3.9843 +07:31:40 3.9860 +07:31:45 3.9648 +07:31:50 3.9311 +07:31:55 3.9954 +07:32:00 3.9828 +07:32:05 3.9345 +07:32:10 3.9774 +07:32:15 3.9972 +07:32:20 3.9881 +07:32:25 3.9946 +07:32:30 3.9638 +07:32:35 3.9475 +07:32:40 3.9115 +07:32:45 3.9717 +07:32:50 3.9925 +07:32:55 3.9819 +07:33:00 3.9361 +07:33:05 3.9941 +07:33:10 4.1109 +07:33:15 4.0931 +07:33:20 4.0483 +07:33:25 4.0308 +07:33:30 4.0170 +07:33:35 3.9972 +07:33:40 3.9874 +07:33:45 4.0318 +07:33:50 4.0051 +07:33:55 4.0405 +07:34:00 4.0419 +07:34:05 4.0084 +07:34:10 3.9410 +07:34:15 3.9341 +07:34:20 3.9440 +07:34:25 3.9001 +07:34:30 3.9862 +07:34:35 3.9862 +07:34:40 3.9692 +07:34:45 3.9566 +07:34:50 4.0924 +07:34:55 4.0334 +07:35:00 4.0595 +07:35:05 4.0333 +07:35:10 4.0112 +07:35:15 4.0107 +07:35:20 4.0381 +07:35:25 4.0007 +07:35:30 4.0876 +07:35:35 4.1263 +07:35:40 4.0388 +07:35:45 3.9396 +07:35:50 3.9247 +07:35:55 3.9195 +07:36:00 3.9774 +07:36:05 4.0169 +07:36:10 3.9980 +07:36:15 3.9550 +07:36:20 3.9281 +07:36:25 3.9087 +07:36:30 4.0222 +07:36:35 3.9692 +07:36:40 3.9929 +07:36:45 4.0782 +07:36:50 4.0314 +07:36:55 4.0021 +07:37:00 3.9934 +07:37:05 3.9621 +07:37:10 3.9977 +07:37:15 4.0528 +07:37:20 3.9871 +07:37:25 3.9783 +07:37:30 3.9491 +07:37:35 4.0215 +07:37:40 4.0339 +07:37:45 3.9985 +07:37:50 3.9271 +07:37:55 4.0020 +07:38:00 3.9708 +07:38:05 3.9732 +07:38:10 4.0268 +07:38:15 4.0644 +07:38:20 3.9611 +07:38:25 3.9839 +07:38:30 4.0518 +07:38:35 3.9964 +07:38:40 3.9341 +07:38:45 3.9913 +07:38:50 4.0619 +07:38:55 4.0054 +07:39:00 3.9688 +07:39:05 3.9823 +07:39:10 3.9432 +07:39:15 4.0172 +07:39:20 4.0745 +07:39:25 4.0627 +07:39:30 4.0147 +07:39:35 4.0344 +07:39:40 4.1459 +07:39:45 4.0060 +07:39:50 3.9854 +07:39:55 3.9388 +07:40:00 4.0417 +07:40:05 3.9545 +07:40:10 4.0443 +07:40:15 3.9669 +07:40:20 3.9868 +07:40:25 4.0808 +07:40:30 4.0782 +07:40:35 4.0118 +07:40:40 3.8667 +07:40:45 3.8985 +07:40:50 3.9561 +07:40:55 3.9345 +07:41:00 3.9363 +07:41:05 3.9341 +07:41:10 3.9846 +07:41:15 3.9419 +07:41:20 4.0330 +07:41:25 4.0802 +07:41:30 4.0557 +07:41:35 3.9986 +07:41:40 4.0281 +07:41:45 3.9957 +07:41:50 3.9433 +07:41:55 4.0746 +07:42:00 3.9418 +07:42:05 3.9718 +07:42:10 3.9917 +07:42:15 3.9682 +07:42:20 3.9667 +07:42:25 4.0337 +07:42:30 4.0806 +07:42:35 4.0179 +07:42:40 4.0310 +07:42:45 4.0512 +07:42:50 4.0269 +07:42:55 4.0163 +07:43:00 4.0475 +07:43:05 3.9709 +07:43:10 3.9100 +07:43:15 3.9426 +07:43:20 3.9498 +07:43:25 3.9755 +07:43:30 4.0342 +07:43:35 4.0040 +07:43:40 3.9569 +07:43:45 3.9138 +07:43:50 3.9876 +07:43:55 3.8970 +07:44:00 3.9047 +07:44:05 3.9595 +07:44:10 4.1133 +07:44:15 4.1495 +07:44:20 4.0210 +07:44:25 3.9984 +07:44:30 4.0075 +07:44:35 3.9621 +07:44:40 3.9409 +07:44:45 3.9544 +07:44:50 3.9906 +07:44:55 4.0273 +07:45:00 4.0417 +07:45:05 3.9591 +07:45:10 4.0132 +07:45:15 4.0553 +07:45:20 3.9374 +07:45:25 3.8852 +07:45:30 3.9169 +07:45:35 3.9788 +07:45:40 3.9683 +07:45:45 3.9594 +07:45:50 3.9428 +07:45:55 4.0012 +07:46:00 3.9315 +07:46:05 4.0128 +07:46:10 4.0311 +07:46:15 4.0231 +07:46:20 3.9435 +07:46:25 3.9887 +07:46:30 3.9711 +07:46:35 3.9699 +07:46:40 4.0276 +07:46:45 4.0440 +07:46:50 4.0292 +07:46:55 4.0175 +07:47:00 4.0378 +07:47:05 3.9671 +07:47:10 3.9567 +07:47:15 3.9553 +07:47:20 3.9799 +07:47:25 3.9634 +07:47:30 3.9780 +07:47:35 3.9921 +07:47:40 3.9732 +07:47:45 3.9689 +07:47:50 4.0112 +07:47:55 3.9738 +07:48:00 3.9429 +07:48:05 3.9912 +07:48:10 4.0891 +07:48:15 4.1150 +07:48:20 3.9857 +07:48:25 4.0010 +07:48:30 3.9883 +07:48:35 3.9777 +07:48:40 4.0747 +07:48:45 4.0345 +07:48:50 3.9811 +07:48:55 3.9662 +07:49:00 3.9252 +07:49:05 3.8850 +07:49:10 3.9957 +07:49:15 4.0248 +07:49:20 3.9773 +07:49:25 4.1332 +07:49:30 3.9942 +07:49:35 3.9339 +07:49:40 4.0356 +07:49:45 4.0539 +07:49:50 4.0836 +07:49:55 4.0437 +07:50:00 3.9342 +07:50:05 3.9470 +07:50:10 3.9601 +07:50:15 4.0006 +07:50:20 4.0182 +07:50:25 3.9467 +07:50:30 4.0238 +07:50:35 4.0169 +07:50:40 4.0735 +07:50:45 4.0969 +07:50:50 3.9874 +07:50:55 3.9897 +07:51:00 4.0284 +07:51:05 4.0492 +07:51:10 4.0634 +07:51:15 3.9503 +07:51:20 4.0127 +07:51:25 3.9289 +07:51:30 4.0229 +07:51:35 4.0740 +07:51:40 4.0605 +07:51:45 4.0872 +07:51:50 3.9452 +07:51:55 4.0306 +07:52:00 4.0054 +07:52:05 3.9970 +07:52:10 3.9915 +07:52:15 4.0410 +07:52:20 4.0350 +07:52:25 4.0162 +07:52:30 3.9289 +07:52:35 3.8351 +07:52:40 3.9203 +07:52:45 4.0370 +07:52:50 4.0870 +07:52:55 4.0008 +07:53:00 4.0982 +07:53:05 4.0375 +07:53:10 3.9045 +07:53:15 3.9823 +07:53:20 3.9681 +07:53:25 4.0044 +07:53:30 3.9139 +07:53:35 4.0631 +07:53:40 3.9580 +07:53:45 3.9385 +07:53:50 3.9858 +07:53:55 4.0183 +07:54:00 4.0330 +07:54:05 4.0419 +07:54:10 4.0432 +07:54:15 4.0137 +07:54:20 4.0013 +07:54:25 3.9467 +07:54:30 3.9752 +07:54:35 3.8770 +07:54:40 3.9133 +07:54:45 3.9232 +07:54:50 4.0269 +07:54:55 3.9772 +07:55:00 4.0000 +07:55:05 3.9313 +07:55:10 4.0334 +07:55:15 4.0384 +07:55:20 4.0974 +07:55:25 3.8880 +07:55:30 3.8505 +07:55:35 3.9453 +07:55:40 3.9472 +07:55:45 4.0646 +07:55:50 3.9383 +07:55:55 3.9044 +07:56:00 3.8710 +07:56:05 4.0180 +07:56:10 3.9656 +07:56:15 3.9579 +07:56:20 3.8838 +07:56:25 3.9843 +07:56:30 3.9417 +07:56:35 4.0287 +07:56:40 4.1304 +07:56:45 4.1495 +07:56:50 4.0465 +07:56:55 4.0552 +07:57:00 4.0157 +07:57:05 3.9889 +07:57:10 4.0326 +07:57:15 4.0182 +07:57:20 3.9516 +07:57:25 3.9318 +07:57:30 3.9783 +07:57:35 3.9596 +07:57:40 4.0230 +07:57:45 3.9788 +07:57:50 3.9844 +07:57:55 3.9553 +07:58:00 3.9792 +07:58:05 4.0551 +07:58:10 4.0531 +07:58:15 4.0933 +07:58:20 4.0642 +07:58:25 4.1144 +07:58:30 3.9737 +07:58:35 4.0294 +07:58:40 4.1012 +07:58:45 4.0561 +07:58:50 3.9797 +07:58:55 4.0391 +07:59:00 4.0830 +07:59:05 3.9325 +07:59:10 4.0061 +07:59:15 4.0500 +07:59:20 4.0985 +07:59:25 4.0463 +07:59:30 3.9722 +07:59:35 3.9789 +07:59:40 4.0397 +07:59:45 4.0641 +07:59:50 4.0586 +07:59:55 4.0023 +08:00:00 3.8813 +08:00:05 4.0437 +08:00:10 4.0438 +08:00:15 4.0893 +08:00:20 4.1045 +08:00:25 3.9860 +08:00:30 3.9458 +08:00:35 4.0401 +08:00:40 4.0191 +08:00:45 3.9372 +08:00:50 3.9189 +08:00:55 3.8979 +08:01:00 4.0677 +08:01:05 4.0129 +08:01:10 3.9805 +08:01:15 4.0664 +08:01:20 4.0720 +08:01:25 4.0299 +08:01:30 4.0319 +08:01:35 4.0259 +08:01:40 4.0582 +08:01:45 3.9801 +08:01:50 3.9505 +08:01:55 4.0454 +08:02:00 4.0843 +08:02:05 3.9298 +08:02:10 3.8879 +08:02:15 4.0280 +08:02:20 4.0118 +08:02:25 4.0533 +08:02:30 3.9817 +08:02:35 3.9722 +08:02:40 3.9455 +08:02:45 3.9026 +08:02:50 3.8904 +08:02:55 3.9078 +08:03:00 3.9973 +08:03:05 4.0690 +08:03:10 4.0057 +08:03:15 4.0057 +08:03:20 3.9278 +08:03:25 3.9287 +08:03:30 3.9052 +08:03:35 3.9262 +08:03:40 3.9814 +08:03:45 3.9305 +08:03:50 3.9532 +08:03:55 3.9337 +08:04:00 3.8757 +08:04:05 3.9445 +08:04:10 4.0168 +08:04:15 3.9895 +08:04:20 3.9064 +08:04:25 3.8872 +08:04:30 3.9197 +08:04:35 3.9890 +08:04:40 3.9414 +08:04:45 3.9903 +08:04:50 3.9909 +08:04:55 3.9885 +08:05:00 3.9885 +08:05:05 3.9878 +08:05:10 3.9903 +08:05:15 3.9920 +08:05:20 3.9892 +08:05:25 3.9900 +08:05:30 3.9900 +08:05:35 3.9860 +08:05:40 3.9877 +08:05:45 3.9956 +08:05:50 3.9975 +08:05:55 3.9936 +08:06:00 3.9936 +08:06:05 3.9916 +08:06:10 3.9911 +08:06:15 3.9936 +08:06:20 3.9901 +08:06:25 3.9901 +08:06:30 3.9934 +08:06:35 4.0010 +08:06:40 3.9971 +08:06:45 3.9971 +08:06:50 3.9976 +08:06:55 3.9976 +08:07:00 3.9994 +08:07:05 4.0004 +08:07:10 4.0061 +08:07:15 4.0107 +08:07:20 4.0111 +08:07:25 4.0111 +08:07:30 4.0168 +08:07:35 4.0111 +08:07:40 4.0101 +08:07:45 4.0144 +08:07:50 4.0209 +08:07:55 4.0209 +08:08:00 4.0272 +08:08:05 4.0423 +08:08:10 4.0500 +08:08:15 4.0545 +08:08:20 4.0647 +08:08:25 4.0647 +08:08:30 4.0650 +08:08:35 4.0661 +08:08:40 4.0722 +08:08:45 4.0664 +08:08:50 4.0706 +08:08:55 4.0688 +08:09:00 4.0688 +08:09:05 4.0661 +08:09:10 4.0713 +08:09:15 4.0666 +08:09:20 3.9059 +08:09:25 3.9409 +08:09:30 3.9834 +08:09:35 3.9559 +08:09:40 3.8940 +08:09:45 3.8839 +08:09:50 3.8736 +08:09:55 3.9604 +08:10:00 4.0262 +08:10:05 4.0232 +08:10:10 3.9741 +08:10:15 3.9944 +08:10:20 3.9905 +08:10:25 3.8958 +08:10:30 3.8878 +08:10:35 4.0318 +08:10:40 3.9985 +08:10:45 3.9562 +08:10:50 4.0179 +08:10:55 3.9810 +08:11:00 3.9610 +08:11:05 4.0197 +08:11:10 4.0233 +08:11:15 4.0069 +08:11:20 4.0248 +08:11:25 3.9454 +08:11:30 4.0151 +08:11:35 4.0178 +08:11:40 4.0001 +08:11:45 4.0719 +08:11:50 4.0412 +08:11:55 4.0093 +08:12:00 3.9623 +08:12:05 3.9329 +08:12:10 3.9418 +08:12:15 3.9776 +08:12:20 3.9534 +08:12:25 3.9433 +08:12:30 4.0519 +08:12:35 4.0630 +08:12:40 4.0486 +08:12:45 4.0100 +08:12:50 3.9226 +08:12:55 3.9947 +08:13:00 3.9913 +08:13:05 3.9335 +08:13:10 3.9834 +08:13:15 4.0252 +08:13:20 4.0229 +08:13:25 4.0330 +08:13:30 3.9917 +08:13:35 3.9063 +08:13:40 3.9863 +08:13:45 3.9766 +08:13:50 4.0003 +08:13:55 4.0513 +08:14:00 4.0584 +08:14:05 3.9683 +08:14:10 4.0158 +08:14:15 4.0569 +08:14:20 3.8917 +08:14:25 3.9474 +08:14:30 4.0335 +08:14:35 3.9504 +08:14:40 3.9493 +08:14:45 3.9673 +08:14:50 3.9485 +08:14:55 3.9409 +08:15:00 4.0484 +08:15:05 3.9996 +08:15:10 3.9073 +08:15:15 3.9972 +08:15:20 4.0568 +08:15:25 3.9789 +08:15:30 4.0165 +08:15:35 4.0831 +08:15:40 4.0674 +08:15:45 4.0077 +08:15:50 4.0914 +08:15:55 4.0109 +08:16:00 3.9813 +08:16:05 3.9983 +08:16:10 4.0353 +08:16:15 3.9615 +08:16:20 3.9998 +08:16:25 4.0267 +08:16:30 4.0030 +08:16:35 4.0033 +08:16:40 3.9525 +08:16:45 4.0112 +08:16:50 4.0112 +08:16:55 4.0565 +08:17:00 4.0473 +08:17:05 4.0309 +08:17:10 3.9956 +08:17:15 3.9218 +08:17:20 3.9845 +08:17:25 4.0239 +08:17:30 4.0454 +08:17:35 4.0000 +08:17:40 4.0090 +08:17:45 3.9324 +08:17:50 3.8911 +08:17:55 3.9708 +08:18:00 3.9704 +08:18:05 3.9098 +08:18:10 4.0112 +08:18:15 4.0170 +08:18:20 3.9810 +08:18:25 3.9344 +08:18:30 4.0801 +08:18:35 3.9548 +08:18:40 3.9841 +08:18:45 4.0611 +08:18:50 3.9899 +08:18:55 4.0117 +08:19:00 4.1174 +08:19:05 4.0336 +08:19:10 3.9787 +08:19:15 4.0188 +08:19:20 3.9898 +08:19:25 3.8395 +08:19:30 3.9379 +08:19:35 3.9463 +08:19:40 3.9283 +08:19:45 3.9650 +08:19:50 3.9734 +08:19:55 4.0571 +08:20:00 3.9920 +08:20:05 4.0088 +08:20:10 4.0406 +08:20:15 4.0886 +08:20:20 3.9662 +08:20:25 4.0271 +08:20:30 4.0010 +08:20:35 4.0220 +08:20:40 3.9988 +08:20:45 4.0391 +08:20:50 4.0271 +08:20:55 3.9926 +08:21:00 3.9645 +08:21:05 3.9987 +08:21:10 4.0486 +08:21:15 3.9711 +08:21:20 4.0100 +08:21:25 3.9903 +08:21:30 4.0626 +08:21:35 3.9436 +08:21:40 4.0508 +08:21:45 4.0194 +08:21:50 3.9379 +08:21:55 3.9143 +08:22:00 3.9572 +08:22:05 3.8625 +08:22:10 4.0040 +08:22:15 3.9890 +08:22:20 4.0783 +08:22:25 4.0251 +08:22:30 4.1519 +08:22:35 4.0467 +08:22:40 4.0457 +08:22:45 4.0522 +08:22:50 3.9937 +08:22:55 3.9889 +08:23:00 3.9866 +08:23:05 4.1136 +08:23:10 4.0970 +08:23:15 3.9813 +08:23:20 3.9880 +08:23:25 4.0045 +08:23:30 3.8291 +08:23:35 3.9054 +08:23:40 3.9824 +08:23:45 4.0406 +08:23:50 4.0859 +08:23:55 4.1527 +08:24:00 4.0190 +08:24:05 3.9886 +08:24:10 3.9411 +08:24:15 3.9661 +08:24:20 4.0354 +08:24:25 3.9175 +08:24:30 3.9984 +08:24:35 4.0452 +08:24:40 4.0369 +08:24:45 4.0266 +08:24:50 3.9061 +08:24:55 3.9084 +08:25:00 3.9583 +08:25:05 3.9560 +08:25:10 3.9405 +08:25:15 3.9877 +08:25:20 4.0423 +08:25:25 3.9790 +08:25:30 4.0421 +08:25:35 3.9568 +08:25:40 3.9529 +08:25:45 3.9750 +08:25:50 3.9340 +08:25:55 4.0732 +08:26:00 4.0032 +08:26:05 3.9355 +08:26:10 4.0352 +08:26:15 4.0157 +08:26:20 3.9340 +08:26:25 4.0755 +08:26:30 3.9820 +08:26:35 3.9605 +08:26:40 3.9558 +08:26:45 3.9521 +08:26:50 4.0223 +08:26:55 3.9900 +08:27:00 3.9845 +08:27:05 4.0780 +08:27:10 3.9558 +08:27:15 3.9726 +08:27:20 4.0192 +08:27:25 3.9686 +08:27:30 3.9911 +08:27:35 3.9766 +08:27:40 4.0285 +08:27:45 4.0070 +08:27:50 4.0941 +08:27:55 4.1368 +08:28:00 4.0655 +08:28:05 4.0341 +08:28:10 4.0018 +08:28:15 3.9572 +08:28:20 3.9705 +08:28:25 4.0494 +08:28:30 4.1193 +08:28:35 4.0454 +08:28:40 4.0497 +08:28:45 3.9941 +08:28:50 3.9807 +08:28:55 3.9538 +08:29:00 4.0346 +08:29:05 3.9442 +08:29:10 3.8731 +08:29:15 3.9171 +08:29:20 3.9704 +08:29:25 3.9611 +08:29:30 3.9287 +08:29:35 3.9558 +08:29:40 3.9734 +08:29:45 3.9336 +08:29:50 3.9309 +08:29:55 3.9706 +08:30:00 3.9000 +08:30:05 3.9439 +08:30:10 3.9667 +08:30:15 4.0107 +08:30:20 4.0336 +08:30:25 3.9975 +08:30:30 4.0185 +08:30:35 4.0235 +08:30:40 4.0291 +08:30:45 4.0685 +08:30:50 4.0343 +08:30:55 4.0733 +08:31:00 4.0495 +08:31:05 3.9309 +08:31:10 3.8886 +08:31:15 3.8842 +08:31:20 3.8765 +08:31:25 3.9649 +08:31:30 4.0185 +08:31:35 3.9216 +08:31:40 3.9444 +08:31:45 4.0470 +08:31:50 4.0551 +08:31:55 3.9941 +08:32:00 3.9735 +08:32:05 4.0055 +08:32:10 4.0072 +08:32:15 3.9674 +08:32:20 3.9799 +08:32:25 3.9734 +08:32:30 4.0448 +08:32:35 3.9855 +08:32:40 3.9453 +08:32:45 3.9345 +08:32:50 3.9903 +08:32:55 3.9872 +08:33:00 3.8902 +08:33:05 3.9411 +08:33:10 3.9458 +08:33:15 3.9231 +08:33:20 3.9494 +08:33:25 3.9503 +08:33:30 3.8982 +08:33:35 4.0473 +08:33:40 3.9778 +08:33:45 3.9569 +08:33:50 3.9582 +08:33:55 3.9874 +08:34:00 3.9983 +08:34:05 4.0212 +08:34:10 3.9492 +08:34:15 3.9464 +08:34:20 3.9542 +08:34:25 3.9336 +08:34:30 3.9511 +08:34:35 3.8728 +08:34:40 3.8914 +08:34:45 4.0097 +08:34:50 4.0167 +08:34:55 3.9419 +08:35:00 3.9504 +08:35:05 3.9485 +08:35:10 4.0029 +08:35:15 4.0663 +08:35:20 3.9734 +08:35:25 3.9258 +08:35:30 3.9680 +08:35:35 3.9818 +08:35:40 3.9747 +08:35:45 3.9243 +08:35:50 3.9350 +08:35:55 4.0503 +08:36:00 4.0678 +08:36:05 4.0310 +08:36:10 3.9243 +08:36:15 3.9879 +08:36:20 4.0485 +08:36:25 4.0174 +08:36:30 3.8832 +08:36:35 3.8986 +08:36:40 3.9783 +08:36:45 3.8902 +08:36:50 3.8330 +08:36:55 3.9425 +08:37:00 4.0228 +08:37:05 3.9824 +08:37:10 4.0141 +08:37:15 4.0773 +08:37:20 4.0423 +08:37:25 3.9658 +08:37:30 3.9688 +08:37:35 4.0131 +08:37:40 3.9095 +08:37:45 3.9185 +08:37:50 3.9448 +08:37:55 3.9790 +08:38:00 3.9711 +08:38:05 4.0244 +08:38:10 3.9948 +08:38:15 3.9369 +08:38:20 3.9243 +08:38:25 3.9489 +08:38:30 4.0292 +08:38:35 3.9508 +08:38:40 3.9296 +08:38:45 3.9449 +08:38:50 4.0867 +08:38:55 4.0995 +08:39:00 4.0276 +08:39:05 3.9588 +08:39:10 3.9257 +08:39:15 3.8554 +08:39:20 3.9168 +08:39:25 3.9684 +08:39:30 4.1025 +08:39:35 4.0942 +08:39:40 4.1029 +08:39:45 4.0091 +08:39:50 3.9430 +08:39:55 3.9655 +08:40:00 3.9637 +08:40:05 3.9163 +08:40:10 3.8848 +08:40:15 3.9872 +08:40:20 3.9584 +08:40:25 3.8781 +08:40:30 3.9980 +08:40:35 3.9627 +08:40:40 3.9602 +08:40:45 3.9543 +08:40:50 3.9331 +08:40:55 3.9159 +08:41:00 3.9642 +08:41:05 3.9644 +08:41:10 3.9690 +08:41:15 3.9787 +08:41:20 4.0381 +08:41:25 4.0643 +08:41:30 3.9557 +08:41:35 3.9533 +08:41:40 3.9828 +08:41:45 3.9858 +08:41:50 4.0183 +08:41:55 3.9128 +08:42:00 3.8540 +08:42:05 3.9747 +08:42:10 4.0202 +08:42:15 4.0068 +08:42:20 4.0302 +08:42:25 3.9536 +08:42:30 3.9500 +08:42:35 3.9892 +08:42:40 3.8927 +08:42:45 3.9421 +08:42:50 4.0618 +08:42:55 4.1163 +08:43:00 4.0682 +08:43:05 4.1158 +08:43:10 4.0106 +08:43:15 3.9641 +08:43:20 4.0354 +08:43:25 3.9817 +08:43:30 3.9486 +08:43:35 4.0596 +08:43:40 3.9953 +08:43:45 3.9774 +08:43:50 4.0091 +08:43:55 4.0125 +08:44:00 4.0138 +08:44:05 3.9728 +08:44:10 3.9039 +08:44:15 3.9659 +08:44:20 4.0629 +08:44:25 3.9732 +08:44:30 3.9012 +08:44:35 3.9800 +08:44:40 4.0467 +08:44:45 3.9895 +08:44:50 3.9373 +08:44:55 3.9511 +08:45:00 4.0204 +08:45:05 3.9427 +08:45:10 4.0281 +08:45:15 4.0489 +08:45:20 3.9987 +08:45:25 3.9167 +08:45:30 3.9390 +08:45:35 3.9641 +08:45:40 3.8772 +08:45:45 3.9964 +08:45:50 4.0169 +08:45:55 3.9931 +08:46:00 4.0114 +08:46:05 4.0286 +08:46:10 4.0444 +08:46:15 3.9846 +08:46:20 3.9911 +08:46:25 4.0030 +08:46:30 4.0082 +08:46:35 4.0295 +08:46:40 3.9617 +08:46:45 3.8579 +08:46:50 4.0388 +08:46:55 4.0432 +08:47:00 4.0729 +08:47:05 3.9685 +08:47:10 3.9426 +08:47:15 3.9606 +08:47:20 4.0833 +08:47:25 4.1376 +08:47:30 4.0061 +08:47:35 4.0050 +08:47:40 3.9474 +08:47:45 3.9360 +08:47:50 4.0148 +08:47:55 3.9876 +08:48:00 3.9914 +08:48:05 3.9491 +08:48:10 4.0126 +08:48:15 4.0167 +08:48:20 3.9897 +08:48:25 3.9792 +08:48:30 3.9671 +08:48:35 3.8923 +08:48:40 3.8938 +08:48:45 3.9834 +08:48:50 3.9586 +08:48:55 3.9602 +08:49:00 4.0370 +08:49:05 4.0033 +08:49:10 4.0371 +08:49:15 4.0473 +08:49:20 3.9490 +08:49:25 4.0169 +08:49:30 4.0128 +08:49:35 4.0164 +08:49:40 3.9989 +08:49:45 4.0173 +08:49:50 4.0526 +08:49:55 4.0508 +08:50:00 4.0035 +08:50:05 4.0408 +08:50:10 4.0266 +08:50:15 3.9400 +08:50:20 3.9809 +08:50:25 3.9025 +08:50:31 3.9583 +08:50:35 3.9959 +08:50:40 3.9316 +08:50:45 4.0148 +08:50:50 4.1073 +08:50:55 4.1205 +08:51:00 4.0011 +08:51:05 3.9443 +08:51:10 3.9353 +08:51:15 3.9095 +08:51:20 3.9325 +08:51:25 3.9605 +08:51:30 3.9631 +08:51:35 4.0037 +08:51:40 3.9625 +08:51:45 4.0819 +08:51:50 3.9921 +08:51:55 3.9810 +08:52:00 3.9810 +08:52:05 3.9726 +08:52:10 3.9964 +08:52:15 3.9987 +08:52:20 3.9812 +08:52:25 3.9676 +08:52:30 3.9660 +08:52:35 3.9372 +08:52:40 4.0171 +08:52:45 3.9645 +08:52:50 3.9657 +08:52:55 3.9790 +08:53:00 3.9521 +08:53:05 3.9928 +08:53:10 4.0833 +08:53:15 4.0450 +08:53:20 3.9958 +08:53:25 3.9552 +08:53:30 3.8727 +08:53:35 3.9622 +08:53:40 3.8636 +08:53:45 3.9463 +08:53:50 3.9404 +08:53:55 3.9863 +08:54:00 3.9678 +08:54:05 4.0179 +08:54:10 4.0234 +08:54:15 3.9915 +08:54:20 3.9294 +08:54:25 3.9806 +08:54:30 4.0041 +08:54:35 4.0001 +08:54:40 3.9512 +08:54:45 3.9294 +08:54:50 3.9819 +08:54:55 4.0536 +08:55:00 4.0273 +08:55:05 4.0399 +08:55:10 4.0048 +08:55:15 4.0371 +08:55:20 4.0576 +08:55:25 4.0198 +08:55:30 3.8868 +08:55:35 3.8492 +08:55:40 3.9570 +08:55:45 4.0255 +08:55:50 3.9984 +08:55:55 3.9594 +08:56:00 3.9647 +08:56:05 4.0360 +08:56:10 3.9813 +08:56:15 3.9297 +08:56:20 3.9753 +08:56:25 4.0072 +08:56:30 4.0432 +08:56:35 4.0500 +08:56:40 3.9434 +08:56:45 3.9787 +08:56:50 3.9634 +08:56:55 3.9082 +08:57:00 3.7568 +08:57:05 3.7184 +08:57:10 3.8546 +08:57:15 3.9252 +08:57:20 3.9070 +08:57:25 3.9095 +08:57:30 3.9184 +08:57:35 3.9696 +08:57:40 4.0113 +08:57:45 3.9432 +08:57:50 3.9121 +08:57:55 4.0103 +08:58:00 3.9635 +08:58:05 3.9885 +08:58:10 3.9599 +08:58:15 3.9407 +08:58:20 3.9283 +08:58:25 3.9582 +08:58:30 4.0212 +08:58:35 4.0127 +08:58:40 4.0150 +08:58:45 4.0532 +08:58:50 4.0701 +08:58:55 3.9462 +08:59:00 3.9007 +08:59:05 3.9557 +08:59:10 3.9085 +08:59:15 3.9706 +08:59:20 4.0174 +08:59:25 4.0459 +08:59:30 3.9561 +08:59:35 4.0658 +08:59:40 4.1289 +08:59:45 3.9320 +08:59:50 3.8703 +08:59:55 3.9320 +09:00:00 3.9026 +09:00:05 3.9094 +09:00:10 3.9181 +09:00:15 3.9281 +09:00:20 4.0082 +09:00:25 3.9618 +09:00:30 4.1944 +09:00:35 3.8815 +09:00:40 4.0046 +09:00:45 3.9718 +09:00:50 3.8848 +09:00:55 4.0679 +09:01:00 3.8829 +09:01:05 4.0181 +09:01:10 3.8955 +09:01:15 4.1476 +09:01:20 4.0425 +09:01:25 3.8633 +09:01:30 4.2034 +09:01:35 3.9111 +09:01:40 3.9196 +09:01:45 4.1620 +09:01:50 3.9057 +09:01:55 4.0862 +09:02:00 3.8982 +09:02:05 3.9585 +09:02:10 4.0054 +09:02:15 4.0036 +09:02:20 3.9482 +09:02:25 3.9609 +09:02:30 4.0482 +09:02:35 3.9767 +09:02:40 4.0038 +09:02:45 3.9352 +09:02:50 3.9016 +09:02:55 3.9504 +09:03:00 3.9335 +09:03:05 3.9099 +09:03:10 3.9586 +09:03:15 3.9906 +09:03:20 3.9705 +09:03:25 3.9033 +09:03:30 3.9194 +09:03:35 3.9398 +09:03:40 4.0247 +09:03:45 3.9545 +09:03:50 3.9992 +09:03:55 3.9596 +09:04:00 3.9864 +09:04:05 3.9992 +09:04:10 4.0276 +09:04:15 4.0089 +09:04:20 3.9199 +09:04:25 3.9587 +09:04:30 4.0258 +09:04:35 3.9671 +09:04:40 3.9195 +09:04:45 3.9699 +09:04:50 4.0437 +09:04:55 4.1013 +09:05:00 4.0913 +09:05:05 3.9621 +09:05:10 3.9603 +09:05:15 4.0663 +09:05:20 4.1009 +09:05:25 4.0497 +09:05:30 4.0423 +09:05:35 4.0485 +09:05:40 4.0168 +09:05:45 3.9509 +09:05:50 3.9718 +09:05:55 3.9458 +09:06:00 3.9538 +09:06:05 3.9652 +09:06:10 4.0019 +09:06:15 3.9861 +09:06:20 4.0160 +09:06:25 4.0185 +09:06:30 3.9952 +09:06:35 4.0694 +09:06:40 3.9237 +09:06:45 3.9054 +09:06:50 3.9866 +09:06:55 3.9762 +09:07:00 3.8969 +09:07:05 3.9303 +09:07:10 3.9698 +09:07:15 3.9757 +09:07:20 3.9898 +09:07:25 3.9540 +09:07:30 3.9883 +09:07:35 4.0885 +09:07:40 4.0357 +09:07:45 4.0111 +09:07:50 3.9162 +09:07:55 3.9756 +09:08:00 4.0568 +09:08:05 4.0227 +09:08:10 4.0309 +09:08:15 3.9628 +09:08:20 3.9529 +09:08:25 3.9398 +09:08:30 3.8903 +09:08:35 3.8768 +09:08:40 3.9322 +09:08:45 3.9810 +09:08:50 3.9501 +09:08:55 3.9341 +09:09:00 4.0024 +09:09:05 4.0412 +09:09:10 4.0311 +09:09:15 3.9860 +09:09:20 3.9736 +09:09:25 3.9748 +09:09:30 4.0204 +09:09:35 3.9503 +09:09:40 3.9509 +09:09:45 3.9139 +09:09:50 3.9201 +09:09:55 3.9490 +09:10:00 4.0020 +09:10:05 3.9734 +09:10:10 3.9946 +09:10:15 4.0287 +09:10:20 4.0056 +09:10:25 3.9963 +09:10:30 3.9700 +09:10:35 3.9943 +09:10:40 3.8822 +09:10:45 3.9416 +09:10:50 4.0093 +09:10:55 4.0461 +09:11:00 4.0392 +09:11:05 3.9744 +09:11:10 3.9849 +09:11:15 3.9157 +09:11:20 4.0314 +09:11:25 4.0579 +09:11:30 4.0791 +09:11:35 4.0032 +09:11:40 3.9909 +09:11:45 4.0130 +09:11:50 3.9939 +09:11:55 3.9481 +09:12:00 3.9951 +09:12:05 4.0153 +09:12:10 3.9742 +09:12:15 3.9734 +09:12:20 3.8981 +09:12:25 3.8917 +09:12:30 4.0098 +09:12:35 3.9335 +09:12:40 3.9522 +09:12:45 3.9576 +09:12:50 3.9824 +09:12:55 3.8814 +09:13:00 3.8504 +09:13:05 3.9867 +09:13:10 3.9064 +09:13:15 3.8306 +09:13:20 3.8611 +09:13:25 3.9154 +09:13:30 3.8721 +09:13:35 3.8510 +09:13:40 3.9872 +09:13:45 4.0319 +09:13:50 3.8819 +09:13:55 4.0158 +09:14:00 3.9158 +09:14:05 3.9536 +09:14:10 4.0790 +09:14:15 4.0152 +09:14:20 4.0560 +09:14:25 3.9705 +09:14:30 4.0517 +09:14:35 4.0736 +09:14:40 4.0872 +09:14:45 4.1996 +09:14:50 4.0228 +09:14:55 4.0313 +09:15:00 4.0599 +09:15:05 4.0157 +09:15:10 3.9526 +09:15:15 3.9166 +09:15:20 3.8356 +09:15:25 3.8660 +09:15:30 3.8097 +09:15:35 3.9104 +09:15:40 3.9859 +09:15:45 3.9674 +09:15:50 3.9716 +09:15:55 4.1186 +09:16:00 4.0244 +09:16:05 3.9607 +09:16:10 3.8917 +09:16:15 3.9409 +09:16:20 3.9663 +09:16:25 3.9814 +09:16:30 3.9684 +09:16:35 3.9023 +09:16:40 4.0013 +09:16:45 3.9684 +09:16:50 3.9354 +09:16:55 3.9427 +09:17:00 3.9185 +09:17:05 3.9001 +09:17:10 3.8790 +09:17:15 3.8939 +09:17:20 3.9965 +09:17:25 3.9104 +09:17:30 3.9415 +09:17:35 3.9409 +09:17:40 3.9442 +09:17:45 3.9813 +09:17:50 4.0043 +09:17:55 4.0353 +09:18:00 3.9322 +09:18:05 3.9964 +09:18:10 3.9802 +09:18:15 3.8984 +09:18:20 3.9210 +09:18:25 3.9290 +09:18:30 4.0317 +09:18:35 4.0445 +09:18:40 4.0734 +09:18:45 3.9932 +09:18:50 3.8709 +09:18:55 3.9434 +09:19:00 3.8759 +09:19:05 3.9505 +09:19:10 3.9768 +09:19:15 3.9329 +09:19:20 3.9821 +09:19:25 3.9729 +09:19:30 4.0061 +09:19:35 4.0010 +09:19:40 3.9859 +09:19:45 3.9958 +09:19:50 3.9214 +09:19:55 3.9344 +09:20:00 4.0097 +09:20:05 3.9930 +09:20:10 3.9946 +09:20:15 3.9889 +09:20:20 3.9376 +09:20:25 3.9850 +09:20:30 4.0629 +09:20:35 4.0633 +09:20:40 3.9687 +09:20:45 3.9667 +09:20:50 3.9618 +09:20:55 3.9653 +09:21:00 3.9441 +09:21:05 3.8934 +09:21:10 3.8937 +09:21:15 3.9045 +09:21:20 3.9774 +09:21:25 4.0146 +09:21:30 3.9514 +09:21:35 3.9383 +09:21:40 3.8049 +09:21:45 3.8884 +09:21:50 3.9176 +09:21:55 3.9486 +09:22:00 3.9925 +09:22:05 3.8731 +09:22:10 3.8810 +09:22:15 3.9566 +09:22:20 3.9881 +09:22:25 3.9633 +09:22:30 4.0458 +09:22:35 3.9979 +09:22:40 4.0002 +09:22:45 3.9824 +09:22:50 4.0000 +09:22:55 3.9607 +09:23:00 3.9936 +09:23:05 4.0417 +09:23:10 4.0602 +09:23:15 4.0866 +09:23:20 4.0276 +09:23:25 4.0074 +09:23:30 3.9939 +09:23:35 3.8641 +09:23:40 3.8143 +09:23:45 3.8374 +09:23:50 3.9005 +09:23:55 3.9436 +09:24:00 4.0564 +09:24:05 3.9793 +09:24:10 3.9287 +09:24:15 3.9742 +09:24:20 4.0483 +09:24:25 3.9959 +09:24:30 3.8512 +09:24:35 3.9290 +09:24:40 3.9476 +09:24:45 3.9360 +09:24:50 4.0257 +09:24:55 3.9716 +09:25:00 3.9111 +09:25:05 3.8669 +09:25:10 3.9086 +09:25:15 4.0175 +09:25:20 4.0335 +09:25:25 3.9244 +09:25:30 3.9637 +09:25:35 3.9755 +09:25:40 3.8314 +09:25:45 3.9610 +09:25:50 3.9638 +09:25:55 3.9125 +09:26:00 4.0177 +09:26:05 4.0074 +09:26:10 4.0639 +09:26:15 3.9969 +09:26:20 3.9355 +09:26:25 3.9460 +09:26:30 4.0225 +09:26:35 3.9444 +09:26:40 3.9885 +09:26:45 3.9520 +09:26:50 3.9252 +09:26:55 3.8318 +09:27:00 3.9500 +09:27:05 3.9884 +09:27:10 3.9043 +09:27:15 3.9582 +09:27:20 3.9076 +09:27:25 3.9684 +09:27:30 4.0209 +09:27:35 4.0364 +09:27:40 3.9894 +09:27:45 3.9853 +09:27:50 4.0116 +09:27:55 4.0085 +09:28:00 4.0179 +09:28:05 3.9244 +09:28:10 3.9808 +09:28:15 3.8864 +09:28:20 3.9076 +09:28:25 4.0409 +09:28:30 3.9916 +09:28:35 4.0585 +09:28:40 4.0840 +09:28:45 4.0490 +09:28:50 3.9851 +09:28:55 3.9885 +09:29:00 4.1232 +09:29:05 4.0850 +09:29:10 4.0292 +09:29:15 3.9309 +09:29:20 3.9597 +09:29:25 3.9522 +09:29:30 3.9379 +09:29:35 4.0095 +09:29:40 4.0190 +09:29:45 4.0249 +09:29:50 4.0412 +09:29:55 3.9565 +09:30:00 4.0236 +09:30:05 4.0018 +09:30:10 3.9787 +09:30:15 3.9928 +09:30:20 3.9974 +09:30:25 3.9451 +09:30:30 4.0004 +09:30:35 3.9485 +09:30:40 3.9685 +09:30:45 4.0494 +09:30:50 3.8650 +09:30:55 3.9053 +09:31:00 4.0077 +09:31:05 3.9653 +09:31:10 3.9742 +09:31:15 4.0010 +09:31:20 3.8874 +09:31:25 3.8892 +09:31:30 3.9428 +09:31:35 3.9144 +09:31:40 3.9430 +09:31:45 3.8940 +09:31:50 3.8977 +09:31:55 3.9750 +09:32:00 4.0103 +09:32:05 4.0475 +09:32:10 4.0435 +09:32:15 4.0587 +09:32:20 3.9976 +09:32:25 4.0013 +09:32:30 4.0503 +09:32:35 4.0356 +09:32:40 3.9835 +09:32:45 3.9023 +09:32:50 3.9678 +09:32:55 3.9503 +09:33:02 3.9728 +09:33:05 3.9769 +09:33:10 3.9422 +09:33:15 3.9545 +09:33:21 3.9696 +09:33:25 3.8915 +09:33:31 3.9039 +09:33:35 3.9445 +09:33:40 4.0206 +09:33:45 3.9472 +09:33:52 3.9385 +09:33:55 3.8471 +09:34:00 3.9455 +09:34:05 4.0128 +09:34:10 3.9719 +09:34:15 3.9362 +09:34:20 3.8932 +09:34:25 4.0424 +09:34:30 4.0974 +09:34:35 3.9899 +09:34:40 4.0433 +09:34:45 4.0237 +09:34:50 3.9966 +09:34:55 3.8746 +09:35:00 3.8128 +09:35:05 3.9183 +09:35:10 3.9684 +09:35:15 4.0538 +09:35:20 4.1515 +09:35:25 4.1264 +09:35:30 4.0369 +09:35:35 4.0213 +09:35:40 4.0043 +09:35:45 4.0158 +09:35:50 3.8874 +09:35:55 3.8374 +09:36:00 3.8926 +09:36:07 3.9555 +09:36:10 3.9158 +09:36:15 3.8723 +09:36:20 3.8382 +09:36:25 3.8439 +09:36:30 3.9041 +09:36:35 3.9704 +09:36:40 3.9511 +09:36:45 3.9337 +09:36:50 4.0467 +09:36:55 4.0334 +09:37:00 3.9430 +09:37:05 3.8771 +09:37:10 3.9500 +09:37:15 3.9846 +09:37:20 3.9649 +09:37:25 3.9022 +09:37:30 3.8949 +09:37:35 3.9527 +09:37:40 3.9218 +09:37:45 3.9184 +09:37:50 3.9804 +09:37:55 3.9053 +09:38:00 3.9329 +09:38:05 3.9465 +09:38:10 3.9204 +09:38:15 3.8679 +09:38:20 3.9099 +09:38:25 3.9964 +09:38:30 3.9820 +09:38:35 4.0495 +09:38:40 4.0729 +09:38:45 4.0411 +09:38:50 4.0335 +09:38:55 4.0293 +09:39:00 4.0594 +09:39:05 3.9820 +09:39:10 3.9536 +09:39:15 3.8824 +09:39:20 3.8979 +09:39:25 3.9119 +09:39:30 3.9845 +09:39:35 4.0669 +09:39:40 4.0154 +09:39:45 3.8817 +09:39:50 3.9392 +09:39:55 3.9675 +09:40:00 3.9735 +09:40:05 3.9735 +09:40:10 3.9781 +09:40:15 3.9865 +09:40:20 3.9901 +09:40:25 3.9914 +09:40:30 3.9495 +09:40:35 3.9514 +09:40:40 3.9518 +09:40:45 3.9480 +09:40:50 3.9451 +09:40:55 3.9441 +09:41:00 3.9600 +09:41:05 4.0559 +09:41:10 4.0028 +09:41:15 4.0028 +09:41:20 3.9576 +09:41:25 3.8866 +09:41:30 3.9163 +09:41:35 3.9252 +09:41:40 3.9763 +09:41:45 4.0747 +09:41:50 4.0355 +09:41:55 4.0340 +09:42:00 4.0756 +09:42:05 4.0985 +09:42:10 4.0372 +09:42:15 3.9845 +09:42:20 4.0437 +09:42:25 3.9746 +09:42:30 4.0273 +09:42:35 3.8792 +09:42:40 3.9676 +09:42:45 4.0374 +09:42:50 4.1033 +09:42:55 4.1044 +09:43:00 4.1182 +09:43:05 4.0746 +09:43:10 4.1441 +09:43:15 4.0915 +09:43:20 4.0743 +09:43:25 4.1226 +09:43:30 4.0927 +09:43:35 4.0978 +09:43:40 4.0538 +09:43:45 4.0307 +09:43:50 3.9976 +09:43:55 3.9155 +09:44:00 4.0192 +09:44:05 4.0911 +09:44:10 4.1070 +09:44:15 4.0718 +09:44:20 4.0149 +09:44:25 4.0042 +09:44:30 3.9882 +09:44:35 4.0548 +09:44:40 4.0148 +09:44:45 3.9106 +09:44:50 4.0067 +09:44:55 4.0710 +09:45:00 3.9924 +09:45:05 4.0026 +09:45:10 3.9525 +09:45:15 4.0169 +09:45:20 3.9477 +09:45:25 3.9071 +09:45:30 3.9583 +09:45:35 3.9589 +09:45:40 3.9991 +09:45:45 4.0727 +09:45:50 4.0091 +09:45:55 3.9534 +09:46:00 4.0820 +09:46:05 4.1160 +09:46:10 4.0894 +09:46:15 4.0011 +09:46:20 3.9954 +09:46:25 3.9608 +09:46:30 3.9897 +09:46:35 3.9456 +09:46:40 3.9599 +09:46:45 3.9983 +09:46:50 4.0550 +09:46:55 4.0984 +09:47:00 4.0649 +09:47:05 4.0484 +09:47:10 3.9757 +09:47:15 3.9836 +09:47:20 4.0030 +09:47:25 4.0606 +09:47:30 4.0414 +09:47:35 4.0162 +09:47:40 4.0602 +09:47:45 4.0070 +09:47:50 4.1211 +09:47:55 4.0743 +09:48:00 4.0283 +09:48:05 4.1394 +09:48:10 4.2489 +09:48:15 4.1891 +09:48:20 4.1098 +09:48:25 4.0643 +09:48:30 4.0177 +09:48:35 3.9889 +09:48:40 4.0019 +09:48:45 4.1154 +09:48:50 4.1039 +09:48:55 4.0413 +09:49:00 4.0292 +09:49:05 4.0173 +09:49:10 4.0202 +09:49:15 4.0435 +09:49:20 4.0260 +09:49:25 4.0894 +09:49:30 4.0815 +09:49:35 4.1953 +09:49:40 4.1304 +09:49:45 4.1109 +09:49:50 4.0761 +09:49:55 4.0359 +09:50:00 3.9914 +09:50:05 4.0975 +09:50:10 4.1445 +09:50:15 4.1314 +09:50:20 4.0451 +09:50:25 4.0479 +09:50:30 4.0062 +09:50:35 4.0001 +09:50:40 4.0693 +09:50:45 4.1258 +09:50:50 4.0258 +09:50:55 4.0563 +09:51:00 4.1786 +09:51:05 4.0785 +09:51:10 4.0746 +09:51:15 4.0489 +09:51:20 3.9864 +09:51:25 4.0283 +09:51:30 4.0087 +09:51:35 4.0923 +09:51:40 4.1493 +09:51:45 4.1672 +09:51:50 4.0838 +09:51:55 4.0516 +09:52:00 4.0783 +09:52:05 4.1618 +09:52:10 4.1663 +09:52:15 4.0433 +09:52:20 4.0241 +09:52:25 3.9010 +09:52:30 3.9354 +09:52:35 4.0255 +09:52:40 4.0281 +09:52:45 4.0441 +09:52:50 4.0796 +09:52:55 4.1222 +09:53:00 4.0795 +09:53:05 4.0863 +09:53:10 4.1284 +09:53:15 4.1350 +09:53:20 4.1341 +09:53:25 4.1147 +09:53:30 4.1407 +09:53:35 4.1428 +09:53:40 4.1368 +09:53:45 4.1213 +09:53:50 4.0971 +09:53:55 3.9504 +09:54:00 3.9375 +09:54:05 4.0359 +09:54:10 4.0653 +09:54:15 4.0977 +09:54:20 4.0772 +09:54:25 4.1851 +09:54:30 4.2068 +09:54:35 4.1559 +09:54:40 4.1528 +09:54:45 4.1780 +09:54:50 4.1068 +09:54:55 4.0843 +09:55:00 4.0484 +09:55:05 4.0639 +09:55:10 4.1047 +09:55:15 4.0662 +09:55:20 3.9734 +09:55:25 3.9199 +09:55:30 4.0582 +09:55:35 4.1753 +09:55:40 4.2284 +09:55:45 4.1133 +09:55:50 4.0931 +09:55:55 4.0467 +09:56:00 4.0667 +09:56:05 4.1194 +09:56:10 4.0805 +09:56:15 4.0952 +09:56:20 4.1551 +09:56:25 4.2370 +09:56:30 4.1593 +09:56:35 4.0834 +09:56:40 4.1003 +09:56:45 4.1730 +09:56:50 4.1709 +09:56:55 4.1149 +09:57:00 4.1200 +09:57:05 4.1790 +09:57:10 4.1433 +09:57:15 4.1328 +09:57:20 4.0720 +09:57:25 4.1192 +09:57:30 4.1593 +09:57:35 4.1201 +09:57:40 4.0631 +09:57:46 4.0085 +09:57:50 4.0861 +09:57:55 4.0710 +09:58:00 4.1447 +09:58:05 4.1334 +09:58:10 4.1315 +09:58:15 4.1230 +09:58:20 4.2004 +09:58:25 4.2006 +09:58:30 4.0918 +09:58:35 4.0489 +09:58:40 4.1218 +09:58:46 4.1397 +09:58:50 4.1492 +09:58:55 4.2171 +09:59:00 4.1661 +09:59:05 4.1092 +09:59:10 4.0616 +09:59:15 4.0564 +09:59:20 4.0529 +09:59:25 4.1183 +09:59:30 4.1689 +09:59:35 4.1584 +09:59:40 4.1458 +09:59:45 4.0754 +09:59:50 4.0178 +09:59:55 4.0501 +10:00:00 4.1977 +10:00:05 4.2674 +10:00:10 4.1983 +10:00:15 4.0753 +10:00:20 3.9931 +10:00:25 4.0636 +10:00:30 4.0834 +10:00:45 4.0694 +10:00:50 4.1598 +10:00:55 4.1347 +10:01:00 4.1412 +10:01:05 4.1510 +10:01:10 4.1470 +10:01:15 4.0717 +10:01:20 4.0523 +10:01:25 4.2238 +10:01:30 4.1766 +10:01:35 4.1164 +10:01:40 4.1662 +10:01:45 4.1937 +10:01:50 4.1156 +10:01:55 4.0887 +10:02:00 4.2136 +10:02:05 4.1548 +10:02:12 4.1868 +10:02:15 4.1844 +10:02:20 4.1122 +10:02:25 4.0962 +10:02:30 4.0991 +10:02:35 4.0933 +10:02:42 4.2038 +10:02:45 4.1918 +10:02:50 4.1831 +10:02:55 4.0717 +10:03:00 4.0739 +10:03:05 4.0588 +10:03:10 4.1502 +10:03:15 4.1033 +10:03:21 4.1834 +10:03:25 4.1737 +10:03:30 4.2225 +10:03:35 4.0744 +10:03:40 4.0806 +10:03:45 4.1095 +10:03:50 4.1095 +10:03:55 4.1157 +10:04:00 4.0868 +10:04:05 4.1876 +10:04:10 4.2363 +10:04:15 4.2441 +10:04:20 4.2084 +10:04:25 4.0711 +10:04:30 4.0777 +10:04:35 4.0843 +10:04:40 4.0911 +10:04:45 4.0911 +10:04:50 4.0977 +10:04:55 4.1013 +10:05:00 4.1850 +10:05:05 4.2107 +10:05:10 4.1775 +10:05:15 4.2481 +10:05:22 4.1634 +10:05:25 4.2640 +10:05:30 4.2360 +10:05:35 4.2693 +10:05:40 4.3123 +10:05:45 4.2252 +10:05:50 4.2214 +10:05:55 4.2603 +10:06:00 4.2239 +10:06:05 4.1652 +10:06:10 4.1733 +10:06:18 4.3517 +10:06:21 4.3203 +10:06:25 4.3227 +10:06:30 4.3146 +10:06:35 4.3042 +10:06:40 4.2688 +10:06:46 4.3110 +10:06:50 4.3023 +10:06:55 4.3432 +10:07:00 4.3735 +10:07:05 4.3672 +10:07:10 4.3883 +10:07:15 4.2332 +10:07:20 4.2698 +10:07:25 4.2874 +10:07:30 4.3263 +10:07:35 4.3695 +10:07:40 4.3052 +10:07:45 4.2942 +10:07:51 4.3971 +10:07:55 4.4498 +10:08:00 4.4626 +10:08:05 4.4267 +10:08:10 4.4793 +#period 5 exact 1 isdst 1 +10:08:16 +10:08:20 undefined +10:08:25 4.5309 +10:08:30 4.4898 +10:08:35 4.4469 +10:08:40 4.4667 +10:08:45 4.4009 +10:08:50 4.4302 +10:08:55 4.4369 +10:09:00 4.4604 +10:09:05 4.4402 +10:09:10 4.4093 +10:09:15 4.3947 +10:09:20 4.4626 +10:09:25 4.4533 +10:09:30 4.5054 +10:09:35 4.6057 +10:09:40 4.5314 +10:09:45 4.5361 +10:09:50 4.5028 +10:09:55 4.5615 +10:10:00 4.6017 +10:10:05 4.5683 +10:10:10 4.5588 +10:10:15 4.5805 +10:10:20 4.5144 +10:10:25 4.4799 +10:10:30 4.5251 +10:10:35 4.5545 +10:10:40 4.5417 +10:10:45 4.5881 +10:10:50 4.6495 +10:10:55 4.6175 +10:11:00 4.5833 +10:11:05 4.5402 +10:11:10 4.6276 +10:11:15 4.6846 +10:11:20 4.6756 +10:11:25 4.6362 +10:11:31 4.7040 +10:11:35 4.7006 +10:11:40 4.7173 +10:11:45 4.6859 +10:11:50 4.7699 +10:11:55 4.8866 +10:12:00 4.8699 +10:12:05 4.7715 +10:12:11 4.9611 +10:12:15 4.9410 +10:12:20 4.9797 +10:12:25 4.9349 +10:12:30 5.0345 +10:12:35 5.1417 +10:12:40 5.0372 +10:12:45 5.0190 +10:12:50 5.1549 +10:12:55 4.9726 +10:13:00 4.8602 +10:13:05 4.9784 +10:13:10 4.8996 +10:13:15 4.8955 +10:13:20 4.7904 +10:13:25 4.6539 +10:13:30 4.7712 +10:13:35 4.6408 +10:13:40 4.0118 +10:13:45 4.2130 +10:13:50 3.9624 +10:13:55 3.8323 +10:14:00 3.8840 +10:14:05 3.7873 +10:14:12 3.7908 +10:14:15 3.7038 +10:14:20 3.8138 +10:14:25 3.6026 +10:14:30 3.4014 +10:14:37 3.0824 +10:14:40 3.1093 +10:14:45 3.0469 +10:14:50 3.0370 +10:14:55 2.9698 +10:15:02 2.9821 +10:15:05 2.9281 +10:15:10 2.9533 +10:15:16 2.7299 +10:15:20 2.8013 +10:15:25 2.7915 +10:15:30 2.7123 +10:15:37 2.7727 +10:15:40 2.6613 +10:15:45 2.6136 +10:15:50 2.5886 +10:15:55 2.5668 +10:16:01 2.5513 +10:16:06 2.5195 +10:16:10 2.5175 +10:16:16 2.4045 +10:16:21 2.3603 +10:16:25 2.3563 +10:16:30 2.3686 +10:16:35 2.2853 +10:16:40 2.2018 +10:16:45 2.1908 +10:16:51 2.1988 +10:16:56 2.1741 +10:17:00 2.1728 +10:17:05 2.1435 +10:17:10 2.0850 +10:17:15 2.0945 +10:17:20 2.0995 +10:17:25 2.1094 +10:17:30 2.0759 +10:17:35 2.0773 +10:17:40 2.0447 +10:17:45 1.9825 +10:17:50 2.0116 +10:17:55 2.0028 +10:18:00 1.9688 +10:18:05 2.0344 +10:18:10 1.9971 +10:18:15 1.8735 +10:18:20 1.8866 +10:18:25 1.9595 +10:18:30 1.9283 +10:18:36 1.8870 +10:18:40 1.8669 +10:18:45 1.8315 +10:18:50 1.8762 +10:18:55 1.8520 +10:19:00 1.7964 +10:19:05 1.7248 +10:19:10 1.7084 +10:19:15 1.7557 +10:19:20 1.7257 +10:19:25 1.7357 +10:19:30 1.6880 +10:19:35 1.7390 +10:19:40 1.7337 +10:19:45 1.6693 +10:19:50 1.6945 +10:19:55 1.6755 +10:20:00 1.6237 +10:20:05 1.6217 +10:20:10 1.6092 +10:20:15 1.6295 +10:20:20 1.6135 +10:20:25 1.6078 +10:20:30 1.6020 +10:20:35 1.6016 +10:20:40 1.5988 +10:20:45 1.5802 +10:20:50 1.6069 +10:20:55 1.6013 +10:21:00 1.6061 +10:21:05 1.5960 +10:21:11 1.5893 +10:21:15 1.5743 +10:21:20 1.5583 +10:21:25 1.5448 +10:21:30 1.5502 +10:21:35 1.5512 +10:21:40 1.5029 +10:21:45 1.5095 +10:21:50 1.4845 +10:21:55 1.4593 +10:22:00 1.4726 +10:22:05 1.4739 +10:22:10 1.4852 +10:22:15 1.4988 +10:22:20 1.4806 +10:22:25 1.4789 +10:22:30 1.4713 +10:22:35 1.4371 +10:22:40 1.4391 +10:22:45 1.4764 +10:22:50 1.5071 +10:22:55 1.4604 +10:23:00 1.4464 +10:23:05 1.4523 +10:23:10 1.4435 +10:23:15 1.4598 +10:23:20 1.4583 +10:23:25 1.4309 +10:23:30 1.4178 +10:23:35 1.4097 +10:23:40 1.4047 +10:23:45 1.4366 +10:23:50 1.4170 +10:23:55 1.4189 +10:24:00 1.3959 +10:24:05 1.3850 +10:24:10 1.3815 +10:24:15 1.3833 +10:24:20 1.3898 +10:24:28 1.4062 +10:24:30 1.4138 +10:24:35 1.4155 +10:24:40 1.4018 +10:24:46 1.3788 +10:24:50 1.3887 +10:24:55 1.3709 +10:25:07 1.3212 +10:25:10 1.3345 +10:25:15 1.3373 +10:25:20 1.3470 +10:25:25 1.3533 +10:25:30 1.3266 +10:25:35 1.3014 +10:25:40 1.3064 +10:25:45 1.3088 +10:25:50 1.3298 +10:25:55 1.3265 +10:26:00 1.3176 +10:26:05 1.3095 +10:26:10 1.3285 +10:26:15 1.3067 +10:26:20 1.3128 +10:26:25 1.3157 +10:26:30 1.3198 +10:26:35 1.3351 +10:26:40 1.3204 +10:26:45 1.3256 +10:26:50 1.3135 +10:26:55 1.2743 +10:27:00 1.2764 +10:27:05 1.3021 +10:27:10 1.2929 +10:27:15 1.2802 +10:27:20 1.2837 +10:27:25 1.2751 +10:27:30 1.2797 +10:27:35 1.2815 +10:27:40 1.2969 +10:27:45 1.2695 +10:27:50 1.2570 +10:27:55 1.2657 +10:28:00 1.2556 +10:28:05 1.2593 +10:28:10 1.2378 +10:28:15 1.2661 +10:28:20 1.2492 +10:28:25 1.2548 +10:28:30 1.2944 +10:28:35 1.2595 +10:28:40 1.2985 +10:28:45 1.3029 +10:28:50 1.3073 +10:28:55 1.2707 +10:29:00 1.2547 +10:29:05 1.2262 +10:29:10 1.2370 +10:29:15 1.2449 +10:29:20 1.2542 +10:29:25 1.2477 +10:29:30 1.2694 +10:29:35 1.2559 +10:29:40 1.2441 +10:29:45 1.2658 +10:29:50 1.2425 +10:29:55 1.2307 +10:30:00 1.2271 +10:30:05 1.2296 +10:30:10 1.2629 +10:30:15 1.2348 +10:30:20 1.2277 +10:30:25 1.2394 +10:30:30 1.2062 +10:30:35 1.1937 +10:30:40 1.2118 +10:30:45 1.2176 +10:30:50 1.2219 +10:30:55 1.2156 +10:31:00 1.2225 +10:31:05 1.2177 +10:31:10 1.2405 +10:31:15 1.2575 +10:31:20 1.2432 +10:31:25 1.2235 +10:31:30 1.2277 +10:31:35 1.2281 +10:31:40 1.2086 +10:31:45 1.2086 +10:31:50 1.2286 +10:32:05 1.2398 +10:32:10 1.2044 +10:32:15 1.2039 +10:32:20 1.1998 +10:32:25 1.2254 +10:32:30 1.2332 +10:32:35 1.2403 +10:32:40 1.2338 +10:32:45 1.2160 +10:32:50 1.2091 +10:32:55 1.2151 +10:33:00 1.2040 +10:33:05 1.2147 +10:33:10 1.2183 +10:33:15 1.2197 +10:33:20 1.2240 +10:33:25 1.2205 +10:33:30 1.1993 +10:33:35 1.2179 +10:33:40 1.2217 +10:33:45 1.2175 +10:33:50 1.2162 +10:33:55 1.2168 +10:34:00 1.2218 +10:34:05 1.2217 +10:34:10 1.2016 +10:34:15 1.2193 +10:34:20 1.2085 +10:34:25 1.2214 +10:34:30 1.1953 +10:34:35 1.1889 +10:34:40 1.2015 +10:34:45 1.1777 +10:34:50 1.1770 +10:34:55 1.1864 +10:35:00 1.1965 +10:35:05 1.2061 +10:35:10 1.1853 +10:35:15 1.1827 +10:35:20 1.1663 +10:35:25 1.1647 +10:35:30 1.1472 +10:35:35 1.1622 +10:35:40 1.1622 +10:35:45 1.1618 +10:35:50 1.1602 +10:35:55 1.1596 +10:36:00 1.1582 +10:36:05 1.1554 +10:36:10 1.1554 +10:36:15 1.1543 +10:36:20 1.1524 +10:36:25 1.1500 +10:36:30 1.1475 +10:36:35 1.1467 +10:36:40 1.1467 +10:36:45 1.1441 +10:36:50 1.1424 +10:36:55 1.1390 +10:37:00 1.1365 +10:37:05 1.1365 +10:37:10 1.1349 +10:37:15 1.1324 +10:37:20 1.1300 +10:37:25 1.1276 +10:37:30 1.1245 +10:37:35 1.1233 +10:37:40 1.1233 +10:37:45 1.1225 +10:37:50 1.1191 +10:37:55 1.1180 +10:38:00 1.1145 +10:38:05 1.1145 +10:38:10 1.1113 +10:38:15 1.1082 +10:38:20 1.1069 +10:38:25 1.1015 +10:38:30 1.0979 +10:38:35 1.0979 +10:38:40 1.0936 +10:38:45 1.0879 +10:38:50 1.0828 +10:38:55 1.0792 +10:39:00 1.0729 +10:39:05 1.0729 +10:39:10 1.0676 +10:39:15 1.0605 +10:39:22 1.0539 +10:39:26 1.0467 +10:39:30 0.84536 +10:39:35 0.74483 +10:39:40 0.75113 +10:39:45 0.74009 +10:39:50 0.73113 +10:39:55 0.72446 +10:40:00 0.72089 +10:40:05 0.71807 +10:40:10 0.70345 +10:40:15 0.70250 +10:40:20 0.69386 +10:40:25 0.68588 +10:40:30 0.66583 +10:40:35 0.65567 +10:40:40 0.65210 +10:40:45 0.64048 +10:40:50 0.63952 +10:40:55 0.62747 +10:41:00 0.62174 +10:41:05 0.61403 +10:41:10 0.60431 +10:41:15 0.60070 +10:41:20 0.59183 +10:41:25 0.58700 +10:41:30 0.57518 +10:41:35 0.56262 +10:41:40 0.55496 +10:41:45 0.55109 +10:41:50 0.53962 +10:41:55 0.53210 +10:42:00 0.52577 +10:42:05 0.52365 +10:42:10 0.51646 +10:42:15 0.50681 +10:42:20 0.49762 +10:42:25 0.49408 +10:42:30 0.48980 +10:42:35 0.48224 +10:42:40 0.47788 +10:42:45 0.47288 +10:42:50 0.46190 +10:42:55 0.45356 +10:43:00 0.44897 +10:43:05 0.44388 +10:43:10 0.43695 +10:43:15 0.43316 +10:43:20 0.42751 +10:43:25 0.42078 +10:43:30 0.41658 +10:43:35 0.41287 +10:43:40 0.40774 +10:43:45 0.40262 +10:43:50 0.39624 +10:43:55 0.38923 +10:44:00 0.38594 +10:44:05 0.38164 +10:44:10 0.37612 +10:44:15 0.37139 +10:44:20 0.36717 +10:44:25 0.36472 +10:44:30 0.36209 +10:44:35 0.35429 +10:44:41 0.35104 +10:44:45 0.35039 +10:44:51 0.34263 +10:44:55 0.34034 +10:45:00 0.33901 +10:45:05 0.33084 +10:45:10 0.32455 +10:45:15 0.32082 +10:45:20 0.31893 +10:45:25 0.31708 +10:45:30 0.31320 +10:45:36 0.30916 +10:45:40 0.30400 +10:45:45 0.30150 +10:45:50 0.29944 +10:45:55 0.29693 +10:46:00 0.29333 +10:46:05 0.28846 +10:46:10 0.28612 +10:46:15 0.28194 +10:46:20 0.27922 +10:46:25 0.27704 +10:46:30 0.27601 +10:46:35 0.27250 +10:46:40 0.27030 +10:46:45 0.26873 +10:46:50 0.26542 +10:46:55 0.26364 +10:47:13 0.26133 +10:47:15 0.25953 +10:47:20 0.25731 +10:47:25 0.25488 +10:47:30 0.25295 +10:47:35 0.25086 +10:47:40 0.24804 +10:47:45 0.24557 +10:47:50 0.24362 +10:47:55 0.24290 +10:48:00 0.23928 +10:48:05 0.23789 +10:48:10 0.23654 +10:48:15 0.23578 +10:48:20 0.23481 +10:48:25 0.23269 +10:48:30 0.23017 +10:48:36 0.22944 +10:48:40 0.22824 +10:48:45 0.22601 +10:48:50 0.22445 +10:48:55 0.22392 +10:49:00 0.22162 +10:49:05 0.22056 +10:49:10 0.21970 +10:49:15 0.21829 +10:49:20 0.21726 +10:49:25 0.21563 +10:49:30 0.21467 +10:49:35 0.21332 +10:49:40 0.21325 +10:49:45 0.21151 +10:49:50 0.20937 +10:49:55 0.20929 +10:50:00 0.20912 +10:50:05 0.20758 +10:50:10 0.20683 +10:50:45 0.20683 +10:50:50 0.20361 +10:50:55 0.19867 +10:51:00 0.20091 +10:51:05 0.19896 +10:51:10 0.19481 +10:51:15 0.19445 +10:51:20 0.19477 +10:51:25 0.19518 +10:51:30 0.19576 +10:51:35 0.19480 +10:51:40 0.19300 +10:51:45 0.19219 +10:51:50 0.19360 +10:51:55 0.19238 +10:52:00 0.19138 +10:52:05 0.19190 +10:52:10 0.19302 +10:52:15 0.18943 +10:52:20 0.18850 +10:52:25 0.19098 +10:52:30 0.18732 +10:52:35 0.18793 +10:52:40 0.18731 +10:52:45 0.18768 +10:52:50 0.18947 +10:52:55 0.18657 +10:53:00 0.18429 +10:53:05 0.18504 +10:53:10 0.18570 +10:53:15 0.18377 +10:53:20 0.18366 +10:53:26 0.18473 +10:53:30 0.18327 +10:53:35 0.18286 +10:53:40 0.18259 +10:53:45 0.18108 +10:53:50 0.18386 +10:53:55 0.18327 +10:54:00 0.18113 +10:54:05 0.18104 +10:54:10 0.17978 +10:54:15 0.18180 +10:54:20 0.18140 +10:54:25 0.17852 +10:54:30 0.17879 +10:54:35 0.17776 +10:54:40 0.17910 +10:54:45 0.17814 +10:54:50 0.17834 +10:54:55 0.17832 +10:55:00 0.17793 +10:55:05 0.17900 +10:55:10 0.17671 +10:55:15 0.17583 +10:55:20 0.17775 +10:55:25 0.17560 +10:55:30 0.17490 +10:55:35 0.17670 +10:55:40 0.17595 +10:55:45 0.17613 +10:55:50 0.17471 +10:55:55 0.17515 +10:56:00 0.17577 +10:56:05 0.17558 +10:56:10 0.17623 +10:56:15 0.17700 +10:56:20 0.17579 +10:56:25 0.17303 +10:56:30 0.17407 +10:56:35 0.17452 +10:56:40 0.17587 +10:56:45 0.17579 +10:56:50 0.17391 +10:56:55 0.17416 +10:57:00 0.17470 +10:57:05 0.17509 +10:57:10 0.17758 +10:57:15 0.17691 +10:57:20 0.17655 +10:57:25 0.17521 +10:57:30 0.17390 +10:57:35 0.17355 +10:57:40 0.17325 +10:57:45 0.17285 +10:57:50 0.17206 +10:57:55 0.17371 +10:58:00 0.17464 +10:58:05 0.17539 +10:58:10 0.17357 +10:58:15 0.17354 +10:58:20 0.17328 +10:58:25 0.17219 +10:58:30 0.17345 +10:58:35 0.17295 +10:58:40 0.17402 +10:58:45 0.17367 +10:58:50 0.17251 +10:58:55 0.17370 +10:59:00 0.17298 +10:59:05 0.17233 +10:59:10 0.17306 +10:59:15 0.17183 +10:59:20 0.17074 +10:59:25 0.17206 +10:59:30 0.17406 +10:59:35 0.17503 +10:59:42 0.17345 +10:59:45 0.17100 +10:59:50 0.17206 +10:59:55 0.17148 +11:00:00 0.17226 +11:00:05 0.17189 +11:00:10 0.17252 +11:00:15 0.17095 +11:00:20 0.17170 +11:00:25 0.17195 +11:00:30 0.17073 +11:00:35 0.17342 +11:00:40 0.17349 +11:00:45 0.17298 +11:00:50 0.17270 +11:00:55 0.17201 +11:01:00 0.17278 +11:01:05 0.17146 +11:01:10 0.17165 +11:01:15 0.17264 +11:01:20 0.17293 +11:01:25 0.17341 +11:01:30 0.17291 +11:01:35 0.17303 +11:01:40 0.17342 +11:01:45 0.17244 +11:01:50 0.17070 +11:01:55 0.16949 +11:02:00 0.17083 +11:02:05 0.17189 +11:02:10 0.17100 +11:02:15 0.17224 +11:02:20 0.17130 +11:02:25 0.17118 +11:02:30 0.17205 +11:02:35 0.17104 +11:02:40 0.17005 +11:02:45 0.17039 +11:02:50 0.17138 +11:02:55 0.17199 +11:03:00 0.17222 +11:03:05 0.17008 +11:03:10 0.17144 +11:03:15 0.17236 +11:03:20 0.17117 +11:03:25 0.17320 +11:03:30 0.17169 +11:03:35 0.17078 +11:03:40 0.16918 +11:03:45 0.16948 +11:03:50 0.16729 +11:03:55 0.17117 +11:04:00 0.17135 +11:04:05 0.17234 +11:04:10 0.17061 +11:04:15 0.17112 +11:04:20 0.17200 +11:04:25 0.17329 +11:04:30 0.17292 +11:04:35 0.17379 +11:04:40 0.17288 +11:04:45 0.17322 +11:04:50 0.17342 +11:04:55 0.17063 +11:05:00 0.17190 +11:05:05 0.17150 +11:05:10 0.17237 +11:05:15 0.17141 +11:05:20 0.17062 +11:05:25 0.16872 +11:05:30 0.17156 +11:05:35 0.17223 +11:05:40 0.17161 +11:05:45 0.16884 +11:05:50 0.17009 +11:05:55 0.17003 +11:06:00 0.16997 +11:06:05 0.16861 +11:06:10 0.16865 +11:06:15 0.16881 +11:06:20 0.16837 +11:06:25 0.16955 +11:06:30 0.16943 +11:06:35 0.16899 +11:06:40 0.17035 +11:06:45 0.16953 +11:06:50 0.16769 +11:06:55 0.16894 +11:07:00 0.17079 +11:07:05 0.17013 +11:07:10 0.16827 +11:07:15 0.16696 +11:07:20 0.16713 +11:07:25 0.17030 +11:07:30 0.16981 +11:07:35 0.17028 +11:07:40 0.17114 +11:07:45 0.17002 +11:07:50 0.16801 +11:07:55 0.16763 +11:08:00 0.16885 +11:08:05 0.16950 +11:08:10 0.16966 +11:08:15 0.16711 +11:08:20 0.16819 +11:08:25 0.16835 +11:08:30 0.16825 +11:08:35 0.16766 +11:08:40 0.16834 +11:08:45 0.16834 +11:08:50 0.16887 +11:08:55 0.16744 +11:09:00 0.16829 +11:09:05 0.16791 +11:09:10 0.16852 +11:09:15 0.16674 +11:09:20 0.16763 +11:09:25 0.16774 +11:09:30 0.16982 +11:09:35 0.16970 +11:09:40 0.16953 +11:09:45 0.16923 +11:09:50 0.16882 +11:09:55 0.16741 +11:10:00 0.16742 +11:10:05 0.16757 +11:10:10 0.16716 +11:10:15 0.16858 +11:10:20 0.16842 +11:10:25 0.16707 +11:10:30 0.16772 +11:10:35 0.16719 +11:10:40 0.16849 +11:10:45 0.17013 +11:10:50 0.16863 +11:10:55 0.16834 +11:11:00 0.16861 +11:11:05 0.16860 +11:11:10 0.17049 +11:11:15 0.16984 +11:11:20 0.16889 +11:11:25 0.16696 +11:11:30 0.16720 +11:11:35 0.16795 +11:11:40 0.16761 +11:11:45 0.16731 +11:11:50 0.16815 +11:11:55 0.16858 +11:12:00 0.16598 +11:12:05 0.16389 +11:12:10 0.16437 +11:12:15 0.16605 +11:12:20 0.16766 +11:12:25 0.16852 +11:12:30 0.16975 +11:12:35 0.16718 +11:12:40 0.16852 +11:12:45 0.16623 +11:12:50 0.16528 +11:12:55 0.16771 +11:13:00 0.16789 +11:13:05 0.16746 +11:13:10 0.16804 +11:13:15 0.16724 +11:13:20 0.16721 +11:13:25 0.16864 +11:13:30 0.16929 +11:13:35 0.16842 +11:13:40 0.16849 +11:13:45 0.16800 +11:13:50 0.16834 +11:13:55 0.16788 +11:14:00 0.16716 +11:14:05 0.16872 +11:14:10 0.16797 +11:14:15 0.16766 +11:14:20 0.16801 +11:14:25 0.16852 +11:14:30 0.16771 +11:14:35 0.16969 +11:14:40 0.17053 +11:14:45 0.16788 +11:14:50 0.16806 +11:14:55 0.17059 +11:15:00 0.16848 +11:15:05 0.16675 +11:15:10 0.16590 +11:15:15 0.16748 +11:15:20 0.16795 +11:15:25 0.16861 +11:15:30 0.16923 +11:15:35 0.16723 +11:15:40 0.16869 +11:15:45 0.16887 +11:15:50 0.16936 +11:15:55 0.16794 +11:16:00 0.16773 +11:16:05 0.16881 +11:16:10 0.16798 +11:16:15 0.16671 +11:16:20 0.16740 +11:16:26 0.16839 +11:16:30 0.16834 +11:16:35 0.16775 +11:16:40 0.16933 +11:16:46 0.17123 +11:16:50 0.16948 +11:16:55 0.16880 +11:17:00 0.17043 +11:17:05 0.16880 +11:17:10 0.16783 +11:17:15 0.16921 +11:17:20 0.16759 +11:17:25 0.16682 +11:17:30 0.16761 +11:17:36 0.16722 +11:17:40 0.16778 +11:17:45 0.16816 +11:17:50 0.16604 +11:17:55 0.16583 +11:18:02 0.16627 +11:18:07 0.16714 +11:18:10 0.16747 +11:18:15 0.16686 +11:18:20 0.16604 +11:18:25 0.16638 +11:18:30 0.16838 +11:18:35 0.16923 +11:18:40 0.16864 +11:18:45 0.16720 +11:18:50 0.16739 +11:18:55 0.16785 +11:19:00 0.16762 +11:19:05 0.16838 +11:19:10 0.16712 +11:19:15 0.16734 +11:19:20 0.16841 +11:19:25 0.16940 +11:19:30 0.16815 +11:19:35 0.16607 +11:19:40 0.16655 +11:19:45 0.16817 +11:19:50 0.16744 +11:19:55 0.16822 +11:20:00 0.16871 +11:20:06 0.16801 +11:20:11 0.16741 +11:20:15 0.16816 +11:20:20 0.16876 +11:20:25 0.16859 +11:20:30 0.16689 +11:20:35 0.16718 +11:20:40 0.16904 +11:20:45 0.16991 +11:20:50 0.16842 +11:20:55 0.16692 +11:21:01 0.17043 +11:21:05 0.16805 +11:21:10 0.16636 +11:21:15 0.16793 +11:21:20 0.16950 +11:21:26 0.16994 +11:21:30 0.16887 +11:21:35 0.16761 +11:21:41 0.16704 +11:21:45 0.16595 +11:21:50 0.16655 +11:22:06 0.16854 +11:22:10 0.16860 +11:22:15 0.16760 +11:22:20 0.16762 +11:22:25 0.16694 +11:22:30 0.16717 +11:22:35 0.16594 +11:22:40 0.16646 +11:22:45 0.16853 +11:22:50 0.16712 +11:22:55 0.16666 +11:23:00 0.16712 +11:23:05 0.16726 +11:23:10 0.16805 +11:23:15 0.16821 +11:23:20 0.16831 +11:23:25 0.16886 +11:23:30 0.16968 +11:23:35 0.16924 +11:23:40 0.16925 +11:23:45 0.16800 +11:23:50 0.16708 +11:23:56 0.16636 +11:24:00 0.16582 +11:24:05 0.16608 +11:24:10 0.16755 +11:24:15 0.16837 +11:24:24 0.16679 +11:24:25 0.16745 +11:24:30 0.16763 +11:24:35 0.16793 +11:24:40 0.16724 +11:24:46 0.16732 +11:24:50 0.16937 +11:24:56 0.16865 +#period 5 exact 1 isdst 1 +11:25:02 +11:25:08 undefined +11:25:10 0.16569 +11:25:15 0.16780 +11:25:20 0.16914 +11:25:25 0.16709 +11:25:30 0.16637 +11:25:42 0.16662 +11:25:45 0.16746 +11:25:50 0.16715 +11:25:55 0.16675 +11:26:00 0.16642 +11:26:05 0.16650 +11:26:10 0.16836 +11:26:15 0.16760 +11:26:20 0.16687 +11:26:25 0.16660 +11:26:30 0.16597 +11:26:35 0.16758 +11:26:40 0.16734 +11:26:45 0.16683 +11:26:50 0.16935 +11:26:55 0.16817 +11:27:00 0.16830 +11:27:05 0.17000 +11:27:10 0.16855 +11:27:15 0.16712 +11:27:20 0.16844 +11:27:25 0.16978 +11:27:30 0.16931 +11:27:35 0.17017 +11:27:40 0.16953 +11:27:45 0.16890 +11:27:50 0.16699 +11:27:55 0.16745 +11:28:00 0.16577 +11:28:05 0.16523 +11:28:10 0.16571 +11:28:15 0.16856 +11:28:20 0.16771 +11:28:25 0.16680 +11:28:30 0.16751 +11:28:35 0.16886 +11:28:40 0.16900 +11:28:45 0.16960 +11:28:50 0.16849 +11:28:55 0.16824 +11:29:00 0.16911 +11:29:05 0.16797 +11:29:10 0.16759 +11:29:15 0.16819 +11:29:20 0.16609 +11:29:25 0.16441 +11:29:30 0.16637 +11:29:35 0.16731 +11:29:40 0.16724 +11:29:45 0.16788 +11:29:50 0.16833 +11:29:55 0.16854 +11:30:00 0.16597 +11:30:05 0.16670 +11:30:10 0.16639 +11:30:15 0.16540 +11:30:20 0.16672 +11:30:25 0.16862 +11:30:30 0.16860 +11:30:35 0.16610 +11:30:40 0.16525 +11:30:45 0.16755 +11:30:50 0.16710 +11:30:55 0.16703 +11:31:00 0.16640 +11:31:05 0.16715 +11:31:10 0.16835 +11:31:15 0.16808 +11:31:20 0.16869 +11:31:25 0.16671 +11:31:30 0.16719 +11:31:35 0.16659 +11:31:40 0.16743 +11:31:45 0.16758 +11:31:50 0.16752 +11:31:55 0.16582 +11:32:00 0.16755 +11:32:05 0.16897 +11:32:10 0.16824 +11:32:15 0.16738 +11:32:20 0.16826 +11:32:25 0.16943 +11:32:30 0.16754 +11:32:35 0.16890 +11:32:40 0.16896 +11:32:45 0.16641 +#period 5 exact 1 isdst 1 +11:32:51 +11:32:54 undefined +11:32:55 0.16691 +11:33:00 0.16803 +11:33:05 0.16942 +11:33:10 0.16927 +11:33:15 0.16927 +11:33:20 0.16815 +11:33:25 0.16657 +11:33:30 0.16763 +#period 5 exact 1 isdst 1 +11:33:34 +11:33:36 undefined +11:33:40 0.16720 +11:33:45 0.16802 +11:33:50 0.16828 +11:33:55 0.16962 +11:34:00 0.16724 +11:34:05 0.18807 +11:34:10 0.26405 +11:34:15 0.29695 +11:34:20 0.28557 +11:34:25 0.27158 +11:34:30 0.26607 +11:34:35 0.25766 +11:34:40 0.24693 +11:34:45 0.24104 +11:34:50 0.23841 +11:34:55 0.23652 +11:35:00 0.22990 +11:35:05 0.22422 +11:35:10 0.22408 +11:35:15 0.22204 +11:35:20 0.21966 +11:35:25 0.21860 +11:35:30 0.21610 +11:35:35 0.21556 +11:35:40 0.21210 +11:35:45 0.21183 +11:35:50 0.20973 +11:35:55 0.20951 +11:36:00 0.20950 +11:36:05 0.20584 +11:36:10 0.20517 +11:36:15 0.20570 +11:36:20 0.20696 +11:36:25 0.20412 +11:36:30 0.20317 +11:36:35 0.20353 +11:36:40 0.20343 +11:36:45 0.20220 +11:36:50 0.20114 +11:36:55 0.19993 +11:37:00 0.19829 +11:37:05 0.21647 +11:37:19 0.24839 +11:37:20 0.25409 +11:37:25 0.30658 +11:37:30 0.31093 +11:37:35 0.30337 +11:37:40 0.28532 +11:37:45 0.27527 +11:37:50 0.26959 +11:37:55 0.26272 +11:38:00 0.29671 +11:38:05 0.37862 +11:38:10 0.40128 +11:38:15 0.38633 +11:38:20 0.37319 +11:38:25 0.36633 +11:38:30 0.36209 +11:38:35 0.34749 +11:38:40 0.33123 +11:38:45 0.32494 +11:38:50 0.31280 +11:38:55 0.30351 +11:39:00 0.30360 +11:39:05 0.29811 +11:39:10 0.28625 +11:39:15 0.28150 +11:39:20 0.27588 +11:39:25 0.27431 +11:39:30 0.27511 +11:39:35 0.26988 +11:39:40 0.26626 +11:39:45 0.26515 +11:39:50 0.26306 +11:39:55 0.25825 +11:40:00 0.25979 +11:40:05 0.25523 +11:40:10 0.25450 +11:40:15 0.24938 +11:40:20 0.24825 +11:40:25 0.24672 +11:40:30 0.24364 +11:40:35 0.24264 +11:40:40 0.24064 +11:40:45 0.24055 +11:40:50 0.24332 +11:40:55 0.23543 +11:41:00 0.23481 +11:41:05 0.23377 +11:41:10 0.23128 +11:41:15 0.23124 +11:41:20 0.23077 +11:41:25 0.23547 +11:41:30 0.24277 +11:41:35 0.23868 +11:41:40 0.24692 +11:41:45 0.24863 +11:41:50 0.24454 +11:41:55 0.24667 +11:42:00 0.24791 +11:42:05 0.24928 +11:42:10 0.25067 +11:42:15 0.25331 +11:42:20 0.25700 +11:42:25 0.25861 +11:42:30 0.25518 +11:42:35 0.25326 +11:42:40 0.26100 +11:42:45 0.26359 +11:42:50 0.26235 +11:42:55 0.26845 +11:43:00 0.26646 +11:43:05 0.26620 +11:43:10 0.26791 +11:43:15 0.26933 +11:43:20 0.27137 +11:43:25 0.27263 +11:43:30 0.27153 +11:43:35 0.27250 +11:43:40 0.27287 +11:43:45 0.27401 +11:43:50 0.27699 +11:43:55 0.27201 +11:44:00 0.27471 +11:44:05 0.28284 +11:44:10 0.28228 +11:44:15 0.27845 +11:44:20 0.27478 +11:44:25 0.27466 +11:44:30 0.27295 +11:44:35 0.27613 +11:44:40 0.28173 +11:44:45 0.28299 +11:44:50 0.28044 +11:44:55 0.28030 +11:45:00 0.28614 +11:45:05 0.27933 +11:45:10 0.28259 +11:45:15 0.28707 +11:45:20 0.28120 +11:45:25 0.28212 +11:45:30 0.29001 +11:45:35 0.29033 +11:45:40 0.29131 +11:45:45 0.28370 +11:45:50 0.28132 +11:45:55 0.28151 +11:46:00 0.28219 +11:46:05 0.28960 +11:46:10 0.29348 +11:46:15 0.29167 +11:46:20 0.29311 +11:46:25 0.28740 +11:46:30 0.28994 +11:46:35 0.28390 +11:46:40 0.28080 +11:46:45 0.28895 +11:46:50 0.28762 +11:46:55 0.28376 +11:47:00 0.28751 +11:47:05 0.28868 +11:47:10 0.29128 +11:47:15 0.28934 +11:47:20 0.29290 +11:47:25 0.29253 +11:47:30 0.28533 +11:47:35 0.28683 +11:47:40 0.28639 +11:47:45 0.28520 +11:47:50 0.29097 +11:47:55 0.29217 +11:48:00 0.29287 +11:48:05 0.29543 +11:48:10 0.29524 +11:48:15 0.29272 +11:48:20 0.29203 +11:48:25 0.29265 +11:48:30 0.28751 +11:48:35 0.29346 +11:48:40 0.29456 +11:48:45 0.28842 +11:48:50 0.29005 +11:48:55 0.29033 +11:49:00 0.29074 +11:49:05 0.28890 +11:49:10 0.28963 +11:49:15 0.29586 +11:49:20 0.29522 +11:49:25 0.28917 +11:49:30 0.29223 +11:49:35 0.29215 +11:49:40 0.29093 +11:49:45 0.29264 +11:49:50 0.28628 +11:49:55 0.28850 +11:50:00 0.29223 +11:50:05 0.29216 +11:50:10 0.28504 +11:50:15 0.28805 +11:50:20 0.28356 +11:50:25 0.28175 +11:50:30 0.28778 +11:50:35 0.28934 +11:50:40 0.28585 +11:50:45 0.28721 +11:50:50 0.28924 +11:50:55 0.28060 +11:51:00 0.28262 +11:51:05 0.28506 +11:51:10 0.28300 +11:51:15 0.28656 +11:51:20 0.28978 +11:51:25 0.28865 +11:51:30 0.28767 +11:51:35 0.28674 +11:51:40 0.28746 +11:51:45 0.29161 +11:51:50 0.28296 +11:51:55 0.28497 +11:52:00 0.28275 +11:52:05 0.28303 +11:52:10 0.28743 +11:52:15 0.28640 +11:52:20 0.28970 +11:52:25 0.28897 +11:52:30 0.29344 +11:52:35 0.29036 +11:52:40 0.28510 +11:52:45 0.28446 +11:52:50 0.28213 +11:52:55 0.28207 +11:53:00 0.28403 +11:53:05 0.28457 +11:53:10 0.28352 +11:53:15 0.28732 +11:53:20 0.29326 +11:53:25 0.29274 +11:53:30 0.28827 +11:53:35 0.28761 +11:53:40 0.28629 +11:53:45 0.29315 +11:53:50 0.28785 +11:53:55 0.28157 +11:54:00 0.28102 +11:54:05 0.28919 +11:54:10 0.28777 +11:54:15 0.28712 +11:54:20 0.28504 +11:54:25 0.29126 +11:54:30 0.29298 +11:54:35 0.28833 +11:54:40 0.28915 +11:54:45 0.28398 +11:54:50 0.28303 +11:54:55 0.29030 +11:55:00 0.28888 +11:55:05 0.29384 +11:55:10 0.29249 +11:55:15 0.28721 +11:55:20 0.28445 +11:55:25 0.28846 +11:55:30 0.28781 +11:55:35 0.28483 +11:55:40 0.28906 +11:55:45 0.28108 +11:55:50 0.27826 +11:55:55 0.27961 +11:56:00 0.28645 +11:56:05 0.28628 +11:56:10 0.27372 +11:56:15 0.27578 +11:56:20 0.28568 +11:56:25 0.29089 +11:56:30 0.28513 +11:56:35 0.28347 +11:56:40 0.28412 +11:56:45 0.28837 +11:56:50 0.29010 +11:56:55 0.28629 +11:57:00 0.28043 +11:57:05 0.27714 +11:57:10 0.28387 +11:57:15 0.28829 +11:57:20 0.28218 +11:57:25 0.28140 +11:57:30 0.28849 +11:57:35 0.28841 +11:57:40 0.28098 +11:57:45 0.28179 +11:57:50 0.28267 +11:57:55 0.28353 +11:58:00 0.28730 +11:58:05 0.28866 +11:58:10 0.28537 +11:58:15 0.28595 +11:58:20 0.28376 +11:58:25 0.28350 +11:58:30 0.28550 +11:58:35 0.28326 +11:58:40 0.27693 +11:58:45 0.28583 +11:58:50 0.28777 +11:58:55 0.28133 +11:59:00 0.28455 +11:59:05 0.27978 +11:59:10 0.28112 +11:59:15 0.28937 +11:59:20 0.29163 +11:59:25 0.28607 +11:59:30 0.27981 +11:59:35 0.28470 +11:59:40 0.28645 +11:59:45 0.28762 +11:59:50 0.28596 +11:59:55 0.28529 +12:00:00 0.28652 +12:00:05 0.28992 +12:00:10 0.28573 +12:00:15 0.28059 +12:00:20 0.27977 +12:00:25 0.28098 +12:00:30 0.28549 +12:00:35 0.28617 +12:00:40 0.28493 +12:00:45 0.28359 +12:00:58 0.27947 +12:01:00 0.28060 +12:01:05 0.27632 +12:01:10 0.28396 +12:01:15 0.28060 +12:01:20 0.28801 +12:01:25 0.28313 +12:01:30 0.28327 +12:01:35 0.27912 +12:01:40 0.28490 +12:01:45 0.28252 +12:01:50 0.27599 +12:01:55 0.27515 +12:02:00 0.27723 +12:02:05 0.28290 +12:02:10 0.28261 +12:02:15 0.28090 +12:02:20 0.28051 +12:02:25 0.28137 +12:02:30 0.28021 +12:02:35 0.28208 +12:02:40 0.28686 +12:02:45 0.28068 +12:02:50 0.28100 +12:02:55 0.27616 +12:03:00 0.28653 +12:03:05 0.28356 +12:03:10 0.28128 +12:03:15 0.27708 +12:03:20 0.28264 +12:03:25 0.28207 +12:03:30 0.28218 +12:03:35 0.28667 +12:03:40 0.28311 +12:03:45 0.27664 +12:03:50 0.28180 +12:03:55 0.28178 +12:04:00 0.27786 +12:04:05 0.27898 +12:04:10 0.27972 +12:04:15 0.28501 +12:04:20 0.28061 +12:04:25 0.28874 +12:04:30 0.28868 +12:04:35 0.28539 +12:04:40 0.28550 +12:04:45 0.28114 +12:04:50 0.29205 +12:04:55 0.28986 +12:05:00 0.28379 +12:05:05 0.28717 +12:05:10 0.28431 +12:05:15 0.28524 +12:05:20 0.28247 +12:05:25 0.28585 +12:05:30 0.28957 +12:05:35 0.28666 +12:05:40 0.28035 +12:05:45 0.28286 +12:05:50 0.28679 +12:05:55 0.28496 +12:06:00 0.28120 +12:06:05 0.27804 +12:06:10 0.27675 +12:06:15 0.28127 +12:06:20 0.28136 +12:06:25 0.27526 +12:06:30 0.27554 +12:06:35 0.27410 +12:06:40 0.27554 +12:06:45 0.28015 +12:06:50 0.28421 +12:06:55 0.28968 +12:07:00 0.28335 +12:07:05 0.27568 +12:07:10 0.27486 +12:07:15 0.27840 +12:07:20 0.28633 +12:07:25 0.29157 +12:07:30 0.28866 +12:07:35 0.28296 +12:07:40 0.27713 +12:07:45 0.27529 +12:07:50 0.27422 +12:07:55 0.27648 +12:08:00 0.27666 +12:08:05 0.28298 +12:08:10 0.28214 +12:08:15 0.28234 +12:08:20 0.27996 +12:08:25 0.28431 +12:08:30 0.28672 +12:08:35 0.28481 +12:08:40 0.28366 +12:08:45 0.29053 +12:08:50 0.28212 +12:08:55 0.27817 +12:09:00 0.28424 +12:09:05 0.28097 +12:09:10 0.28493 +12:09:15 0.28187 +12:09:20 0.28381 +12:09:25 0.27760 +12:09:30 0.28174 +12:09:35 0.28708 +12:09:40 0.28282 +12:09:45 0.28350 +12:09:50 0.27818 +12:09:55 0.27843 +12:10:00 0.28212 +12:10:05 0.27964 +12:10:10 0.27637 +12:10:15 0.27943 +12:10:20 0.28054 +12:10:25 0.28487 +12:10:30 0.28663 +12:10:35 0.28540 +12:10:40 0.28336 +12:10:45 0.28469 +12:10:50 0.28395 +12:10:55 0.28488 +12:11:00 0.28385 +12:11:05 0.27723 +12:11:10 0.27843 +12:11:15 0.27950 +12:11:20 0.27552 +12:11:25 0.28300 +12:11:30 0.28948 +12:11:35 0.28157 +12:11:40 0.27650 +12:11:45 0.27835 +12:11:50 0.27656 +12:11:56 0.28258 +12:12:00 0.28731 +12:12:05 0.28385 +12:12:10 0.27960 +12:12:15 0.27315 +12:12:20 0.27115 +12:12:25 0.26546 +12:12:30 0.25897 +12:12:35 0.25771 +12:12:40 0.25365 +12:12:45 0.25193 +12:12:50 0.24619 +12:12:55 0.24197 +12:13:00 0.23827 +12:13:05 0.23745 +12:13:10 0.23425 +12:13:15 0.23232 +12:13:20 0.23308 +12:13:25 0.22913 +12:13:30 0.22556 +12:13:35 0.22570 +12:13:40 0.22582 +12:13:45 0.22558 +12:13:50 0.22294 +12:13:55 0.22043 +12:14:00 0.21928 +12:14:05 0.21625 +12:14:10 0.21417 +12:14:15 0.21048 +12:14:20 0.20958 +12:14:25 0.21040 +12:14:30 0.21001 +12:14:35 0.21015 +12:14:40 0.20922 +12:14:45 0.20292 +12:14:50 0.20317 +12:14:55 0.20195 +12:15:00 0.20223 +12:15:05 0.20020 +12:15:10 0.20124 +12:15:15 0.20379 +12:15:20 0.20381 +12:15:25 0.20113 +12:15:30 0.19836 +12:15:35 0.19736 +12:15:40 0.19562 +12:15:45 0.19593 +12:15:50 0.19735 +12:15:55 0.19302 +12:16:00 0.19330 +12:16:12 0.19388 +12:16:15 0.19313 +12:16:20 0.19340 +12:16:25 0.19243 +12:16:30 0.19154 +12:16:35 0.19109 +12:16:40 0.19096 +12:16:45 0.19029 +12:16:50 0.18966 +12:16:55 0.18957 +12:17:00 0.18759 +12:17:05 0.18749 +12:17:10 0.18655 +12:17:15 0.18451 +12:17:20 0.18272 +12:17:25 0.18386 +12:17:30 0.18335 +12:17:35 0.18347 +12:17:40 0.18327 +12:17:45 0.18279 +12:17:50 0.18208 +12:17:55 0.18080 +12:18:00 0.17834 +12:18:05 0.17804 +12:18:10 0.18084 +12:18:15 0.18084 +12:18:20 0.17781 +12:18:25 0.17810 +12:18:30 0.18083 +12:18:35 0.17748 +12:18:40 0.17834 +12:18:45 0.17727 +12:18:50 0.17616 +12:18:55 0.17637 +12:19:00 0.17572 +12:19:05 0.17637 +12:19:10 0.17446 +12:19:15 0.17425 +12:19:20 0.17554 +12:19:25 0.17587 +12:19:30 0.17406 +12:19:35 0.17334 +12:19:40 0.17426 +12:19:45 0.17520 +12:19:50 0.17546 +12:19:55 0.17407 +12:20:00 0.17176 +12:20:05 0.17265 +12:20:10 0.17367 +12:20:15 0.17416 +12:20:20 0.17396 +12:20:25 0.17122 +12:20:30 0.17141 +12:20:35 0.17216 +12:20:40 0.17265 +12:20:45 0.17193 +12:20:50 0.17296 +12:20:55 0.17496 +12:21:00 0.17498 +12:21:05 0.17169 +12:21:10 0.17248 +12:21:15 0.17191 +12:21:20 0.17071 +12:21:25 0.17148 +12:21:30 0.17218 +12:21:35 0.17191 +12:21:40 0.17350 +12:21:45 0.17271 +12:21:50 0.17143 +12:21:55 0.16946 +12:22:00 0.16722 +12:22:05 0.17004 +12:22:10 0.16908 +12:22:15 0.17020 +12:22:20 0.17186 +12:22:25 0.16888 +12:22:30 0.16917 +12:22:35 0.16968 +12:22:40 0.17129 +12:22:45 0.17212 +12:22:50 0.17214 +12:22:55 0.17007 +12:23:00 0.16836 +12:23:05 0.16840 +12:23:10 0.16907 +12:23:15 0.16930 +12:23:20 0.17108 +12:23:25 0.17082 +12:23:30 0.17012 +12:23:35 0.16980 +12:23:40 0.16841 +12:23:45 0.16726 +12:23:50 0.16810 +12:23:55 0.16807 +12:24:00 0.16786 +12:24:05 0.16824 +12:24:10 0.17097 +12:24:15 0.16806 +12:24:20 0.16672 +12:24:25 0.16929 +12:24:30 0.17059 +12:24:35 0.16944 +12:24:40 0.16668 +12:24:45 0.16582 +12:24:50 0.16586 +12:24:55 0.16804 +12:25:00 0.16883 +12:25:05 0.16870 +12:25:10 0.16860 +12:25:15 0.16684 +12:25:20 0.16697 +12:25:25 0.16794 +12:25:30 0.16783 +12:25:35 0.16733 +12:25:40 0.16689 +12:25:45 0.16924 +12:25:50 0.16883 +12:25:55 0.16911 +12:26:00 0.17011 +12:26:05 0.16709 +12:26:10 0.16440 +12:26:15 0.16596 +12:26:20 0.16702 +12:26:25 0.16762 +12:26:30 0.16814 +12:26:35 0.16771 +12:26:40 0.16758 +12:26:45 0.16637 +12:26:50 0.16649 +12:26:55 0.16576 +12:27:00 0.16567 +12:27:05 0.16692 +12:27:10 0.16817 +12:27:15 0.16816 +12:27:20 0.16906 +12:27:25 0.16941 +12:27:30 0.16469 +12:27:35 0.16572 +12:27:40 0.16886 +12:27:45 0.16713 +12:27:50 0.16799 +12:27:55 0.16760 +12:28:00 0.16586 +12:28:05 0.16565 +12:28:10 0.16531 +12:28:15 0.16712 +12:28:20 0.16831 +12:28:25 0.16740 +12:28:30 0.16653 +12:28:35 0.16407 +12:28:40 0.16696 +12:28:45 0.16742 +12:28:50 0.16743 +12:28:55 0.16777 +12:29:00 0.16637 +12:29:05 0.16657 +12:29:10 0.16613 +12:29:15 0.16702 +12:29:20 0.16690 +12:29:25 0.16756 +12:29:30 0.16612 +12:29:35 0.16630 +12:29:40 0.16498 +12:29:45 0.16655 +12:29:50 0.16509 +12:29:55 0.16424 +12:30:00 0.16584 +12:30:05 0.16422 +12:30:10 0.16488 +12:30:15 0.16468 +12:30:20 0.16543 +12:30:25 0.16447 +12:30:30 0.16706 +12:30:35 0.16532 +12:30:40 0.16521 +12:30:45 0.16788 +12:30:50 0.16791 +12:30:55 0.16719 +12:31:00 0.16615 +12:31:05 0.16737 +12:31:10 0.16957 +12:31:15 0.16640 +12:31:20 0.16549 +12:31:25 0.16514 +12:31:30 0.16700 +12:31:35 0.16567 +12:31:40 0.16594 +12:31:45 0.16675 +12:31:50 0.16676 +12:31:55 0.16810 +12:32:00 0.16789 +12:32:05 0.16683 +12:32:10 0.16825 +12:32:15 0.16701 +12:32:20 0.16531 +12:32:25 0.16763 +12:32:30 0.16915 +12:32:35 0.16811 +12:32:40 0.16758 +12:32:45 0.16837 +12:32:50 0.16728 +12:32:55 0.16736 +12:33:00 0.16781 +12:33:05 0.16719 +12:33:10 0.16745 +12:33:15 0.16911 +12:33:20 0.16840 +12:33:25 0.16591 +12:33:30 0.16562 +12:33:35 0.16626 +12:33:40 0.16697 +12:33:45 0.16656 +12:33:50 0.16501 +12:33:55 0.16548 +12:34:00 0.16733 +12:34:05 0.16640 +12:34:10 0.16576 +12:34:15 0.16563 +12:34:20 0.16349 +12:34:25 0.16511 +12:34:30 0.16449 +12:34:35 0.16445 +12:34:40 0.16785 +12:34:45 0.16735 +12:34:50 0.16633 +12:34:55 0.16691 +12:35:00 0.16678 +12:35:05 0.16664 +12:35:10 0.16561 +12:35:15 0.16550 +12:35:20 0.16545 +12:35:25 0.16554 +12:35:30 0.16576 +12:35:35 0.16480 +12:35:40 0.16581 +12:35:45 0.16489 +12:35:50 0.16631 +12:35:55 0.16682 +12:36:00 0.16606 +12:36:05 0.16608 +12:36:10 0.16748 +12:36:15 0.16726 +12:36:20 0.16726 +12:36:25 0.16763 +12:36:30 0.16650 +12:36:35 0.16437 +12:36:40 0.16410 +12:36:45 0.16233 +12:36:50 0.16389 +12:36:55 0.16657 +12:37:00 0.16791 +12:37:05 0.16757 +12:37:10 0.16549 +12:37:15 0.16536 +12:37:20 0.16706 +12:37:25 0.16711 +12:37:30 0.16784 +12:37:35 0.16858 +12:37:40 0.16604 +12:37:45 0.16487 +12:37:50 0.16393 +12:37:55 0.16650 +12:38:00 0.16613 +12:38:05 0.16659 +12:38:10 0.16630 +12:38:15 0.16691 +12:38:20 0.16714 +12:38:25 0.16875 +12:38:30 0.16663 +12:38:35 0.16463 +12:38:40 0.16635 +12:38:45 0.16470 +12:38:50 0.16550 +12:38:55 0.16756 +12:39:00 0.16735 +12:39:05 0.16743 +12:39:10 0.16630 +12:39:15 0.16601 +12:39:20 0.16742 +12:39:25 0.16488 +12:39:30 0.16529 +12:39:35 0.16502 +12:39:40 0.16419 +12:39:45 0.16576 +12:39:50 0.16612 +12:39:55 0.16614 +12:40:00 0.16651 +12:40:05 0.16622 +12:40:10 0.16518 +12:40:15 0.16642 +12:40:20 0.16724 +12:40:25 0.16888 +12:40:30 0.17002 +12:40:35 0.16705 +12:40:40 0.16443 +12:40:45 0.16496 +12:40:50 0.16588 +12:41:03 0.16597 +12:41:05 0.16633 +12:41:10 0.16829 +12:41:15 0.16658 +12:41:20 0.16543 +12:41:25 0.16479 +12:41:30 0.16722 +12:41:35 0.16915 +12:41:40 0.17190 +12:41:45 0.17187 +12:41:50 0.17233 +12:41:55 0.17193 +12:42:00 0.17260 +12:42:05 0.17296 +12:42:10 0.17336 +12:42:15 0.17228 +12:42:20 0.17204 +12:42:25 0.17205 +12:42:30 0.17356 +12:42:35 0.17191 +12:42:40 0.17303 +12:42:45 0.17523 +12:42:50 0.17202 +12:42:55 0.17201 +12:43:00 0.17191 +12:43:05 0.17225 +12:43:10 0.17215 +12:43:15 0.17165 +12:43:20 0.17106 +12:43:25 0.17173 +12:43:30 0.17116 +12:43:35 0.16903 +12:43:40 0.16805 +12:43:45 0.17000 +12:43:50 0.17148 +12:43:55 0.17189 +12:44:00 0.17325 +12:44:05 0.17133 +12:44:10 0.17066 +12:44:15 0.17095 +12:44:20 0.17147 +12:44:25 0.17083 +12:44:30 0.17106 +12:44:35 0.17198 +12:44:40 0.17020 +12:44:45 0.16957 +12:44:50 0.16958 +12:44:55 0.17149 +12:45:00 0.17087 +12:45:05 0.16999 +12:45:10 0.17000 +12:45:15 0.16946 +12:45:20 0.16974 +12:45:25 0.16970 +12:45:30 0.17029 +12:45:35 0.17129 +12:45:40 0.17081 +12:45:45 0.16911 +12:45:50 0.17047 +12:45:55 0.16942 +12:46:00 0.17009 +12:46:05 0.17074 +12:46:10 0.16971 +12:46:15 0.16926 +12:46:20 0.16956 +12:46:25 0.16780 +12:46:30 0.16769 +12:46:35 0.16937 +12:46:40 0.17070 +12:46:45 0.17150 +12:46:50 0.17022 +12:46:55 0.17115 +12:47:00 0.17010 +12:47:05 0.16870 +12:47:10 0.16951 +12:47:15 0.17081 +12:47:20 0.17127 +12:47:25 0.17136 +12:47:30 0.17005 +12:47:35 0.17012 +12:47:40 0.16997 +12:47:45 0.17256 +12:47:50 0.17229 +12:47:55 0.17022 +12:48:00 0.16860 +12:48:05 0.16884 +12:48:10 0.16972 +12:48:15 0.17053 +12:48:20 0.17089 +12:48:25 0.16936 +12:48:30 0.17126 +12:48:35 0.17293 +12:48:40 0.17029 +12:48:45 0.16701 +12:48:50 0.16833 +12:48:55 0.17218 +12:49:00 0.17212 +12:49:05 0.17145 +12:49:10 0.17046 +12:49:15 0.16905 +12:49:20 0.16949 +12:49:25 0.17103 +12:49:30 0.17171 +12:49:35 0.17154 +12:49:40 0.17064 +12:49:45 0.16880 +12:49:50 0.16894 +12:49:55 0.17009 +12:50:00 0.16962 +12:50:05 0.17074 +12:50:10 0.16984 +12:50:15 0.16755 +12:50:20 0.16833 +12:50:25 0.16859 +12:50:30 0.16935 +12:50:35 0.16855 +12:50:40 0.17144 +12:50:45 0.17259 +12:50:50 0.17259 +12:50:55 0.16946 +12:51:00 0.16941 +12:51:05 0.17033 +12:51:10 0.16934 +12:51:15 0.16906 +12:51:20 0.17040 +12:51:25 0.17123 +12:51:30 0.16983 +12:51:35 0.16774 +12:51:40 0.16944 +12:51:45 0.16716 +12:51:50 0.16651 +12:51:55 0.16765 +12:52:00 0.16863 +12:52:05 0.17000 +12:52:10 0.16979 +12:52:15 0.17139 +12:52:20 0.17045 +12:52:25 0.16960 +12:52:30 0.17219 +12:52:35 0.17328 +12:52:40 0.17109 +12:52:45 0.17001 +12:52:50 0.16912 +12:52:55 0.16970 +12:53:00 0.17020 +12:53:05 0.17018 +12:53:10 0.17023 +12:53:15 0.16906 +12:53:20 0.16994 +12:53:25 0.16909 +12:53:30 0.16992 +12:53:35 0.16858 +12:53:40 0.16923 +12:53:45 0.17082 +12:53:50 0.17090 +12:53:55 0.17100 +12:54:00 0.17180 +12:54:05 0.17271 +12:54:10 0.17214 +12:54:15 0.17190 +12:54:20 0.17035 +12:54:25 0.16961 +12:54:30 0.17157 +12:54:35 0.17314 +12:54:40 0.17135 +12:54:45 0.17098 +12:54:50 0.17007 +12:54:55 0.17098 +12:55:00 0.16912 +12:55:05 0.16869 +12:55:10 0.16809 +12:55:15 0.16840 +12:55:20 0.17040 +12:55:25 0.17081 +12:55:30 0.17120 +12:55:35 0.16993 +12:55:40 0.17137 +12:55:45 0.17048 +12:55:50 0.17036 +12:55:55 0.17057 +12:56:00 0.17067 +12:56:05 0.17005 +12:56:10 0.17242 +12:56:15 0.16972 +12:56:20 0.17101 +12:56:25 0.17157 +12:56:30 0.17031 +12:56:35 0.16906 +12:56:40 0.16921 +12:56:45 0.16922 +12:56:50 0.17215 +12:56:55 0.17062 +12:57:00 0.17167 +12:57:06 0.17293 +12:57:10 0.17011 +12:57:15 0.16947 +12:57:20 0.16934 +12:57:25 0.17120 +12:57:30 0.17013 +12:57:35 0.16917 +12:57:40 0.17000 +12:57:45 0.17029 +12:57:50 0.16919 +12:57:55 0.16805 +12:58:00 0.16869 +12:58:05 0.17253 +12:58:10 0.17194 +12:58:15 0.17042 +12:58:20 0.16863 +12:58:25 0.17030 +12:58:30 0.17248 +12:58:35 0.17002 +12:58:40 0.16747 +12:58:45 0.17191 +12:58:50 0.17105 +12:58:55 0.17030 +12:59:00 0.17243 +12:59:05 0.16989 +12:59:10 0.17047 +12:59:15 0.17127 +12:59:20 0.17053 +12:59:25 0.17100 +12:59:30 0.17137 +12:59:35 0.17005 +12:59:40 0.17153 +12:59:45 0.17100 +12:59:50 0.16898 +12:59:55 0.16877 +13:00:00 0.16969 +13:00:05 0.17042 +13:00:10 0.17211 +13:00:15 0.17025 +13:00:20 0.16993 +13:00:25 0.16917 +13:00:30 0.16963 +13:00:35 0.17077 +13:00:40 0.17096 +13:00:45 0.16990 +13:00:50 0.16958 +13:00:55 0.17004 +13:01:00 0.17113 +13:01:05 0.17006 +13:01:10 0.16838 +13:01:15 0.16798 +13:01:20 0.17018 +13:01:25 0.17075 +13:01:36 0.17041 +13:01:40 0.16961 +13:01:45 0.17040 +13:01:50 0.17035 +13:01:55 0.17111 +13:02:00 0.16994 +13:02:05 0.16971 +13:02:10 0.16919 +13:02:15 0.16985 +13:02:20 0.17191 +13:02:25 0.17140 +#period 5 exact 1 isdst 1 +13:02:30 +13:02:32 undefined +13:02:35 0.16961 +13:02:40 0.16945 +13:02:45 0.16898 +13:02:50 0.17194 +13:02:55 0.17203 +13:03:00 0.17090 +13:03:05 0.16941 +13:03:10 0.16938 +13:03:15 0.17127 +13:03:20 0.16868 +13:03:25 0.16685 +13:03:30 0.17044 +13:03:35 0.17033 +13:03:40 0.16883 +13:03:45 0.16870 +13:03:50 0.16834 +13:03:55 0.16866 +13:04:00 0.16796 +13:04:05 0.16947 +13:04:10 0.16968 +13:04:15 0.16833 +13:04:20 0.16826 +13:04:25 0.16863 +13:04:30 0.16905 +13:04:35 0.16720 +13:04:40 0.16625 +13:04:45 0.16721 +13:04:50 0.16612 +13:04:55 0.16629 +13:05:00 0.16907 +13:05:05 0.16946 +13:05:10 0.16713 +13:05:15 0.16851 +13:05:20 0.16901 +13:05:25 0.16770 +13:05:30 0.16922 +13:05:35 0.16773 +13:05:40 0.16787 +13:06:01 0.16824 +13:06:05 0.16749 +13:06:33 0.16764 +13:06:35 0.16850 +13:06:40 0.16905 +13:06:45 0.16828 +13:06:50 0.16694 +13:06:55 0.16857 +13:07:00 0.16601 +#period 5 exact 1 isdst 1 +13:07:06 +13:07:08 undefined +13:07:10 0.17018 +13:07:15 0.16628 +13:07:20 0.16709 +13:07:25 0.16646 +13:07:30 0.16505 +13:07:35 0.16710 +13:07:40 0.16544 +13:07:45 0.16825 +13:07:50 0.17227 +13:07:55 0.17411 +13:08:00 0.17251 +13:08:05 0.17249 +13:08:10 0.17160 +13:08:15 0.17400 +13:08:20 0.17340 +13:08:25 0.17109 +13:08:30 0.17206 +13:08:35 0.17242 +13:08:40 0.16953 +13:08:45 0.17057 +13:08:50 0.17080 +13:08:55 0.16985 +13:09:00 0.17148 +13:09:05 0.17258 +13:09:10 0.17202 +13:09:15 0.17125 +13:09:20 0.17098 +13:09:25 0.17167 +13:09:30 0.17156 +13:09:35 0.16973 +13:09:40 0.17168 +13:09:45 0.17064 +13:09:50 0.16770 +13:09:55 0.16862 +13:10:00 0.16948 +13:10:05 0.16830 +13:10:10 0.17019 +13:10:15 0.17189 +13:10:20 0.17232 +13:10:25 0.17118 +13:10:30 0.17022 +13:10:35 0.16962 +13:10:40 0.16996 +13:10:45 0.17309 +13:10:50 0.17238 +13:10:55 0.17085 +13:11:00 0.17038 +13:11:05 0.17081 +13:11:10 0.17179 +13:11:15 0.17083 +13:11:20 0.17061 +13:11:25 0.16986 +13:11:30 0.17154 +13:11:36 0.17122 +13:11:40 0.17242 +13:11:45 0.17146 +13:11:52 0.17084 +13:11:55 0.16807 +13:12:00 0.16878 +13:12:05 0.16965 +13:12:10 0.17142 +13:12:15 0.17043 +13:12:20 0.17007 +13:12:25 0.17030 +13:12:30 0.16939 +13:12:35 0.17003 +13:12:40 0.17069 +13:12:45 0.16971 +13:12:50 0.17087 +13:12:55 0.16932 +13:13:00 0.16974 +13:13:05 0.17009 +13:13:10 0.17117 +13:13:15 0.17025 +13:13:20 0.17112 +13:13:25 0.16892 +13:13:30 0.17046 +13:13:35 0.16943 +13:13:40 0.16908 +13:13:45 0.16970 +13:13:50 0.16991 +13:13:55 0.17048 +13:14:00 0.16939 +13:14:05 0.16937 +13:14:10 0.16822 +13:14:15 0.16776 +13:14:20 0.16925 +13:14:25 0.16878 +13:14:30 0.16868 +13:14:35 0.16683 +13:14:40 0.16860 +13:14:45 0.16993 +13:14:50 0.16916 +13:14:55 0.16858 +13:15:00 0.17018 +13:15:05 0.17160 +13:15:10 0.17176 +13:15:15 0.16945 +13:15:20 0.17059 +13:15:25 0.17194 +13:15:30 0.17228 +13:15:35 0.17107 +13:15:40 0.16990 +13:15:45 0.16804 +13:15:50 0.16878 +13:15:55 0.16936 +13:16:00 0.16923 +13:16:05 0.17072 +13:16:10 0.17079 +13:16:15 0.16991 +13:16:20 0.17098 +13:16:25 0.17077 +13:16:30 0.16931 +13:16:35 0.17011 +13:16:40 0.17093 +13:16:45 0.16852 +13:16:50 0.16685 +13:16:55 0.17080 +13:17:00 0.17003 +13:17:05 0.16824 +13:17:10 0.17036 +13:17:15 0.17121 +13:17:20 0.16901 +13:17:25 0.16920 +13:17:30 0.17034 +13:17:35 0.16925 +13:17:40 0.16954 +13:17:45 0.16979 +13:17:50 0.16842 +13:17:55 0.16913 +13:18:00 0.17033 +13:18:05 0.16942 +13:18:10 0.16927 +13:18:15 0.16965 +13:18:20 0.17072 +13:18:25 0.17081 +13:18:30 0.16878 +13:18:35 0.16849 +13:18:40 0.16906 +13:18:45 0.16978 +13:18:50 0.17065 +13:18:55 0.16952 +13:19:00 0.17285 +13:19:05 0.17239 +13:19:10 0.16909 +13:19:15 0.16886 +13:19:20 0.16987 +13:19:25 0.17015 +13:19:30 0.16913 +13:19:35 0.16951 +13:19:40 0.16887 +13:19:45 0.16814 +13:19:50 0.16991 +13:19:55 0.16936 +13:20:00 0.17138 +13:20:05 0.16963 +13:20:10 0.16979 +13:20:15 0.16874 +13:20:20 0.17020 +13:20:25 0.16983 +13:20:30 0.17088 +13:20:35 0.17070 +13:20:40 0.17009 +13:20:45 0.16879 +13:20:50 0.16755 +13:20:55 0.16779 +13:21:07 0.16919 +13:21:10 0.17003 +13:21:15 0.16891 +13:21:25 0.17083 +13:21:30 0.17019 +13:21:39 0.16888 +13:21:40 0.16948 +13:21:46 0.17054 +13:21:50 0.16989 +13:21:55 0.16745 +13:22:03 0.16879 +13:22:05 0.16966 +13:22:10 0.16923 +13:22:17 0.17088 +13:22:20 0.16836 +13:22:25 0.16805 +13:22:30 0.16837 +13:22:35 0.16677 +13:22:40 0.16803 +13:22:45 0.17088 +13:22:50 0.17014 +13:22:55 0.16876 +13:23:01 0.16877 +13:23:05 0.16957 +13:23:10 0.17060 +13:23:15 0.16844 +13:23:20 0.16930 +13:23:25 0.16815 +13:23:30 0.16645 +13:23:35 0.16850 +13:23:40 0.16830 +13:23:45 0.16912 +13:23:50 0.16890 +13:23:55 0.17178 +13:24:00 0.17248 +13:24:05 0.17216 +13:24:10 0.17213 +13:24:15 0.17670 +13:26:55 0.17670 +13:27:00 0.17698 +13:27:05 0.17711 +13:27:10 0.17738 +13:27:15 0.17884 +13:27:23 0.18748 +13:27:25 0.18717 +13:27:30 0.18209 +13:27:35 0.17551 +13:27:40 0.17145 +13:27:45 0.16991 +13:27:52 0.16461 +13:27:55 0.16565 +13:28:00 0.16950 +13:28:05 0.16527 +13:28:10 0.16973 +13:28:15 0.16447 +13:28:20 0.16096 +13:28:25 0.16224 +13:28:30 0.16901 +13:28:35 0.16590 +13:28:40 0.15760 +13:28:45 0.15738 +13:28:50 0.15536 +13:29:00 0.15971 +13:29:05 0.15667 +13:29:10 0.15590 +13:29:15 0.16156 +13:29:20 0.16147 +13:29:25 0.15904 +13:29:30 0.16192 +13:29:35 0.16236 +13:29:40 0.15791 +13:29:45 0.16440 +13:29:50 0.16307 +13:29:55 0.16639 +13:30:00 0.15786 +13:30:05 0.15923 +13:30:10 0.15471 +13:30:15 0.15761 +13:30:20 0.15708 +13:30:25 0.15905 +13:30:30 0.16344 +13:30:36 0.15675 +13:30:40 0.15264 +13:30:47 0.15270 +13:30:50 0.15478 +13:30:55 0.16107 +13:31:00 0.16781 +13:31:05 0.16402 +13:31:10 0.15810 +13:31:15 0.15550 +13:31:20 0.15648 +13:31:25 0.16440 +13:31:30 0.15813 +13:31:35 0.16189 +13:31:40 0.15355 +13:31:45 0.15782 +13:31:50 0.15822 +13:31:55 0.15695 +13:32:00 0.15562 +13:32:05 0.15600 +13:32:10 0.15832 +13:32:15 0.16088 +13:32:20 0.15847 +13:32:25 0.15991 +13:32:30 0.15635 +13:32:35 0.16079 +13:32:40 0.16014 +13:32:45 0.15929 +13:32:50 0.15751 +13:32:55 0.15782 +13:33:00 0.15591 +13:33:05 0.15729 +13:33:10 0.15574 +13:33:15 0.16319 +13:33:20 0.15527 +13:33:25 0.16070 +13:33:30 0.16396 +13:33:35 0.15524 +13:33:40 0.15581 +13:33:45 0.15331 +13:33:50 0.15960 +13:33:55 0.15923 +13:34:00 0.15694 +13:34:05 0.15627 +13:34:10 0.16303 +13:34:15 0.16244 +13:34:20 0.16105 +13:34:25 0.16186 +13:34:30 0.15468 +13:34:35 0.16119 +13:34:40 0.15480 +13:34:45 0.16179 +13:34:50 0.16174 +13:34:55 0.16252 +13:35:00 0.15568 +13:35:05 0.15771 +13:35:10 0.15818 +13:35:15 0.15813 +13:35:20 0.15601 +13:35:25 0.15565 +13:35:30 0.15791 +13:35:35 0.15855 +13:35:40 0.16231 +13:35:45 0.15308 +13:35:50 0.16210 +13:35:55 0.15676 +13:36:00 0.15505 +13:36:05 0.15574 +13:36:10 0.15444 +13:36:15 0.15714 +13:36:20 0.15427 +13:36:29 0.15533 +13:36:30 0.15823 +13:36:35 0.15654 +13:36:40 0.15967 +13:36:45 0.16103 +13:36:50 0.16069 +13:36:55 0.16245 +13:37:00 0.15846 +13:37:05 0.15410 +13:37:10 0.15790 +13:37:15 0.16438 +13:37:20 0.15626 +13:37:25 0.16028 +13:37:30 0.15657 +13:37:35 0.15854 +13:37:40 0.15441 +13:37:45 0.16417 +13:37:50 0.16192 +13:37:55 0.16755 +13:38:00 0.15580 +13:38:05 0.15580 +13:38:10 0.15563 +13:38:15 0.15520 +13:38:20 0.15540 +13:38:25 0.15348 +13:38:30 0.15579 +13:38:35 0.15645 +13:38:40 0.15767 +13:38:45 0.15707 +13:38:50 0.15301 +13:38:55 0.15804 +13:39:00 0.16147 +13:39:05 0.15843 +13:39:10 0.15897 +13:39:15 0.15840 +13:39:20 0.15366 +13:39:25 0.15438 +13:39:30 0.15983 +13:39:35 0.15944 +13:39:40 0.15479 +13:39:45 0.16169 +13:39:50 0.15395 +13:39:55 0.15564 +13:40:00 0.15225 +13:40:05 0.15655 +13:40:10 0.15721 +13:40:15 0.15065 +13:40:20 0.15673 +13:40:25 0.14899 +13:40:30 0.15467 +13:40:35 0.15818 +13:40:40 0.16559 +13:40:45 0.15694 +13:40:50 0.15839 +13:40:55 0.16370 +13:41:00 0.16477 +13:41:05 0.15744 +13:41:10 0.15403 +13:41:15 0.15584 +13:41:20 0.16130 +13:41:25 0.15488 +13:41:30 0.15879 +13:41:35 0.16157 +13:41:40 0.15426 +13:41:45 0.15830 +13:41:50 0.16092 +13:41:55 0.16017 +13:42:00 0.15878 +13:42:05 0.15673 +13:42:10 0.15612 +13:42:15 0.16204 +13:42:20 0.15767 +13:42:25 0.15518 +13:42:30 0.15441 +13:42:35 0.15466 +13:42:40 0.15947 +13:42:45 0.15578 +13:42:50 0.15308 +13:42:55 0.15618 +13:43:00 0.15858 +13:43:05 0.15642 +13:43:10 0.15540 +13:43:15 0.15818 +13:43:20 0.16102 +13:43:25 0.16497 +13:43:30 0.15631 +13:43:35 0.15772 +13:43:40 0.16198 +13:43:45 0.16265 +13:43:50 0.15636 +13:43:55 0.15847 +13:44:00 0.15487 +13:44:05 0.15723 +13:44:10 0.15401 +13:44:15 0.15416 +13:44:20 0.15882 +13:44:25 0.15994 +13:44:30 0.16299 +13:44:35 0.15974 +13:44:40 0.15967 +13:44:45 0.16412 +13:44:50 0.15464 +13:44:55 0.15609 +13:45:00 0.15822 +13:45:05 0.15684 +13:45:10 0.15404 +13:45:15 0.15192 +13:45:20 0.15077 +13:45:25 0.16113 +13:45:30 0.15858 +13:45:35 0.15885 +13:45:40 0.15769 +13:45:45 0.15259 +13:45:50 0.15496 +13:45:55 0.15775 +13:46:00 0.15885 +13:46:05 0.15881 +13:46:10 0.15537 +13:46:15 0.15495 +13:46:20 0.15336 +13:46:25 0.15300 +13:46:30 0.16676 +13:46:35 0.15952 +13:46:40 0.15519 +13:46:45 0.15425 +13:46:50 0.15140 +13:46:55 0.15244 +13:47:00 0.15164 +13:47:05 0.15577 +13:47:10 0.16006 +13:47:15 0.15516 +13:47:20 0.15368 +13:47:25 0.15887 +13:47:30 0.15966 +13:47:35 0.15314 +13:47:40 0.15636 +13:47:45 0.15515 +13:47:50 0.15745 +13:47:55 0.15327 +13:48:00 0.15312 +13:48:05 0.15372 +13:48:10 0.15587 +13:48:15 0.16069 +13:48:20 0.15856 +13:48:25 0.16313 +13:48:30 0.16347 +13:48:35 0.15901 +13:48:40 0.15241 +13:48:45 0.15674 +13:48:50 0.16314 +13:48:55 0.16475 +13:49:00 0.15921 +13:49:05 0.15758 +13:49:10 0.15477 +13:49:15 0.15122 +13:49:20 0.15348 +13:49:25 0.15055 +13:49:30 0.15150 +13:49:35 0.14807 +13:49:40 0.15193 +13:49:45 0.15561 +13:49:50 0.15405 +13:49:55 0.15491 +13:50:00 0.15479 +13:50:05 0.15308 +13:50:10 0.15616 +13:50:15 0.15880 +13:50:20 0.15403 +13:50:25 0.15545 +13:50:30 0.16036 +13:50:35 0.15506 +13:50:40 0.15406 +13:50:45 0.15230 +13:50:50 0.15301 +13:50:55 0.15608 +13:51:00 0.16314 +13:51:05 0.16482 +13:51:10 0.15504 +13:51:15 0.14897 +13:51:20 0.15338 +13:51:25 0.16191 +13:51:30 0.15958 +13:51:35 0.15498 +13:51:40 0.15679 +13:51:45 0.16010 +13:51:50 0.16052 +13:51:55 0.16214 +13:52:00 0.15889 +13:52:05 0.15303 +13:52:10 0.15368 +13:52:15 0.15814 +13:52:20 0.15845 +13:52:25 0.15826 +13:52:30 0.15815 +13:52:35 0.15857 +13:52:40 0.15942 +13:52:45 0.16220 +13:52:50 0.16362 +13:52:55 0.15838 +13:53:00 0.15771 +13:53:05 0.15590 +13:53:10 0.15878 +13:53:15 0.16084 +13:53:20 0.15630 +13:53:25 0.15379 +13:53:30 0.15305 +13:53:35 0.14820 +13:53:40 0.15387 +13:53:45 0.16189 +13:53:50 0.16235 +13:53:55 0.16847 +13:54:00 0.15823 +13:54:05 0.15132 +13:54:10 0.15320 +13:54:15 0.15679 +13:54:20 0.16128 +13:54:25 0.15859 +13:54:30 0.15606 +13:54:35 0.15344 +13:54:40 0.15424 +13:54:45 0.15545 +13:54:50 0.15846 +13:54:55 0.15692 +13:55:00 0.16054 +13:55:05 0.15718 +13:55:10 0.16181 +13:55:15 0.15596 +13:55:20 0.15808 +13:55:25 0.15706 +13:55:30 0.15933 +13:55:35 0.16558 +13:55:40 0.15604 +13:55:45 0.14879 +13:55:50 0.15633 +13:55:55 0.16189 +13:56:00 0.15535 +13:56:05 0.15347 +13:56:10 0.14894 +13:56:15 0.15314 +13:56:20 0.15700 +13:56:25 0.15889 +13:56:30 0.15127 +13:56:35 0.15765 +13:56:40 0.15548 +13:56:45 0.15216 +13:56:50 0.15362 +13:56:55 0.15360 +13:57:00 0.14620 +13:57:05 0.14955 +13:57:10 0.15740 +13:57:15 0.15442 +13:57:20 0.15938 +13:57:25 0.15866 +13:57:30 0.15868 +13:57:35 0.15542 +13:57:40 0.15381 +13:57:45 0.15708 +13:57:50 0.16142 +13:57:55 0.15780 +13:58:00 0.15464 +13:58:05 0.15476 +13:58:10 0.15718 +13:58:15 0.15782 +13:58:20 0.15996 +13:58:25 0.16345 +13:58:30 0.15711 +13:58:35 0.15284 +13:58:40 0.15065 +13:58:45 0.15378 +13:58:50 0.15303 +13:58:55 0.15797 +13:59:00 0.16215 +13:59:05 0.16014 +13:59:10 0.16201 +13:59:15 0.15971 +13:59:20 0.16175 +13:59:25 0.16074 +13:59:30 0.16122 +13:59:35 0.15715 +13:59:40 0.15513 +13:59:45 0.15546 +13:59:50 0.16014 +13:59:55 0.15992 +14:00:00 0.16061 +14:00:05 0.15941 +14:00:10 0.15571 +14:00:15 0.15057 +14:00:20 0.15410 +14:00:25 0.15692 +14:00:30 0.16004 +14:00:35 0.16792 +14:00:40 0.16303 +14:00:45 0.15769 +14:00:50 0.15909 +14:00:55 0.15617 +14:01:00 0.15802 +14:01:05 0.15589 +14:01:14 0.15559 +14:01:15 0.15559 +14:01:20 0.15652 +14:01:25 0.15729 +14:01:30 0.16341 +14:01:35 0.15561 +14:01:40 0.14942 +14:01:45 0.16009 +14:01:50 0.16436 +14:01:55 0.15884 +14:02:00 0.15882 +14:02:05 0.15491 +14:02:10 0.15566 +14:02:15 0.15410 +14:02:20 0.15053 +14:02:25 0.15540 +14:02:30 0.15887 +14:02:35 0.15906 +14:02:40 0.16310 +14:02:45 0.15313 +14:02:50 0.15593 +14:02:55 0.15595 +14:03:00 0.16020 +14:03:05 0.15867 +14:03:10 0.15602 +14:03:15 0.15852 +14:03:20 0.15876 +14:03:25 0.15372 +14:03:30 0.15532 +14:03:35 0.14972 +14:03:40 0.15123 +14:03:45 0.15268 +14:03:50 0.15505 +14:03:55 0.15555 +14:04:00 0.15793 +14:04:05 0.15638 +14:04:10 0.15151 +14:04:15 0.15293 +14:04:20 0.15168 +14:04:25 0.15615 +14:04:30 0.15618 +14:04:35 0.15629 +14:04:40 0.15565 +14:04:45 0.15305 +14:04:50 0.15867 +14:04:55 0.15600 +14:05:00 0.15395 +14:05:05 0.15512 +14:05:10 0.15928 +14:05:15 0.16120 +14:05:20 0.15480 +14:05:25 0.15436 +14:05:30 0.15851 +14:05:35 0.15500 +14:05:40 0.15609 +14:05:45 0.15579 +14:05:50 0.15773 +14:05:55 0.15955 +14:06:00 0.15358 +14:06:05 0.15607 +14:06:10 0.15945 +14:06:15 0.15886 +14:06:20 0.16407 +14:06:25 0.15947 +14:06:30 0.15587 +14:06:35 0.15901 +14:06:40 0.15552 +14:06:45 0.15570 +14:06:50 0.16119 +14:06:55 0.15811 +14:07:00 0.15512 +14:07:05 0.15735 +14:07:10 0.15098 +14:07:15 0.15158 +14:07:20 0.15634 +14:07:25 0.15568 +14:07:30 0.15239 +14:07:35 0.16040 +14:07:40 0.15708 +14:07:45 0.16067 +14:07:50 0.16128 +14:07:55 0.15798 +14:08:00 0.15416 +14:08:05 0.15632 +14:08:10 0.16083 +14:08:15 0.16430 +14:08:20 0.15546 +14:08:25 0.15473 +14:08:30 0.15280 +14:08:35 0.15266 +14:08:40 0.15706 +14:08:45 0.15607 +14:08:50 0.15674 +14:08:55 0.15789 +14:09:00 0.15476 +14:09:05 0.15638 +14:09:10 0.16471 +14:09:15 0.15893 +14:09:20 0.15499 +14:09:25 0.16237 +14:09:30 0.16163 +14:09:35 0.16158 +14:09:40 0.15589 +14:09:45 0.15782 +14:09:50 0.16190 +14:09:55 0.15579 +14:10:00 0.15959 +14:10:05 0.16088 +14:10:10 0.15538 +14:10:15 0.16015 +14:10:20 0.15317 +14:10:25 0.15347 +14:10:30 0.15172 +14:10:35 0.15541 +14:10:40 0.15531 +14:10:45 0.16080 +14:10:50 0.15850 +14:10:55 0.15483 +14:11:00 0.15525 +14:11:05 0.15587 +14:11:10 0.16014 +14:11:15 0.16037 +14:11:20 0.15471 +14:11:25 0.15468 +14:11:30 0.15693 +14:11:35 0.15578 +14:11:40 0.15762 +14:11:45 0.16030 +14:11:50 0.15710 +14:11:55 0.15185 +14:12:00 0.15749 +14:12:05 0.15434 +14:12:10 0.16149 +14:12:15 0.15955 +14:12:20 0.15518 +14:12:25 0.15625 +14:12:30 0.15635 +14:12:35 0.16165 +14:12:40 0.16037 +14:12:45 0.16260 +14:12:50 0.16008 +14:12:55 0.15674 +14:13:00 0.15469 +14:13:05 0.15361 +14:13:10 0.16202 +14:13:15 0.15663 +14:13:20 0.16224 +14:13:25 0.16374 +14:13:30 0.15420 +14:13:35 0.15573 +14:13:40 0.15660 +14:13:45 0.15721 +14:13:50 0.15214 +14:13:55 0.15493 +14:14:00 0.15992 +14:14:05 0.15460 +14:14:10 0.15397 +14:14:15 0.15975 +14:14:20 0.15309 +14:14:25 0.16019 +14:14:30 0.15858 +14:14:35 0.15617 +14:14:40 0.15724 +14:14:45 0.15765 +14:14:50 0.15196 +14:14:55 0.15928 +14:15:00 0.15934 +14:15:05 0.15683 +14:15:10 0.16168 +14:15:15 0.16156 +14:15:20 0.15372 +14:15:25 0.15673 +14:15:30 0.15563 +14:15:35 0.15123 +14:15:40 0.15079 +14:15:45 0.15237 +14:15:50 0.15699 +14:15:55 0.15886 +14:16:00 0.15827 +14:16:05 0.15838 +14:16:10 0.15600 +14:16:15 0.15679 +14:16:25 0.16225 +14:16:30 0.15933 +14:16:35 0.15388 +14:16:40 0.15906 +14:16:45 0.15541 +14:16:50 0.15509 +14:16:55 0.15687 +14:17:00 0.15256 +14:17:05 0.15621 +14:17:10 0.15507 +14:17:15 0.15627 +14:17:20 0.15783 +14:17:25 0.15454 +14:17:30 0.15960 +14:17:35 0.16179 +14:17:40 0.16288 +14:17:45 0.16232 +14:17:50 0.16373 +14:17:55 0.16898 +14:18:00 0.16840 +14:18:05 0.16562 +14:18:10 0.17473 +14:18:15 0.17406 +14:18:20 0.17487 +14:18:25 0.17801 +14:18:30 0.18845 +14:18:35 0.18892 +14:18:40 0.21422 +14:18:45 0.41001 +14:18:50 0.78100 +14:18:55 0.87237 +14:19:00 1.2188 +14:19:05 1.4900 +14:19:10 1.5785 +14:19:15 1.2923 +14:19:20 1.0776 +14:19:25 1.2900 +14:19:30 1.4591 +14:19:35 1.4109 +14:19:40 1.3475 +14:19:45 1.2398 +14:19:50 1.3375 +14:19:55 1.5821 +14:20:00 1.5364 +14:20:05 4.8250 +14:20:10 3.1921 +14:20:15 1.4507 +14:20:20 1.3861 +14:20:25 1.3782 +14:20:30 1.4600 +14:20:35 2.1113 +14:20:40 2.0152 +14:20:45 2.1944 +14:20:50 1.9211 +14:20:55 1.9631 +14:21:00 2.9701 +14:21:05 1.9396 +14:21:10 1.8230 +14:21:15 2.9968 +14:21:20 2.9712 +14:21:25 2.0347 +14:21:30 1.6284 +14:21:35 2.1419 +14:21:40 3.0369 +14:21:45 2.1591 +14:21:50 2.0545 +14:21:55 2.7250 +14:22:00 1.9995 +14:22:05 1.6586 +14:22:13 2.1303 +14:22:15 2.1813 +14:22:20 2.5220 +14:22:25 2.6809 +14:22:30 1.5945 +14:22:35 1.4464 +14:22:40 1.4437 +14:22:45 1.3317 +14:22:50 1.6946 +14:22:55 1.6581 +14:23:00 1.7462 +14:23:05 3.0054 +14:23:10 2.0527 +14:23:15 2.2458 +14:23:20 1.8125 +14:23:25 1.2014 +14:23:30 1.3047 +14:23:35 1.2093 +14:23:40 1.2969 +14:23:45 1.6198 +14:23:50 2.2268 +14:23:55 2.3651 +14:24:00 2.1144 +14:24:05 1.4402 +14:24:10 2.1089 +14:24:15 2.0186 +14:24:20 2.1608 +14:24:25 2.1114 +14:24:30 2.7092 +14:24:35 2.6155 +14:24:40 3.4728 +14:24:45 2.1291 +14:24:50 1.8464 +14:24:55 2.0649 +14:25:00 2.0490 +14:25:05 1.5565 +14:25:10 2.1299 +14:25:15 4.1631 +14:25:20 2.8430 +14:25:25 2.7431 +14:25:30 3.4791 +14:25:35 2.5467 +14:25:40 2.0791 +14:25:45 3.8225 +14:25:50 3.8369 +14:25:55 2.6653 +14:26:00 2.4913 +14:26:05 3.0326 +14:26:10 2.2092 +14:26:15 1.8703 +14:26:20 2.1218 +14:26:25 1.9838 +14:26:30 1.9627 +14:26:35 1.8702 +14:26:40 2.3995 +14:26:45 2.0044 +14:26:50 1.9100 +14:26:55 1.9704 +14:27:00 2.2946 +14:27:05 2.2029 +14:27:10 2.3654 +14:27:15 2.1168 +14:27:20 1.6774 +14:27:25 1.8196 +14:27:30 1.7340 +14:27:35 2.2993 +14:27:40 2.5057 +14:27:45 2.4950 +14:27:50 3.1228 +14:27:55 3.1616 +14:28:00 2.9337 +14:28:05 2.9027 +14:28:10 2.6826 +14:28:15 1.9078 +14:28:20 1.5077 +14:28:25 1.6715 +14:28:30 2.0773 +14:28:35 1.9607 +14:28:40 2.8687 +14:28:45 2.2899 +14:28:50 2.6356 +14:28:55 3.1693 +14:29:00 2.6068 +14:29:05 1.8361 +14:29:10 2.1318 +14:29:15 2.1678 +14:29:20 1.7904 +14:29:25 1.8703 +14:29:30 3.1321 +14:29:35 3.3451 +14:29:40 2.5354 +14:29:45 1.9244 +14:29:50 2.0543 +14:29:55 1.8395 +14:30:00 1.5418 +14:30:05 2.1691 +14:30:10 2.3013 +14:30:15 1.8439 +14:30:20 1.8641 +14:30:25 2.7176 +14:30:30 2.5047 +14:30:35 2.1262 +14:30:40 1.4688 +14:30:45 1.6228 +14:30:50 1.7463 +14:30:55 1.7207 +14:31:00 2.0140 +14:31:05 1.5982 +14:31:10 1.8905 +14:31:15 2.1515 +14:31:20 2.6844 +14:31:25 3.1188 +14:31:30 2.1709 +14:31:35 2.5886 +14:31:40 3.1698 +14:31:45 2.5839 +14:31:50 2.7280 +14:31:55 4.0561 +14:32:00 1.6991 +14:32:05 1.5750 +14:32:10 1.3896 +14:32:15 1.7427 +14:32:20 1.4348 +14:32:25 1.4334 +14:32:30 1.5415 +14:32:35 2.4559 +14:32:40 2.5026 +14:32:45 2.2687 +14:32:50 3.3489 +14:32:55 3.8634 +14:33:00 2.7512 +14:33:05 1.8408 +14:33:10 2.3280 +14:33:15 2.3215 +14:33:20 2.7826 +14:33:25 2.6021 +14:33:30 3.6104 +14:33:35 2.0327 +14:33:40 2.1239 +14:33:45 2.1737 +14:33:50 2.0805 +14:33:55 2.5848 +14:34:00 3.0732 +14:34:05 2.5781 +14:34:10 2.1309 +14:34:15 2.1244 +14:34:20 2.3401 +14:34:25 2.1999 +14:34:30 2.9446 +14:34:35 3.4992 +14:34:40 3.9875 +14:34:45 3.0706 +14:34:50 4.7272 +14:34:58 5.1302 +14:35:05 4.0056 +14:35:10 4.4945 +14:35:17 5.0313 +14:35:20 5.0499 +14:35:25 5.1350 +14:35:30 5.0106 +14:35:35 3.9304 +14:35:40 4.1074 +14:35:46 4.4212 +14:35:54 3.6196 +14:35:55 4.5012 +14:36:00 4.7160 +14:36:05 4.8141 +14:36:10 4.1347 +14:36:15 4.7422 +14:36:20 4.2952 +14:36:25 4.7712 +14:36:31 4.6552 +14:36:35 4.2307 +14:36:40 4.0650 +14:36:45 4.0060 +14:36:52 4.7759 +14:36:59 5.0065 +14:37:01 4.8188 +14:37:07 4.2559 +14:37:10 5.0478 +14:37:15 4.9210 +14:37:20 3.4581 +14:37:25 3.8950 +14:37:30 4.8515 +14:37:35 4.3551 +14:37:40 3.9469 +14:37:45 4.8318 +14:37:50 4.1543 +14:37:55 4.5691 +14:38:00 3.8149 +14:38:05 4.9151 +14:38:10 3.9805 +14:38:15 4.4270 +14:38:20 3.5962 +14:38:25 3.6687 +14:38:30 4.4751 +14:38:35 3.0519 +14:38:40 3.4429 +14:38:45 3.2280 +14:38:50 3.1266 +14:38:55 3.4860 +14:39:03 4.3663 +14:39:05 3.7392 +14:39:10 2.6529 +14:39:15 3.0949 +14:39:20 3.7387 +14:39:25 3.5572 +14:39:30 4.2081 +14:39:35 4.1362 +14:39:40 4.5150 +14:39:45 4.9422 +14:39:50 5.1242 +14:39:55 5.1242 +14:40:00 5.0244 +14:40:05 4.9493 +14:40:10 3.7043 +14:40:15 3.8544 +14:40:20 4.4121 +14:40:25 5.0255 +14:40:30 4.8742 +14:40:35 4.6467 +14:40:40 4.6296 +14:40:45 3.7527 +14:40:50 3.7917 +14:40:55 3.4857 +14:41:00 4.0162 +14:41:05 4.1214 +14:41:10 3.8011 +14:41:15 4.1370 +14:41:20 4.1956 +14:41:25 3.5047 +14:41:30 4.0278 +14:41:35 5.0463 +14:41:40 5.0872 +14:41:45 4.9564 +14:41:50 4.3404 +14:41:55 3.1634 +14:42:00 4.5703 +14:42:05 4.0812 +14:42:10 4.2885 +14:42:15 3.9365 +14:42:20 5.0816 +14:42:25 4.6555 +14:42:30 3.6089 +14:42:35 3.4086 +14:42:40 4.2345 +14:42:45 4.5008 +14:42:50 4.7698 +14:42:55 4.6087 +14:43:00 5.0890 +14:43:05 3.9844 +14:43:10 3.3929 +14:43:15 3.5147 +14:43:20 4.4961 +14:43:25 4.7387 +14:43:30 4.7217 +14:43:35 5.0412 +14:43:40 5.0206 +14:43:45 4.9618 +14:43:50 4.5130 +14:43:55 4.0343 +14:44:00 3.1452 +14:44:05 3.2401 +14:44:10 3.0429 +14:44:15 3.8125 +14:44:20 3.9854 +14:44:25 4.0588 +14:44:30 4.2503 +14:44:35 4.1770 +14:44:40 4.2310 +14:44:45 4.3937 +14:44:50 2.9416 +14:44:55 3.2820 +14:45:00 2.9899 +14:45:05 3.0242 +14:45:10 2.9708 +14:45:15 2.7438 +14:45:20 3.0548 +14:45:25 2.7919 +14:45:30 3.6389 +14:45:35 3.2412 +14:45:40 3.3260 +14:45:45 4.0548 +14:45:50 3.4130 +14:45:55 3.5946 +14:46:00 2.8852 +14:46:05 3.7608 +14:46:10 4.7485 +14:46:15 4.9187 +14:46:20 4.4179 +14:46:25 4.2325 +14:46:30 4.8619 +14:46:35 4.4754 +14:46:40 3.5703 +14:46:45 4.5403 +14:46:50 3.4993 +14:46:55 3.0152 +14:47:00 3.1246 +14:47:05 3.1586 +14:47:10 4.0283 +14:47:15 3.6102 +14:47:20 4.5847 +14:47:25 4.2917 +14:47:30 3.5848 +14:47:35 3.8719 +14:47:40 4.6561 +14:47:45 4.2296 +14:47:50 3.5808 +14:47:55 4.2895 +14:48:00 4.0953 +14:48:05 3.1817 +14:48:10 4.4750 +14:48:15 4.3748 +14:48:20 3.0079 +14:48:25 3.8458 +14:48:30 3.4098 +14:48:35 4.1065 +14:48:40 4.9178 +14:48:45 4.4329 +14:48:50 3.1905 +14:48:55 3.3332 +14:49:00 3.5097 +14:49:05 4.5185 +14:49:10 4.2362 +14:49:15 3.9386 +14:49:20 2.0419 +14:49:25 3.1712 +14:49:30 4.4247 +14:49:35 4.9970 +14:49:40 4.8842 +14:49:45 5.1506 +14:49:50 4.3711 +14:49:55 3.5476 +14:50:00 3.9071 +14:50:05 3.8639 +14:50:10 3.5371 +14:50:15 4.4881 +14:50:20 4.4470 +14:50:25 4.3819 +14:50:30 4.4402 +14:50:35 4.6129 +14:50:40 3.4963 +14:50:45 3.7861 +14:50:50 4.5448 +14:50:55 4.2310 +14:51:00 3.8084 +14:51:05 3.9762 +14:51:10 3.9739 +14:51:15 3.5739 +14:51:20 3.9396 +14:51:25 4.2651 +14:51:30 4.4844 +14:51:35 3.9608 +14:51:40 3.6818 +14:51:45 3.4129 +14:51:50 3.7100 +14:51:55 3.5340 +14:52:00 3.8764 +14:52:05 4.6628 +14:52:10 4.8502 +14:52:15 4.9944 +14:52:20 4.2582 +14:52:25 4.5458 +14:52:30 5.0109 +14:52:35 4.8235 +14:52:40 4.8607 +14:52:45 4.8896 +14:52:50 4.6132 +14:52:55 3.6442 +14:53:00 4.4602 +14:53:05 4.9746 +14:53:10 3.1687 +14:53:15 4.3439 +14:53:20 4.1353 +14:53:25 4.0062 +14:53:30 4.0938 +14:53:35 4.2403 +14:53:40 3.5565 +14:53:45 3.6352 +14:53:50 5.0975 +14:53:55 4.7209 +14:54:00 4.6470 +14:54:05 5.1167 +14:54:10 4.2715 +14:54:15 4.9665 +14:54:20 4.0763 +14:54:25 4.2809 +14:54:30 4.4555 +14:54:35 4.6470 +14:54:40 5.0003 +14:54:45 4.7497 +14:54:50 4.5890 +14:54:55 4.8740 +14:55:00 4.0362 +14:55:05 4.9024 +14:55:10 3.7485 +14:55:15 3.1337 +14:55:20 3.9894 +14:55:25 4.7961 +14:55:30 4.5765 +14:55:35 4.7683 +14:55:40 3.7457 +14:55:45 3.2225 +14:55:50 2.4384 +14:55:55 2.8683 +14:56:00 2.7061 +14:56:05 4.0842 +14:56:10 2.5299 +14:56:15 4.4479 +14:56:20 2.8100 +14:56:25 4.7457 +14:56:30 3.2385 +14:56:35 3.7688 +14:56:40 4.5341 +14:56:45 4.6938 +14:56:50 5.0368 +14:56:55 5.0211 +14:57:00 3.7509 +14:57:05 3.8207 +14:57:10 3.7379 +14:57:15 3.2405 +14:57:20 2.5792 +14:57:25 3.2422 +14:57:30 3.6622 +14:57:35 3.2560 +14:57:40 4.6923 +14:57:45 3.6954 +14:57:50 3.5134 +14:57:56 4.3686 +14:58:00 3.9063 +14:58:05 4.6352 +14:58:10 4.9259 +14:58:15 4.6910 +14:58:20 4.4678 +14:58:25 4.5679 +14:58:30 3.3506 +14:58:35 4.0658 +14:58:40 3.6075 +14:58:45 2.3810 +14:58:50 3.2624 +14:58:55 4.6035 +14:59:00 4.4081 +14:59:13 4.9171 +14:59:17 4.0662 +14:59:20 4.1487 +14:59:25 4.7783 +14:59:30 4.6604 +14:59:36 4.9341 +14:59:40 4.6574 +14:59:45 4.9124 +14:59:50 4.4971 +14:59:55 4.3894 +15:00:00 5.1211 +15:00:05 3.9982 +15:00:10 4.8046 +15:00:15 4.7012 +15:00:20 3.8995 +15:00:26 3.0856 +15:00:30 3.3430 +15:00:35 4.5266 +15:00:40 4.3647 +15:00:45 4.6591 +15:00:50 4.7625 +15:00:55 4.6689 +15:01:00 4.7110 +15:01:05 3.8538 +15:01:10 3.5960 +15:01:16 3.5279 +15:01:20 2.6005 +15:01:25 4.5662 +15:01:30 3.8245 +15:01:38 4.2261 +15:01:40 3.8425 +15:01:45 4.7769 +15:01:50 4.5973 +15:01:56 4.4149 +15:02:00 3.8324 +15:02:07 3.4295 +15:02:10 4.6343 +15:02:15 4.0480 +15:02:20 5.0389 +15:02:25 3.0484 +15:02:30 4.0833 +15:02:35 5.1093 +15:02:40 3.9803 +15:02:45 4.9780 +15:02:50 3.2970 +15:02:55 4.4228 +15:03:00 2.8577 +15:03:05 3.5472 +15:03:10 3.6960 +15:03:15 4.1321 +15:03:20 3.5264 +15:03:25 4.5213 +15:03:30 4.6481 +15:03:35 4.2143 +15:03:40 4.1998 +15:03:45 3.4891 +15:03:51 4.5291 +15:03:58 4.0335 +15:04:00 3.4658 +15:04:05 3.6556 +15:04:11 2.9325 +15:04:15 3.1396 +15:04:20 4.3290 +15:04:26 3.6793 +15:04:30 3.9021 +15:04:35 3.7590 +15:04:40 4.5041 +15:04:47 4.1426 +15:04:51 3.3112 +15:04:57 3.7590 +15:05:02 4.3033 +15:05:05 4.4554 +15:05:12 4.0625 +15:05:15 4.0331 +15:05:22 4.6265 +15:05:28 4.3487 +15:05:30 3.1857 +15:05:36 2.9882 +15:05:42 3.5770 +15:05:45 4.0475 +15:05:51 4.4456 +15:06:00 4.0065 +15:06:05 2.7791 +15:06:10 4.2369 +15:06:15 3.0891 +15:06:20 3.8759 +15:06:25 3.9173 +15:06:31 4.2046 +15:06:35 3.4916 +15:06:40 3.3164 +15:06:45 3.6887 +15:06:50 3.2659 +15:06:55 3.9265 +15:07:00 3.1595 +15:07:05 3.9887 +15:07:10 3.9594 +15:07:15 3.1072 +15:07:20 4.7449 +15:07:25 3.8658 +15:07:30 4.2975 +15:07:35 4.5659 +15:07:40 4.2452 +15:07:45 4.2052 +15:07:50 3.2710 +15:07:55 2.8941 +15:08:00 3.9788 +15:08:05 4.7096 +15:08:10 3.3579 +15:08:15 4.6452 +15:08:20 3.4665 +15:08:25 4.3437 +15:08:30 4.4395 +15:08:35 5.0097 +15:08:40 4.9457 +15:08:45 3.9214 +15:08:50 4.7544 +15:08:55 3.4557 +15:09:00 3.6499 +15:09:05 3.7397 +15:09:10 3.0223 +15:09:15 3.5375 +15:09:20 4.4060 +15:09:25 4.9537 +15:09:30 4.1028 +15:09:36 4.7996 +15:09:40 4.5860 +15:09:45 4.3259 +15:09:50 4.3007 +15:09:55 4.2802 +15:10:00 3.2956 +15:10:05 3.1497 +15:10:10 3.6630 +15:10:16 4.2005 +15:10:20 3.7970 +15:10:26 3.6850 +15:10:32 4.0624 +15:10:35 3.3703 +15:10:40 3.5958 +15:10:45 4.1215 +15:10:50 4.3751 +15:10:56 3.7933 +15:11:00 3.6074 +15:11:05 3.5466 +15:11:10 4.2689 +15:11:16 4.1222 +15:11:21 4.2462 +15:11:26 4.1700 +15:11:40 3.4369 +15:11:45 3.6291 +15:11:51 4.8817 +15:11:57 4.7252 +15:12:00 4.6008 +15:12:05 3.3738 +15:12:10 3.7236 +15:12:15 3.9230 +15:12:20 3.7132 +15:12:25 4.4135 +15:12:30 4.7577 +15:12:35 3.9632 +15:12:40 4.7232 +15:12:45 3.9490 +15:12:50 3.9311 +15:12:55 3.8749 +15:13:00 4.8197 +15:13:05 4.8255 +15:13:10 4.1803 +15:13:15 3.5291 +15:13:21 3.3685 +15:13:25 4.1323 +15:13:30 4.2274 +15:13:35 4.0041 +15:13:41 3.8375 +15:13:46 4.2266 +15:13:54 4.3074 +15:13:55 3.5857 +15:14:00 3.4089 +15:14:05 3.5144 +15:14:11 4.0357 +15:14:17 4.3330 +15:14:20 3.9776 +15:14:27 4.6330 +15:14:30 4.5952 +15:14:35 3.9972 +15:14:42 3.1962 +15:14:45 3.7487 +15:14:55 4.2996 +15:15:06 4.2906 +15:15:13 5.0396 +15:15:18 4.3532 +15:15:20 3.1873 +15:15:25 1.8356 +15:15:34 4.6100 +15:15:35 2.7322 +15:15:40 3.6042 +15:15:45 4.2982 +15:15:50 3.6596 +15:15:55 3.7637 +15:16:00 3.8533 +15:16:48 3.8691 +15:16:54 4.0854 +15:16:55 3.9145 +15:17:00 3.8565 +15:17:05 4.0900 +15:17:10 4.2668 +15:17:15 4.5612 +15:17:20 4.6264 +15:17:25 4.7419 +15:17:30 3.3621 +15:17:35 2.8598 +15:17:41 4.5943 +15:17:45 4.6145 +15:17:50 3.0591 +15:17:55 3.5438 +15:18:00 3.7712 +15:18:05 3.1788 +15:18:10 3.4818 +15:18:15 3.8744 +15:18:20 3.7498 +15:18:25 4.3014 +15:18:30 3.9566 +15:18:35 2.6596 +15:18:40 3.6802 +15:18:45 3.7579 +15:18:50 3.9190 +15:18:57 3.8695 +15:19:00 2.6564 +15:19:06 4.6534 +15:19:10 4.4296 +15:19:15 4.0382 +15:19:20 4.9317 +15:19:25 4.5007 +15:19:32 3.6640 +15:19:35 4.8829 +15:19:42 4.4323 +15:19:45 4.2974 +15:19:50 4.7321 +15:19:55 4.1828 +15:20:00 4.0782 +15:20:05 4.2462 +15:20:10 4.2712 +15:20:15 3.9919 +15:20:20 3.6666 +15:20:25 2.9239 +15:20:30 3.7230 +15:20:35 5.0828 +15:20:40 4.5134 +15:20:45 4.4724 +15:20:50 4.7821 +15:20:55 3.4196 +15:21:00 2.7891 +15:21:10 2.7570 +15:21:15 3.9705 +15:21:20 3.9389 +15:21:25 4.6344 +15:21:30 4.8470 +15:21:35 4.9319 +15:21:40 4.9101 +15:21:45 4.2418 +15:21:50 3.7708 +15:21:55 3.6534 +15:22:00 3.0165 +15:22:05 3.1831 +15:22:10 4.1335 +15:22:15 4.6802 +15:22:20 4.6883 +15:22:25 4.7305 +15:22:30 3.5468 +15:22:35 4.4628 +15:22:40 4.9027 +15:22:45 4.1616 +15:22:50 3.7034 +15:22:55 3.6235 +15:23:00 3.6344 +15:23:05 4.1065 +15:23:10 3.6308 +15:23:15 4.4060 +15:23:20 4.9768 +15:23:25 4.7487 +15:23:30 4.5619 +15:23:35 4.1228 +15:23:40 4.0845 +15:23:45 4.4343 +15:23:50 4.7816 +15:23:55 4.6888 +15:24:00 4.1218 +15:24:05 4.2811 +15:24:10 4.9119 +15:24:15 4.7081 +15:24:20 3.8448 +15:24:25 4.2118 +15:24:30 4.3141 +15:24:35 2.8787 +15:24:40 2.3096 +15:24:45 3.0453 +15:24:50 3.1223 +15:24:55 3.4361 +15:25:00 4.6786 +15:25:05 4.8238 +15:25:10 3.9141 +15:25:15 3.0532 +15:25:20 3.5844 +15:25:25 4.1636 +15:25:30 4.6639 +15:25:41 4.2526 +15:25:45 3.5818 +15:25:50 4.3284 +15:25:55 3.0972 +15:26:00 3.3592 +15:26:05 2.6707 +15:26:10 2.5910 +15:26:15 3.0275 +15:26:20 3.3105 +15:26:25 4.1370 +15:26:30 3.3704 +15:26:35 2.3095 +15:26:40 3.1479 +15:26:45 3.4726 +15:26:50 4.0446 +15:26:55 4.5630 +15:27:00 3.5423 +15:27:05 3.9095 +15:27:10 4.5574 +15:27:16 3.7128 +15:27:20 3.6147 +15:27:25 2.3083 +15:27:30 3.8030 +15:27:35 3.9167 +15:27:40 3.6425 +15:27:45 3.5450 +15:27:50 3.5685 +15:27:55 3.5555 +15:28:00 3.4802 +15:28:05 3.4802 +15:28:10 3.7658 +15:28:15 3.4044 +15:28:20 3.4830 +15:28:25 3.8128 +15:28:30 4.8181 +15:28:35 4.7007 +15:28:40 4.6712 +15:28:45 4.4058 +15:28:50 5.0051 +15:28:55 5.0291 +15:29:00 3.9822 +15:29:05 3.4761 +15:29:10 4.2317 +15:29:15 5.1026 +15:29:20 4.3960 +15:29:25 4.2040 +15:29:30 3.9823 +15:29:35 4.3102 +15:29:40 4.2233 +15:29:45 4.0966 +15:29:50 4.3017 +15:29:55 4.2482 +15:30:00 3.7821 +15:30:05 3.7102 +15:30:10 3.8120 +15:30:15 3.7733 +15:30:20 3.7787 +15:30:25 3.7787 +15:30:30 3.7234 +15:30:35 3.3555 +15:30:40 3.4839 +15:30:45 3.4334 +15:30:50 3.5458 +15:30:55 3.5458 +15:31:00 3.5737 +15:31:05 3.8093 +15:31:10 3.8687 +15:31:15 3.6158 +15:31:20 3.6400 +15:31:25 3.8343 +15:31:30 4.3289 +15:31:35 4.6827 +15:31:40 4.0072 +15:31:45 3.2425 +15:31:50 5.0425 +15:31:55 5.0143 +15:32:00 4.0130 +15:32:05 3.9664 +15:32:10 3.4517 +15:32:15 2.9267 +15:32:20 3.2338 +15:32:25 3.5358 +15:32:30 3.8163 +15:32:35 2.9669 +15:32:40 3.4165 +15:32:45 4.0559 +15:32:50 3.7171 +15:32:55 3.8179 +15:33:00 3.4580 +15:33:05 3.4154 +15:33:10 3.5092 +15:33:15 3.5714 +15:33:20 3.7812 +15:33:25 3.5706 +15:33:30 3.4405 +15:33:35 3.8478 +15:33:40 3.6984 +15:33:45 3.8862 +15:33:50 4.0245 +15:33:55 5.1584 +15:34:00 4.2329 +15:34:05 4.4908 +15:34:10 4.6532 +15:34:15 3.9406 +15:34:20 3.7473 +15:34:25 3.6472 +15:34:30 3.4764 +15:34:35 4.1516 +15:34:40 4.1582 +15:34:45 3.4295 +15:34:50 3.9380 +15:34:55 3.7949 +15:35:00 3.8207 +15:35:05 3.8994 +15:35:10 4.7165 +15:35:15 4.3260 +15:35:20 2.8046 +15:35:25 4.3127 +15:35:30 4.6219 +15:35:35 3.7009 +15:35:40 3.2196 +15:35:45 4.1235 +15:35:50 3.3892 +15:35:55 4.1848 +15:36:00 4.2728 +15:36:05 3.0149 +15:36:15 3.5741 +15:36:20 3.3450 +15:36:25 3.5353 +15:36:30 3.7689 +15:36:35 3.1939 +15:36:40 3.3990 +15:36:45 3.7898 +15:36:50 4.0800 +15:36:55 4.1625 +15:37:00 4.0324 +15:37:05 2.6636 +15:37:10 2.9293 +15:37:15 3.4335 +15:37:20 3.0982 +15:37:25 2.9650 +15:37:30 3.0823 +15:37:35 3.9720 +15:37:40 4.6494 +15:37:45 4.0524 +15:37:50 3.3487 +15:37:55 3.0645 +15:38:00 3.5640 +15:38:05 3.1275 +15:38:10 3.8128 +15:38:15 4.5543 +15:38:20 4.9980 +15:38:25 5.0811 +15:38:30 4.4440 +15:38:35 4.6937 +15:38:40 4.5591 +15:38:45 3.6078 +15:38:50 3.5456 +15:38:55 3.7120 +15:39:00 3.2494 +15:39:05 3.7511 +15:39:10 3.3647 +15:39:15 4.6861 +15:39:20 4.7032 +15:39:25 4.2812 +15:39:30 3.5334 +15:39:35 2.9161 +15:39:40 3.7053 +15:39:45 2.9780 +15:39:50 4.4497 +15:39:55 4.5490 +15:40:00 4.8275 +15:40:05 4.9899 +15:40:10 4.6672 +15:40:15 3.9396 +15:40:20 4.7298 +15:40:25 3.8586 +15:40:30 3.6741 +15:40:35 3.9354 +15:40:40 3.5975 +15:40:45 2.8065 +15:40:50 3.5648 +15:40:55 4.0775 +15:41:00 4.9281 +15:41:05 4.7539 +15:41:10 4.9543 +15:41:15 4.2202 +15:41:20 5.0146 +15:41:25 4.0771 +15:41:30 4.5286 +15:41:35 5.0618 +15:41:40 5.1537 +15:41:45 4.8183 +15:41:50 4.9481 +15:41:55 4.3225 +15:42:00 3.4334 +15:42:05 4.0070 +15:42:10 3.1660 +15:42:15 2.7699 +15:42:20 3.7629 +15:42:25 3.9472 +15:42:30 5.0832 +15:42:35 4.8400 +15:42:40 4.7717 +15:42:45 4.0138 +15:42:50 3.5939 +15:42:55 3.7825 +15:43:00 5.0680 +15:43:05 3.8511 +15:43:10 5.1326 +15:43:15 4.0633 +15:43:20 3.2859 +15:43:25 2.0645 +15:43:30 2.0550 +15:43:35 3.5359 +15:43:40 3.5696 +15:43:45 3.2064 +15:43:50 4.5037 +15:43:55 4.4547 +15:44:00 4.2186 +15:44:05 3.2178 +15:44:10 2.2721 +15:44:15 4.1242 +15:44:20 4.7840 +15:44:25 3.0753 +15:44:30 5.1292 +15:44:35 4.0880 +15:44:40 4.3629 +15:44:45 4.6045 +15:44:50 4.0396 +15:44:55 3.6352 +15:45:00 3.4947 +15:45:05 4.2058 +15:45:10 3.6470 +15:45:15 3.2292 +15:45:20 3.3054 +15:45:25 4.3254 +15:45:30 3.3571 +15:45:35 3.4613 +15:45:40 3.6562 +15:45:45 3.9765 +15:45:50 3.8140 +15:45:55 3.2001 +15:46:00 4.2890 +15:46:05 4.2849 +15:46:10 2.8204 +15:46:15 3.1173 +15:46:20 3.3885 +15:46:25 3.7724 +15:46:30 3.2397 +15:46:35 3.3354 +15:46:40 3.9091 +15:46:45 4.1466 +15:46:50 4.3368 +15:46:55 4.6446 +15:47:00 4.5703 +15:47:05 4.5498 +15:47:10 4.6055 +15:47:15 4.2674 +15:47:20 2.9780 +15:47:25 2.9015 +15:47:30 2.9928 +15:47:35 3.1222 +15:47:40 3.6976 +15:47:45 4.0542 +15:47:50 4.9046 +15:47:55 3.9439 +15:48:00 4.4248 +15:48:05 4.6497 +15:48:10 3.4013 +15:48:15 3.9242 +15:48:20 2.8499 +15:48:25 3.5752 +15:48:30 4.1481 +15:48:35 3.6090 +15:48:40 3.4556 +15:48:45 4.0051 +15:48:50 4.5730 +15:48:55 5.0374 +15:49:00 3.7008 +15:49:05 4.3418 +15:49:10 4.5581 +15:49:15 4.4103 +15:49:20 4.3618 +15:49:25 4.8249 +15:49:30 4.5168 +15:49:35 4.3327 +15:49:40 2.8764 +15:49:45 4.5569 +15:49:50 4.4800 +15:49:55 4.5583 +15:50:00 3.6093 +15:50:05 4.0981 +15:50:10 2.7855 +15:50:15 2.4716 +15:50:20 2.5959 +15:50:25 2.6254 +15:50:30 4.3024 +15:50:35 4.2036 +15:50:40 4.3863 +15:50:45 4.2410 +15:50:50 2.7534 +15:50:55 4.0177 +15:51:00 4.0177 +15:51:05 3.9786 +15:51:10 3.9597 +15:51:15 3.9455 +15:51:20 3.9528 +15:51:25 3.9113 +15:51:30 3.9113 +15:51:35 3.8331 +15:51:40 3.8125 +15:51:45 3.8326 +15:51:50 3.8736 +15:51:57 3.9346 +15:52:00 3.9346 +15:52:05 3.9783 +15:52:10 3.9860 +15:52:15 3.9491 +15:52:20 3.9283 +15:52:25 3.8969 +15:52:30 3.8969 +15:52:35 3.7973 +15:52:40 3.7131 +15:52:45 3.6774 +15:52:50 3.5981 +15:52:55 3.5934 +15:53:00 3.5934 +15:53:05 3.5908 +15:53:10 3.6047 +15:53:15 3.6349 +15:53:20 3.7138 +15:53:25 3.7360 +15:53:30 3.7360 +15:53:35 3.6574 +15:53:40 3.5424 +15:53:45 3.5037 +15:53:50 3.5389 +15:53:55 3.5389 +15:54:00 3.6133 +15:54:05 3.5394 +15:54:10 3.5531 +15:54:15 3.6394 +15:54:20 3.7266 +15:54:25 3.7266 +15:54:30 3.5878 +15:54:35 3.6012 +15:54:40 3.7164 +15:54:45 3.9094 +15:54:50 4.0513 +15:54:55 4.0513 +15:55:00 4.1209 +15:55:05 4.1304 +15:55:10 4.0536 diff --git a/test/scancommand.tcl b/test/scancommand.tcl new file mode 100644 index 00000000..04e5ee02 --- /dev/null +++ b/test/scancommand.tcl @@ -0,0 +1,486 @@ +#-------------------------------------------------------------------------- +# general scan command wrappers for TOPSI and the like. +# New version using the object.tcl system from sntl instead of obTcl which +# caused a lot of trouble with tcl8.0 +# +# Requires the built in scan command xxxscan. +# +# Mark Koennecke, February 2000 +#-------------------------------------------------------------------------- + +source object.tcl +set recoverfil recover.bin + +#-------------------------- some utility functions ------------------------- +proc MC { t n } { + set string $t + for { set i 1 } { $i < $n } { incr i } { + set string [format "%s%s" $string $t] + } + return $string +} +#-------------------------------------------------------------------------- +proc GetNum { text } { + set list [split $text =] + return [lindex $list 1] +} +#--------------------------------------------------------------------------- + + +#************** Definition of scan class ********************************** + +object_class ScanCommand { + member Mode Monitor + member NP 1 + member counter counter + member NoVar 0 + member Preset 10000 + member File default.dat + member pinterest "" + member Channel 0 + member Active 0 + member Recover 0 + member scanvars + member scanstart + member scanstep + member pinterest + + method var {name start step} { + # check for activity + if {$slot(Active)} { + ClientPut "ERROR: cannot change parameters while scanning" error + return + } + # check parameters + set t [SICSType $name] + if { [string compare $t DRIV] != 0 } { + ClientPut [format "ERROR: %s is not drivable" $name] error + return 0 + } + set t [SICSType $start] + if { [string compare $t NUM] != 0 } { + ClientPut [format "ERROR: %s is no number!" $start] error + return 0 + } + set t [SICSType $step] + if { [string compare $t NUM] != 0 } { + ClientPut [format "ERROR: %s is no number!" $step] error + return 0 + } +# install the variable + set i $slot(NoVar) + incr slot(NoVar) + lappend slot(scanvars) $name + lappend slot(scanstart) $start + lappend slot(scanstep) $step + $self SendInterest pinterest ScanVarChange + ClientPut OK + } + + method info {} { + if { $slot(NoVar) < 1 } { + return "0,1,NONE,0.,0.,default.dat" + } + append result $slot(NP) "," $slot(NoVar) + for {set i 0} { $i < $slot(NoVar) } { incr i} { + append result "," [lindex $slot(scanvars) $i] + } + append result "," [lindex $slot(scanstart) 0] "," \ + [lindex $slot(scanstep) 0] + set r1 [xxxscan getfile] + set l1 [split $r1 "="] + append result "," [lindex $l1 1] + return $result + } + + method getvars {} { + set list "" + lappend list $slot(scanvars) + return [format "scan.Vars = %s -END-" $list] + } + + method xaxis {} { + if { $slot(NoVar) <= 0} { +#---- default Answer + set t [format "%s.xaxis = %f %f" $self 0 1] + } else { + set t [format "%s.xaxis = %f %f" $self [lindex $slot(scanstart) 0] \ + [lindex $slot(scanstep) 0] ] + } + ClientPut $t + } + + method cinterest {} { + xxxscan interest + } + + method uuinterest {} { + xxxscan uuinterest + } + + method pinterest {} { + set nam [GetNum [config MyName]] + lappend $slot(pinterest) $nam + } + + method SendInterest { type text } { +#------ check list first + set l1 $slot($type) + set l2 "" + foreach e $l1 { + set b [string trim $e] + set g [string trim $b "{}"] + set ret [SICSType $g] + if { [string first COM $ret] >= 0 } { + lappend l2 $e + } + } +#-------- update scan data and write + set slot($type) $l2 + foreach e $l2 { + set b [string trim $e] + $b put $text + } + } + + method mode { {NewVal NULL} } { + if { [string compare $NewVal NULL] == 0 } { + set val [format "%s.Mode = %s" $self $slot(Mode)] + ClientPut $val + return $val + } else { +# check for activity + if {$slot(Active)} { + ClientPut "ERROR: cannot change parameters while scanning" error + return + } + set tmp [string tolower $NewVal] + set NewVal $tmp + if { ([string compare $NewVal "timer"] == 0) || \ + ([string compare $NewVal monitor] ==0) } { + set slot(Mode) $NewVal + ClientPut OK + } else { + ClientPut [format "ERROR: %s not recognized as ScanMode" $NewVal] + } + } + } + + method np { { NewVal NULL } } { + if { [string compare $NewVal NULL] == 0 } { + set val [format "%s.NP = %d" $self $slot(NP)] + ClientPut $val + return $val + } else { +# check for activity + if {$slot(Active)} { + ClientPut "ERROR: cannot change parameters while scanning" error + return + } + set t [SICSType $NewVal] + if { [string compare $t NUM] != 0 } { + ClientPut [format "ERROR: %s is no number" $NewVal] error + return + } + set slot(NP) $NewVal + ClientPut OK + } + } + + method preset { {NewVal NULL} } { + if { [string compare $NewVal NULL] == 0 } { + set val [format "%s.Preset = %f" $self $slot(Preset)] + ClientPut $val + return $val + } else { +# check for activity + if {$slot(Active)} { + ClientPut "ERROR: cannot change parameters while scanning" error + return + } + set t [SICSType $NewVal] + if { [string compare $t NUM] != 0} { + ClientPut [format "ERROR: %s is no number" $NewVal] error + return + } + set slot(Preset) $NewVal + ClientPut OK + } + } + + method file {} { + return [xxxscan file] + } + + method setchannel {num} { + set ret [catch {xxxscan setchannel $num} msg] + if { $ret == 0} { + set slot(Channel) $num + } else { + return $msg + } + } + + method list { } { + ClientPut [format "%s.Preset = %f" $self $slot(Preset)] + ClientPut [format "%s.Mode = %s" $self $slot(Mode)] + ClientPut [format "%s.File = %s" $self $slot(File)] + ClientPut [format "%s.NP = %d" $self $slot(NP)] + ClientPut [format "%s.Channel = %d" $self $slot(Channel)] + ClientPut "ScanVariables:" + for { set i 0 } {$i < $slot(NoVar) } { incr i } { + ClientPut [format " %s %f %f" [lindex $slot(scanvars) $i] \ + [lindex $slot(scanstart) $i] \ + [lindex $slot(scanstep) $i] ] + } + } + + method clear {} { +# check for activity + if {$slot(Active)} { + ClientPut "ERROR: cannot clear running scan" error + return + } + + set slot(NP) 0 + set slot(NoVar) 0 + set slot(scanvars) "" + set slot(scanstart) "" + set slot(scanstep) "" + $self SendInterest pinterest ScanVarChange + xxxscan clear + ClientPut OK + } + + method getcounts {} { + return [xxxscan getcounts] + } + + method run { } { +# start with error checking + if { $slot(NP) < 1 } { + ClientPut "ERROR: Insufficient Number of ScanPoints" + return + } + if { $slot(NoVar) < 1 } { + ClientPut "ERROR: No variables to scan given!" + return + } +#------- check for activity + if {$slot(Active)} { + ClientPut "ERROR: Scan already in progress" error + return + } + set slot(Active) 1 + xxxscan clear + for {set i 0 } { $i < $slot(NoVar)} {incr i} { + set ret [catch {xxxscan add [lindex $slot(scanvars) $i] \ + [lindex $slot(scanstart) $i] [lindex $slot(scanstep) $i]} msg] + if {$ret != 0} { + set slot(Active) 0 + error $msg + } + } + set ret [catch \ + {xxxscan run $slot(NP) $slot(Mode) $slot(Preset)} msg] + set slot(Active) 0 + if {$ret != 0 } { + error $msg + } else { + return "Scan Finished" + } + } + + method recover {} { + set slot(Active) 1 + catch {xxxscan recover} msg + set slot(Active) 0 + return "Scan Finished" + } + + method forceclear {} { + set Active 0 + } +} +#---- end of ScanCommand definition + +#********************** initialisation of module commands to SICS ********** + +set ret [catch {scan list} msg] +if {$ret != 0} { + object_new ScanCommand scan + Publish scan Spy + Publish scancounts Spy + Publish textstatus Spy + Publish cscan User + Publish sscan User + Publish sftime Spy + Publish scaninfo Spy +} + +#************************************************************************* + +#===================== Helper commands for status display work ============ +# a new user command which allows status clients to read the counts in a scan +# This is just to circumvent the user protection on scan +proc scancounts { } { + set status [ catch {scan getcounts} result] + if { $status == 0 } { + return $result + } else { + return "scan.Counts= 0" + } +} +#--------------------------------------------------------------------------- +# This is just another utilility function which helps in implementing the +# status display client +proc textstatus { } { + set text [status] + return [format "Status = %s" $text] +} +#--------------------------------------------------------------------------- +# Dumps time in a useful format +proc sftime {} { + return [format "sicstime = %s" [sicstime]] +} +#------------------------------------------------------------------------- +# Utility function which gives scan parameters as an easily parsable +# comma separated list for java status client +proc scaninfo {} { + set result [scan info] + set r1 [sample] + set l1 [split $r1 "="] + append result "," [lindex $l1 1] + append result "," [sicstime] + set r1 [lastscancommand] + set l1 [split $r1 "="] + append result "," [lindex $l1 1] + return [format "scaninfo = %s" $result] +} + +#===================== Syntactical sugar around scan =================== +# center scan. A convenience scan for the one and only Daniel Clemens +# at TOPSI. Scans around a given ceter point. Requires the scan command +# for TOPSI to work. +# +# another convenience scan: +# sscan var1 start end var1 start end .... np preset +# scans var1, var2 from start to end with np steps and a preset of preset +# +# Mark Koennecke, August, 22, 1997 +#----------------------------------------------------------------------------- +proc cscan { var center delta np preset } { +#------ start with some argument checking + set t [SICSType $var] + if { [string compare $t DRIV] != 0 } { + ClientPut [format "ERROR: %s is NOT drivable!" $var] + return + } + set t [SICSType $center] + if { [string compare $t NUM] != 0 } { + ClientPut [format "ERROR: %s is no number!" $center] + return + } + set t [SICSType $delta] + if { [string compare $t NUM] != 0 } { + ClientPut [format "ERROR: %s is no number!" $delta] + return + } + set t [SICSType $np] + if { [string compare $t NUM] != 0 } { + ClientPut [format "ERROR: %s is no number!" $np] + return + } + set t [SICSType $preset] + if { [string compare $t NUM] != 0 } { + ClientPut [format "ERROR: %s is no number!" $preset] + return + } +#-------- store command in lastscancommand + set txt [format "cscan %s %s %s %s %s" $var $center \ + $delta $np $preset] + catch {lastscancommand $txt} +#-------- set standard parameters + scan clear + scan preset $preset + scan np [expr $np*2 + 1] +#--------- calculate start + set start [expr $center - $np * $delta] + set ret [catch {scan var $var $start $delta} msg] + if { $ret != 0} { + ClientPut $msg + return + } +#---------- start scan + set ret [catch {scan run} msg] + if {$ret != 0} { + error $msg + } +} +#--------------------------------------------------------------------------- +proc sscan args { + scan clear +#------- check arguments: the last two must be preset and np! + set l [llength $args] + if { $l < 5} { + ClientPut "ERROR: Insufficient number of arguments to sscan" + return + } + set preset [lindex $args [expr $l - 1]] + set np [lindex $args [expr $l - 2]] + set t [SICSType $preset] + ClientPut $t + ClientPut [string first $t "NUM"] + if { [string compare $t NUM] != 0 } { + ClientPut [format "ERROR: expected number for preset, got %s" \ + $preset] + return + } + set t [SICSType $np] + if { [string compare $t NUM] != 0 } { + ClientPut [format "ERROR: expected number for np, got %s" \ + $np] + return + } + scan preset $preset + scan np $np +#--------- do variables + set nvar [expr ($l - 2) / 3] + for { set i 0 } { $i < $nvar} { incr i } { + set var [lindex $args [expr $i * 3]] + set t [SICSType $var] + if {[string compare $t DRIV] != 0} { + ClientPut [format "ERROR: %s is not drivable" $var] + return + } + set start [lindex $args [expr ($i * 3) + 1]] + set t [SICSType $start] + if { [string compare $t NUM] != 0 } { + ClientPut [format "ERROR: expected number for start, got %s" \ + $start] + return + } + set end [lindex $args [expr ($i * 3) + 2]] + set t [SICSType $end] + if { [string compare $t NUM] != 0 } { + ClientPut [format "ERROR: expected number for end, got %s" \ + $end] + return + } +#--------- do scan parameters + set step [expr double($end - $start)/double($np)] + set ret [catch {scan var $var $start $step} msg] + if { $ret != 0} { + ClientPut $msg + return + } + } +#------------- set lastcommand text + set txt [format "sscan %s" [join $args]] + catch {lastscancommand $txt} +#------------- start scan + set ret [catch {scan run} msg] + if {$ret != 0} { + error $msg + } +} diff --git a/test/scantest.tcl b/test/scantest.tcl new file mode 100644 index 00000000..2b085b24 --- /dev/null +++ b/test/scantest.tcl @@ -0,0 +1,177 @@ +#-------------------------------------------------------------------------- +# This file contains some regression tests for the SICS scan module. +# This relies on the presence of a suitable configured multicounter in +# the test initialization file. That multi counters script must have been +# configured to generate an gaussian based on a a4 position. This gaussian +# will then be used to check data and for testing peak based algorithms +# such as optimization routines, peak and center etc. +# +# Mark Koennecke, October 2006 +#-------------------------------------------------------------------------- + +clientput "Testing scan routines.." + +config rights Mugger Mugger +aba setpar errortype 1 0 +config rights User User + +proc testScanResults {} { + set result [eval peak data] + set l [split $result ,] + if { abs([lindex $l 0] - 5.3) > .1} { + error "Bad peak position" + } + if { abs([lindex $l 1] - 1.4) > .3} { + error "Bad peak FWHM" + } + if { abs([lindex $l 2] - 288) > 7} { + error "Bad peak Value" + } +} + +test scan-1.0 {Test Normal Scan} -body { + testOK "xxxscan clear" + testOK "xxxscan add a4 2. .2" + set result [eval xxxscan run 30 timer 2] + if {[string first ERROR $result] >= 0} { + set idx [string first ERROR $result] + set errText [string range $result $idx end] + error "ERROR occurred during scan: $errText" + } + testScanResults + return OK +} -result OK + +test scan-1.1 {Test cscan} -body { + set result [eval cscan a4 5.3 .2 15 3] + if {[string first ERROR $result] >= 0} { + set idx [string first ERROR $result] + set errText [string range $result $idx end] + error "ERROR occurred during scan: $errText" + } + testScanResults + return OK +} -result OK + +test scan-1.2 {Test sscan} -body { + set result [eval sscan a4 2 7 30 2] + if {[string first ERROR $result] >= 0} { + set idx [string first ERROR $result] + set errText [string range $result $idx end] + error "ERROR occurred during scan: $errText" + } + testScanResults + return OK +} -result OK + +test scan-1.3 {Test scan interest} -body { + testOK "xxxscan interest" + set result [eval xxxscan run 3 timer 2] + if {[string first ERROR $result] >= 0} { + set idx [string first ERROR $result] + set errText [string range $result $idx end] + error "ERROR occurred during scan: $errText" + } + if {[string first NewScan $result] < 0} { + error " NewScan missing" + } + if {[string first scan.Counts $result] < 0} { + error "count data missing" + } + if {[string first ScanEnd $result] < 0} { + error "ScanEnd missing" + } + xxxscan uninterest + return OK +} -result OK + +test scan-1.3.1 {Test scan uuinterest} -body { + testOK "xxxscan uuinterest" + set result [eval xxxscan run 3 timer 2] + if {[string first ERROR $result] >= 0} { + set idx [string first ERROR $result] + set errText [string range $result $idx end] + error "ERROR occurred during scan: $errText" + } + if {[string first NewScan $result] < 0} { + error " NewScan missing" + } + if {[string first ScanData $result] < 0} { + error "count data missing" + } + if {[string first ScanEnd $result] < 0} { + error "ScanEnd missing" + } + xxxscan uninterest + return OK +} -result OK + +test scan-1.3.2 {Test scan dyninterest} -body { + testOK "xxxscan dyninterest" + set result [eval xxxscan run 3 timer 2] + if {[string first ERROR $result] >= 0} { + set idx [string first ERROR $result] + set errText [string range $result $idx end] + error "ERROR occurred during scan: $errText" + } + if {[string first NewScan $result] < 0} { + error " NewScan missing" + } + if {[string first xxxscan.scanpoint $result] < 0} { + error "scan point data missing" + } + if {[string first ScanEnd $result] < 0} { + error "ScanEnd missing" + } + xxxscan uninterest + return OK +} -result OK + +test scan-1.4 {Test scan log} -body { + testOK "xxxscan log brumm" + return OK +} -result OK + +test scan-1.5 {Test scan getfile} -body { + testCommand "xxxscan getfile" scan.File +} -result OK + +test scan-1.6 {Test scan getcounts} -body { + testCommand "xxxscan getcounts" xxxscan.Counts +} -result OK + +test scan-1.7 {Test scan getmonitor } -body { + testCommand "xxxscan getmonitor 1" xxxscan.mon01 +} -result OK + +test scan-1.8 {Test scan gettimes } -body { + testCommand "xxxscan gettimes" xxxscan.scantimes +} -result OK + +test scan-1.9 {Test scan np } -body { + testCommand "xxxscan np" xxxscan.nP +} -result OK + +test scan-1.10 {Test scan getvardata } -body { + testCommand "xxxscan getvardata 0" scan.a4 +} -result OK + +test scan-1.11 {Test scan noscanvar } -body { + testCommand "xxxscan noscanvar" xxxscan.noscanvar +} -result OK + +test scan-1.12 {Test scan getvarpar} -body { + testCommand "xxxscan getvarpar 0" xxxscan.a4 +} -result OK + +test scan-1.13 {Test scan aborting} -body { + exec interrupt.tcl & + set result [eval cscan a4 5.3 .2 15 3] + if {[string first "Scan aborted" $result] < 0} { + error "Scan did not interrupt!" + } + return OK +} -result OK + + + diff --git a/test/sicsdatasoll.dat b/test/sicsdatasoll.dat new file mode 100644 index 00000000..b5406ef2 --- /dev/null +++ b/test/sicsdatasoll.dat @@ -0,0 +1,23 @@ + 0 32 + 1 32 + 2 32 + 3 32 + 4 32 + 5 32 + 6 32 + 7 32 + 8 32 + 9 32 + 10 32 + 11 32 + 12 32 + 13 32 + 14 32 + 15 32 + 16 32 + 17 32 + 18 32 + 19 32 + 20 32 + 21 32 + 22 32 diff --git a/test/sicsstat.tcl b/test/sicsstat.tcl new file mode 100644 index 00000000..01fca9a5 --- /dev/null +++ b/test/sicsstat.tcl @@ -0,0 +1,185 @@ +exe batchpath ./ +exe syspath ./ + +#--- BEGIN (commands producing errors on last restore) +#--- END (commands producing errors on last restore) + +lotte UNKNOWN +lotte setAccess 2 +# Motor brumm +brumm sign 1.000000 +brumm SoftZero 0.000000 +brumm SoftLowerLim -180.000000 +brumm SoftUpperLim 180.000000 +brumm Fixed -1.000000 +brumm InterruptMode 0.000000 +brumm precision 0.010000 +brumm ignorefault 0.000000 +brumm AccessCode 2.000000 +brumm failafter 3.000000 +brumm maxretry 3.000000 +brumm movecount 10.000000 +# Motor miau +miau sign 1.000000 +miau SoftZero 0.000000 +miau SoftLowerLim -180.000000 +miau SoftUpperLim 180.000000 +miau Fixed -1.000000 +miau InterruptMode 0.000000 +miau precision 0.010000 +miau ignorefault 0.000000 +miau AccessCode 2.000000 +miau failafter 3.000000 +miau maxretry 3.000000 +miau movecount 10.000000 +# Counter aba +aba SetPreset 1000.000000 +aba SetMode Timer +# Counter hugo +hugo SetPreset 1000.000000 +hugo SetMode Timer +# Counter lieselotte +lieselotte SetPreset 1000.000000 +lieselotte SetMode Timer +# Counter multi +multi SetPreset 0.000000 +multi SetMode Timer +# Motor a1 +a1 sign 1.000000 +a1 SoftZero 0.000000 +a1 SoftLowerLim -2.000000 +a1 SoftUpperLim 180.000000 +a1 Fixed -1.000000 +a1 InterruptMode 0.000000 +a1 precision 0.010000 +a1 ignorefault 0.000000 +a1 AccessCode 2.000000 +a1 failafter 3.000000 +a1 maxretry 3.000000 +a1 movecount 10.000000 +# Motor a2 +a2 sign 1.000000 +a2 SoftZero 0.000000 +a2 SoftLowerLim 30.000000 +a2 SoftUpperLim 150.000000 +a2 Fixed -1.000000 +a2 InterruptMode 0.000000 +a2 precision 0.010000 +a2 ignorefault 0.000000 +a2 AccessCode 2.000000 +a2 failafter 3.000000 +a2 maxretry 3.000000 +a2 movecount 10.000000 +# Motor a3 +a3 sign 1.000000 +a3 SoftZero 0.000000 +a3 SoftLowerLim -360.000000 +a3 SoftUpperLim 360.000000 +a3 Fixed -1.000000 +a3 InterruptMode 0.000000 +a3 precision 0.010000 +a3 ignorefault 0.000000 +a3 AccessCode 2.000000 +a3 failafter 3.000000 +a3 maxretry 3.000000 +a3 movecount 10.000000 +# Motor a4 +a4 sign 1.000000 +a4 SoftZero 0.000000 +a4 SoftLowerLim -180.000000 +a4 SoftUpperLim 180.000000 +a4 Fixed -1.000000 +a4 InterruptMode 0.000000 +a4 precision 0.010000 +a4 ignorefault 0.000000 +a4 AccessCode 2.000000 +a4 failafter 3.000000 +a4 maxretry 3.000000 +a4 movecount 10.000000 +# Motor a5 +a5 sign 1.000000 +a5 SoftZero 0.000000 +a5 SoftLowerLim -180.000000 +a5 SoftUpperLim 180.000000 +a5 Fixed -1.000000 +a5 InterruptMode 0.000000 +a5 precision 0.010000 +a5 ignorefault 0.000000 +a5 AccessCode 2.000000 +a5 failafter 3.000000 +a5 maxretry 3.000000 +a5 movecount 10.000000 +# Motor a6 +a6 sign 1.000000 +a6 SoftZero 0.000000 +a6 SoftLowerLim -180.000000 +a6 SoftUpperLim 180.000000 +a6 Fixed -1.000000 +a6 InterruptMode 0.000000 +a6 precision 0.010000 +a6 ignorefault 0.000000 +a6 AccessCode 2.000000 +a6 failafter 3.000000 +a6 maxretry 3.000000 +a6 movecount 10.000000 +# Motor sgu +sgu sign 1.000000 +sgu SoftZero 0.000000 +sgu SoftLowerLim -20.000000 +sgu SoftUpperLim 20.000000 +sgu Fixed -1.000000 +sgu InterruptMode 0.000000 +sgu precision 0.010000 +sgu ignorefault 0.000000 +sgu AccessCode 2.000000 +sgu failafter 3.000000 +sgu maxretry 3.000000 +sgu movecount 10.000000 +# Motor sgl +sgl sign 1.000000 +sgl SoftZero 0.000000 +sgl SoftLowerLim -20.000000 +sgl SoftUpperLim 20.000000 +sgl Fixed -1.000000 +sgl InterruptMode 0.000000 +sgl precision 0.010000 +sgl ignorefault 0.000000 +sgl AccessCode 2.000000 +sgl failafter 3.000000 +sgl maxretry 3.000000 +sgl movecount 10.000000 +# Counter scancter +scancter SetPreset 0.000000 +scancter SetMode Timer +hm CountMode timer +hm preset 10.000000 +tof CountMode timer +tof preset 10.000000 +tof genbin 10.000000 12.000000 100 +tof init +#---- tasUB module tasub +tasub mono dd 3.350000 +tasub mono hb1 1.000000 +tasub mono hb2 1.000000 +tasub mono vb1 1.000000 +tasub mono vb2 1.000000 +tasub mono ss 1 +tasub ana dd 3.350000 +tasub ana hb1 1.000000 +tasub ana hb2 1.000000 +tasub ana vb1 1.000000 +tasub ana vb2 1.000000 +tasub ana ss 1 +tasub cell 1.000000 1.000000 1.000000 90.000000 90.000000 90.000000 +tasub clear +tasub outofplane 1 +tasub const ki +tasub ss 1 + tasub setub 1.000000 0.000000 0.000000 0.000000 1.000000 0.000000 0.000000 0.000000 1.000000 + tasub setnormal 0.000000 0.000000 0.000000 +tasub settarget 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 +tasub r1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 +tasub r2 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 +tasub update +#----- MultiMotor sa +sa recovernampos noeff a3 24 a4 48 diff --git a/test/sicstcldebug.tcl b/test/sicstcldebug.tcl new file mode 100644 index 00000000..b6c8273b --- /dev/null +++ b/test/sicstcldebug.tcl @@ -0,0 +1,40 @@ +#------------------------------------------------------------------ +# This is a helper file in order to debug SICS Tcl scripts. The idea +# is that a connection to a SICS interpreter at localhost:2911 is opened. +# Then unknown is reimplemented to send unknown commands (which must be +# SICS commands) to the SICS interpreter for evaluation. This is done +# with transact in order to figure out when SICS finished processing. +# Thus is should be possible to debug SICS Tcl scripts in a normal +# standalone interpreter without the overhead of restarting SICS +# all the time. It may even be possible to use one of the normal +# Tcl debuggers then.... +# +# Mark Koennecke, February 2006 +#------------------------------------------------------------------ + +set socke [socket localhost 2911] +gets $socke +puts $socke "Spy Spy" +flush $socke +gets $socke +#------------------------------------------------------------------ +proc unknown args { + global socke + append com "transact " [join $args] + puts $socke $com + flush $socke + set reply "" + while {1} { + set line [gets $socke] + if {[string first TRANSACTIONFINISHED $line] >= 0} { + return $reply + } else { + append reply $line "\n" + } + } +} +#------------------------------------------------------------------ +proc clientput args { + puts stdout [join $args] +} +#------------------------------------------------------------------ diff --git a/test/tcltest.tcl b/test/tcltest.tcl new file mode 100644 index 00000000..407c31d4 --- /dev/null +++ b/test/tcltest.tcl @@ -0,0 +1,3354 @@ +# tcltest.tcl -- +# +# This file contains support code for the Tcl test suite. It +# defines the tcltest namespace and finds and defines the output +# directory, constraints available, output and error channels, +# etc. used by Tcl tests. See the tcltest man page for more +# details. +# +# This design was based on the Tcl testing approach designed and +# initially implemented by Mary Ann May-Pumphrey of Sun +# Microsystems. +# +# Copyright (c) 1994-1997 Sun Microsystems, Inc. +# Copyright (c) 1998-1999 by Scriptics Corporation. +# Copyright (c) 2000 by Ajuba Solutions +# Contributions from Don Porter, NIST, 2002. (not subject to US copyright) +# All rights reserved. +# +# RCS: @(#) $Id$ + +package require Tcl 8.3 ;# uses [glob -directory] +namespace eval tcltest { + + # When the version number changes, be sure to update the pkgIndex.tcl file, + # and the install directory in the Makefiles. When the minor version + # changes (new feature) be sure to update the man page as well. + variable Version 2.2.8 + + # Compatibility support for dumb variables defined in tcltest 1 + # Do not use these. Call [package provide Tcl] and [info patchlevel] + # yourself. You don't need tcltest to wrap it for you. + variable version [package provide Tcl] + variable patchLevel [info patchlevel] + +##### Export the public tcltest procs; several categories + # + # Export the main functional commands that do useful things + namespace export cleanupTests loadTestedCommands makeDirectory \ + makeFile removeDirectory removeFile runAllTests test + + # Export configuration commands that control the functional commands + namespace export configure customMatch errorChannel interpreter \ + outputChannel testConstraint + + # Export commands that are duplication (candidates for deprecation) + namespace export bytestring ;# dups [encoding convertfrom identity] + namespace export debug ;# [configure -debug] + namespace export errorFile ;# [configure -errfile] + namespace export limitConstraints ;# [configure -limitconstraints] + namespace export loadFile ;# [configure -loadfile] + namespace export loadScript ;# [configure -load] + namespace export match ;# [configure -match] + namespace export matchFiles ;# [configure -file] + namespace export matchDirectories ;# [configure -relateddir] + namespace export normalizeMsg ;# application of [customMatch] + namespace export normalizePath ;# [file normalize] (8.4) + namespace export outputFile ;# [configure -outfile] + namespace export preserveCore ;# [configure -preservecore] + namespace export singleProcess ;# [configure -singleproc] + namespace export skip ;# [configure -skip] + namespace export skipFiles ;# [configure -notfile] + namespace export skipDirectories ;# [configure -asidefromdir] + namespace export temporaryDirectory ;# [configure -tmpdir] + namespace export testsDirectory ;# [configure -testdir] + namespace export verbose ;# [configure -verbose] + namespace export viewFile ;# binary encoding [read] + namespace export workingDirectory ;# [cd] [pwd] + + # Export deprecated commands for tcltest 1 compatibility + namespace export getMatchingFiles mainThread restoreState saveState \ + threadReap + + # tcltest::normalizePath -- + # + # This procedure resolves any symlinks in the path thus creating + # a path without internal redirection. It assumes that the + # incoming path is absolute. + # + # Arguments + # pathVar - name of variable containing path to modify. + # + # Results + # The path is modified in place. + # + # Side Effects: + # None. + # + proc normalizePath {pathVar} { + upvar $pathVar path + set oldpwd [pwd] + catch {cd $path} + set path [pwd] + cd $oldpwd + return $path + } + +##### Verification commands used to test values of variables and options + # + # Verification command that accepts everything + proc AcceptAll {value} { + return $value + } + + # Verification command that accepts valid Tcl lists + proc AcceptList { list } { + return [lrange $list 0 end] + } + + # Verification command that accepts a glob pattern + proc AcceptPattern { pattern } { + return [AcceptAll $pattern] + } + + # Verification command that accepts integers + proc AcceptInteger { level } { + return [incr level 0] + } + + # Verification command that accepts boolean values + proc AcceptBoolean { boolean } { + return [expr {$boolean && $boolean}] + } + + # Verification command that accepts (syntactically) valid Tcl scripts + proc AcceptScript { script } { + if {![info complete $script]} { + return -code error "invalid Tcl script: $script" + } + return $script + } + + # Verification command that accepts (converts to) absolute pathnames + proc AcceptAbsolutePath { path } { + return [file join [pwd] $path] + } + + # Verification command that accepts existing readable directories + proc AcceptReadable { path } { + if {![file readable $path]} { + return -code error "\"$path\" is not readable" + } + return $path + } + proc AcceptDirectory { directory } { + set directory [AcceptAbsolutePath $directory] + if {![file exists $directory]} { + return -code error "\"$directory\" does not exist" + } + if {![file isdir $directory]} { + return -code error "\"$directory\" is not a directory" + } + return [AcceptReadable $directory] + } + +##### Initialize internal arrays of tcltest, but only if the caller + # has not already pre-initialized them. This is done to support + # compatibility with older tests that directly access internals + # rather than go through command interfaces. + # + proc ArrayDefault {varName value} { + variable $varName + if {[array exists $varName]} { + return + } + if {[info exists $varName]} { + # Pre-initialized value is a scalar: destroy it! + unset $varName + } + array set $varName $value + } + + # save the original environment so that it can be restored later + ArrayDefault originalEnv [array get ::env] + + # initialize numTests array to keep track of the number of tests + # that pass, fail, and are skipped. + ArrayDefault numTests [list Total 0 Passed 0 Skipped 0 Failed 0] + + # createdNewFiles will store test files as indices and the list of + # files (that should not have been) left behind by the test files + # as values. + ArrayDefault createdNewFiles {} + + # initialize skippedBecause array to keep track of constraints that + # kept tests from running; a constraint name of "userSpecifiedSkip" + # means that the test appeared on the list of tests that matched the + # -skip value given to the flag; "userSpecifiedNonMatch" means that + # the test didn't match the argument given to the -match flag; both + # of these constraints are counted only if tcltest::debug is set to + # true. + ArrayDefault skippedBecause {} + + # initialize the testConstraints array to keep track of valid + # predefined constraints (see the explanation for the + # InitConstraints proc for more details). + ArrayDefault testConstraints {} + +##### Initialize internal variables of tcltest, but only if the caller + # has not already pre-initialized them. This is done to support + # compatibility with older tests that directly access internals + # rather than go through command interfaces. + # + proc Default {varName value {verify AcceptAll}} { + variable $varName + if {![info exists $varName]} { + variable $varName [$verify $value] + } else { + variable $varName [$verify [set $varName]] + } + } + + # Save any arguments that we might want to pass through to other + # programs. This is used by the -args flag. + # FINDUSER + Default parameters {} + + # Count the number of files tested (0 if runAllTests wasn't called). + # runAllTests will set testSingleFile to false, so stats will + # not be printed until runAllTests calls the cleanupTests proc. + # The currentFailure var stores the boolean value of whether the + # current test file has had any failures. The failFiles list + # stores the names of test files that had failures. + Default numTestFiles 0 AcceptInteger + Default testSingleFile true AcceptBoolean + Default currentFailure false AcceptBoolean + Default failFiles {} AcceptList + + # Tests should remove all files they create. The test suite will + # check the current working dir for files created by the tests. + # filesMade keeps track of such files created using the makeFile and + # makeDirectory procedures. filesExisted stores the names of + # pre-existing files. + # + # Note that $filesExisted lists only those files that exist in + # the original [temporaryDirectory]. + Default filesMade {} AcceptList + Default filesExisted {} AcceptList + proc FillFilesExisted {} { + variable filesExisted + + # Save the names of files that already exist in the scratch directory. + foreach file [glob -nocomplain -directory [temporaryDirectory] *] { + lappend filesExisted [file tail $file] + } + + # After successful filling, turn this into a no-op. + proc FillFilesExisted args {} + } + + # Kept only for compatibility + Default constraintsSpecified {} AcceptList + trace variable constraintsSpecified r {set ::tcltest::constraintsSpecified \ + [array names ::tcltest::testConstraints] ;# } + + # tests that use threads need to know which is the main thread + Default mainThread 1 + variable mainThread + if {[info commands thread::id] != {}} { + set mainThread [thread::id] + } elseif {[info commands testthread] != {}} { + set mainThread [testthread id] + } + + # Set workingDirectory to [pwd]. The default output directory for + # Tcl tests is the working directory. Whenever this value changes + # change to that directory. + variable workingDirectory + trace variable workingDirectory w \ + [namespace code {cd $workingDirectory ;#}] + + Default workingDirectory [pwd] AcceptAbsolutePath + proc workingDirectory { {dir ""} } { + variable workingDirectory + if {[llength [info level 0]] == 1} { + return $workingDirectory + } + set workingDirectory [AcceptAbsolutePath $dir] + } + + # Set the location of the execuatble + Default tcltest [info nameofexecutable] + trace variable tcltest w [namespace code {testConstraint stdio \ + [eval [ConstraintInitializer stdio]] ;#}] + + # save the platform information so it can be restored later + Default originalTclPlatform [array get ::tcl_platform] + + # If a core file exists, save its modification time. + if {[file exists [file join [workingDirectory] core]]} { + Default coreModTime \ + [file mtime [file join [workingDirectory] core]] + } + + # stdout and stderr buffers for use when we want to store them + Default outData {} + Default errData {} + + # keep track of test level for nested test commands + variable testLevel 0 + + # the variables and procs that existed when saveState was called are + # stored in a variable of the same name + Default saveState {} + + # Internationalization support -- used in [SetIso8859_1_Locale] and + # [RestoreLocale]. Those commands are used in cmdIL.test. + + if {![info exists [namespace current]::isoLocale]} { + variable isoLocale fr + switch -- $::tcl_platform(platform) { + "unix" { + + # Try some 'known' values for some platforms: + + switch -exact -- $::tcl_platform(os) { + "FreeBSD" { + set isoLocale fr_FR.ISO_8859-1 + } + HP-UX { + set isoLocale fr_FR.iso88591 + } + Linux - + IRIX { + set isoLocale fr + } + default { + + # Works on SunOS 4 and Solaris, and maybe + # others... Define it to something else on your + # system if you want to test those. + + set isoLocale iso_8859_1 + } + } + } + "windows" { + set isoLocale French + } + } + } + + variable ChannelsWeOpened; array set ChannelsWeOpened {} + # output goes to stdout by default + Default outputChannel stdout + proc outputChannel { {filename ""} } { + variable outputChannel + variable ChannelsWeOpened + + # This is very subtle and tricky, so let me try to explain. + # (Hopefully this longer comment will be clear when I come + # back in a few months, unlike its predecessor :) ) + # + # The [outputChannel] command (and underlying variable) have to + # be kept in sync with the [configure -outfile] configuration + # option ( and underlying variable Option(-outfile) ). This is + # accomplished with a write trace on Option(-outfile) that will + # update [outputChannel] whenver a new value is written. That + # much is easy. + # + # The trick is that in order to maintain compatibility with + # version 1 of tcltest, we must allow every configuration option + # to get its inital value from command line arguments. This is + # accomplished by setting initial read traces on all the + # configuration options to parse the command line option the first + # time they are read. These traces are cancelled whenever the + # program itself calls [configure]. + # + # OK, then so to support tcltest 1 compatibility, it seems we want + # to get the return from [outputFile] to trigger the read traces, + # just in case. + # + # BUT! A little known feature of Tcl variable traces is that + # traces are disabled during the handling of other traces. So, + # if we trigger read traces on Option(-outfile) and that triggers + # command line parsing which turns around and sets an initial + # value for Option(-outfile) -- -- the write trace that + # would keep [outputChannel] in sync with that new initial value + # would not fire! + # + # SO, finally, as a workaround, instead of triggering read traces + # by invoking [outputFile], we instead trigger the same set of + # read traces by invoking [debug]. Any command that reads a + # configuration option would do. [debug] is just a handy one. + # The end result is that we support tcltest 1 compatibility and + # keep outputChannel and -outfile in sync in all cases. + debug + + if {[llength [info level 0]] == 1} { + return $outputChannel + } + if {[info exists ChannelsWeOpened($outputChannel)]} { + close $outputChannel + unset ChannelsWeOpened($outputChannel) + } + switch -exact -- $filename { + stderr - + stdout { + set outputChannel $filename + } + default { + set outputChannel [open $filename a] + set ChannelsWeOpened($outputChannel) 1 + + # If we created the file in [temporaryDirectory], then + # [cleanupTests] will delete it, unless we claim it was + # already there. + set outdir [normalizePath [file dirname \ + [file join [pwd] $filename]]] + if {[string equal $outdir [temporaryDirectory]]} { + variable filesExisted + FillFilesExisted + set filename [file tail $filename] + if {[lsearch -exact $filesExisted $filename] == -1} { + lappend filesExisted $filename + } + } + } + } + return $outputChannel + } + + # errors go to stderr by default + Default errorChannel stderr + proc errorChannel { {filename ""} } { + variable errorChannel + variable ChannelsWeOpened + + # This is subtle and tricky. See the comment above in + # [outputChannel] for a detailed explanation. + debug + + if {[llength [info level 0]] == 1} { + return $errorChannel + } + if {[info exists ChannelsWeOpened($errorChannel)]} { + close $errorChannel + unset ChannelsWeOpened($errorChannel) + } + switch -exact -- $filename { + stderr - + stdout { + set errorChannel $filename + } + default { + set errorChannel [open $filename a] + set ChannelsWeOpened($errorChannel) 1 + + # If we created the file in [temporaryDirectory], then + # [cleanupTests] will delete it, unless we claim it was + # already there. + set outdir [normalizePath [file dirname \ + [file join [pwd] $filename]]] + if {[string equal $outdir [temporaryDirectory]]} { + variable filesExisted + FillFilesExisted + set filename [file tail $filename] + if {[lsearch -exact $filesExisted $filename] == -1} { + lappend filesExisted $filename + } + } + } + } + return $errorChannel + } + +##### Set up the configurable options + # + # The configurable options of the package + variable Option; array set Option {} + + # Usage strings for those options + variable Usage; array set Usage {} + + # Verification commands for those options + variable Verify; array set Verify {} + + # Initialize the default values of the configurable options that are + # historically associated with an exported variable. If that variable + # is already set, support compatibility by accepting its pre-set value. + # Use [trace] to establish ongoing connection between the deprecated + # exported variable and the modern option kept as a true internal var. + # Also set up usage string and value testing for the option. + proc Option {option value usage {verify AcceptAll} {varName {}}} { + variable Option + variable Verify + variable Usage + variable OptionControlledVariables + set Usage($option) $usage + set Verify($option) $verify + if {[catch {$verify $value} msg]} { + return -code error $msg + } else { + set Option($option) $msg + } + if {[string length $varName]} { + variable $varName + if {[info exists $varName]} { + if {[catch {$verify [set $varName]} msg]} { + return -code error $msg + } else { + set Option($option) $msg + } + unset $varName + } + namespace eval [namespace current] \ + [list upvar 0 Option($option) $varName] + # Workaround for Bug (now Feature Request) 572889. Grrrr.... + # Track all the variables tied to options + lappend OptionControlledVariables $varName + # Later, set auto-configure read traces on all + # of them, since a single trace on Option does not work. + proc $varName {{value {}}} [subst -nocommands { + if {[llength [info level 0]] == 2} { + Configure $option [set value] + } + return [Configure $option] + }] + } + } + + proc MatchingOption {option} { + variable Option + set match [array names Option $option*] + switch -- [llength $match] { + 0 { + set sorted [lsort [array names Option]] + set values [join [lrange $sorted 0 end-1] ", "] + append values ", or [lindex $sorted end]" + return -code error "unknown option $option: should be\ + one of $values" + } + 1 { + return [lindex $match 0] + } + default { + # Exact match trumps ambiguity + if {[lsearch -exact $match $option] >= 0} { + return $option + } + set values [join [lrange $match 0 end-1] ", "] + append values ", or [lindex $match end]" + return -code error "ambiguous option $option:\ + could match $values" + } + } + } + + proc EstablishAutoConfigureTraces {} { + variable OptionControlledVariables + foreach varName [concat $OptionControlledVariables Option] { + variable $varName + trace variable $varName r [namespace code {ProcessCmdLineArgs ;#}] + } + } + + proc RemoveAutoConfigureTraces {} { + variable OptionControlledVariables + foreach varName [concat $OptionControlledVariables Option] { + variable $varName + foreach pair [trace vinfo $varName] { + foreach {op cmd} $pair break + if {[string equal r $op] + && [string match *ProcessCmdLineArgs* $cmd]} { + trace vdelete $varName $op $cmd + } + } + } + # Once the traces are removed, this can become a no-op + proc RemoveAutoConfigureTraces {} {} + } + + proc Configure args { + variable Option + variable Verify + set n [llength $args] + if {$n == 0} { + return [lsort [array names Option]] + } + if {$n == 1} { + if {[catch {MatchingOption [lindex $args 0]} option]} { + return -code error $option + } + return $Option($option) + } + while {[llength $args] > 1} { + if {[catch {MatchingOption [lindex $args 0]} option]} { + return -code error $option + } + if {[catch {$Verify($option) [lindex $args 1]} value]} { + return -code error "invalid $option\ + value \"[lindex $args 1]\": $value" + } + set Option($option) $value + set args [lrange $args 2 end] + } + if {[llength $args]} { + if {[catch {MatchingOption [lindex $args 0]} option]} { + return -code error $option + } + return -code error "missing value for option $option" + } + } + proc configure args { + RemoveAutoConfigureTraces + set code [catch {eval Configure $args} msg] + return -code $code $msg + } + + proc AcceptVerbose { level } { + set level [AcceptList $level] + if {[llength $level] == 1} { + if {![regexp {^(pass|body|skip|start|error)$} $level]} { + # translate single characters abbreviations to expanded list + set level [string map {p pass b body s skip t start e error} \ + [split $level {}]] + } + } + set valid [list] + foreach v $level { + if {[regexp {^(pass|body|skip|start|error)$} $v]} { + lappend valid $v + } + } + return $valid + } + + proc IsVerbose {level} { + variable Option + return [expr {[lsearch -exact $Option(-verbose) $level] != -1}] + } + + # Default verbosity is to show bodies of failed tests + Option -verbose {body error} { + Takes any combination of the values 'p', 's', 'b', 't' and 'e'. + Test suite will display all passed tests if 'p' is specified, all + skipped tests if 's' is specified, the bodies of failed tests if + 'b' is specified, and when tests start if 't' is specified. + ErrorInfo is displayed if 'e' is specified. + } AcceptVerbose verbose + + # Match and skip patterns default to the empty list, except for + # matchFiles, which defaults to all .test files in the + # testsDirectory and matchDirectories, which defaults to all + # directories. + Option -match * { + Run all tests within the specified files that match one of the + list of glob patterns given. + } AcceptList match + + Option -skip {} { + Skip all tests within the specified tests (via -match) and files + that match one of the list of glob patterns given. + } AcceptList skip + + Option -file *.test { + Run tests in all test files that match the glob pattern given. + } AcceptPattern matchFiles + + # By default, skip files that appear to be SCCS lock files. + Option -notfile l.*.test { + Skip all test files that match the glob pattern given. + } AcceptPattern skipFiles + + Option -relateddir * { + Run tests in directories that match the glob pattern given. + } AcceptPattern matchDirectories + + Option -asidefromdir {} { + Skip tests in directories that match the glob pattern given. + } AcceptPattern skipDirectories + + # By default, don't save core files + Option -preservecore 0 { + If 2, save any core files produced during testing in the directory + specified by -tmpdir. If 1, notify the user if core files are + created. + } AcceptInteger preserveCore + + # debug output doesn't get printed by default; debug level 1 spits + # up only the tests that were skipped because they didn't match or + # were specifically skipped. A debug level of 2 would spit up the + # tcltest variables and flags provided; a debug level of 3 causes + # some additional output regarding operations of the test harness. + # The tcltest package currently implements only up to debug level 3. + Option -debug 0 { + Internal debug level + } AcceptInteger debug + + proc SetSelectedConstraints args { + variable Option + foreach c $Option(-constraints) { + testConstraint $c 1 + } + } + Option -constraints {} { + Do not skip the listed constraints listed in -constraints. + } AcceptList + trace variable Option(-constraints) w \ + [namespace code {SetSelectedConstraints ;#}] + + # Don't run only the "-constraint" specified tests by default + proc ClearUnselectedConstraints args { + variable Option + variable testConstraints + if {!$Option(-limitconstraints)} {return} + foreach c [array names testConstraints] { + if {[lsearch -exact $Option(-constraints) $c] == -1} { + testConstraint $c 0 + } + } + } + Option -limitconstraints false { + whether to run only tests with the constraints + } AcceptBoolean limitConstraints + trace variable Option(-limitconstraints) w \ + [namespace code {ClearUnselectedConstraints ;#}] + + # A test application has to know how to load the tested commands + # into the interpreter. + Option -load {} { + Specifies the script to load the tested commands. + } AcceptScript loadScript + + # Default is to run each test file in a separate process + Option -singleproc 0 { + whether to run all tests in one process + } AcceptBoolean singleProcess + + proc AcceptTemporaryDirectory { directory } { + set directory [AcceptAbsolutePath $directory] + if {![file exists $directory]} { + file mkdir $directory + } + set directory [AcceptDirectory $directory] + if {![file writable $directory]} { + if {[string equal [workingDirectory] $directory]} { + # Special exception: accept the default value + # even if the directory is not writable + return $directory + } + return -code error "\"$directory\" is not writeable" + } + return $directory + } + + # Directory where files should be created + Option -tmpdir [workingDirectory] { + Save temporary files in the specified directory. + } AcceptTemporaryDirectory temporaryDirectory + trace variable Option(-tmpdir) w \ + [namespace code {normalizePath Option(-tmpdir) ;#}] + + # Tests should not rely on the current working directory. + # Files that are part of the test suite should be accessed relative + # to [testsDirectory] + Option -testdir [workingDirectory] { + Search tests in the specified directory. + } AcceptDirectory testsDirectory + trace variable Option(-testdir) w \ + [namespace code {normalizePath Option(-testdir) ;#}] + + proc AcceptLoadFile { file } { + if {[string equal "" $file]} {return $file} + set file [file join [temporaryDirectory] $file] + return [AcceptReadable $file] + } + proc ReadLoadScript {args} { + variable Option + if {[string equal "" $Option(-loadfile)]} {return} + set tmp [open $Option(-loadfile) r] + loadScript [read $tmp] + close $tmp + } + Option -loadfile {} { + Read the script to load the tested commands from the specified file. + } AcceptLoadFile loadFile + trace variable Option(-loadfile) w [namespace code ReadLoadScript] + + proc AcceptOutFile { file } { + if {[string equal stderr $file]} {return $file} + if {[string equal stdout $file]} {return $file} + return [file join [temporaryDirectory] $file] + } + + # output goes to stdout by default + Option -outfile stdout { + Send output from test runs to the specified file. + } AcceptOutFile outputFile + trace variable Option(-outfile) w \ + [namespace code {outputChannel $Option(-outfile) ;#}] + + # errors go to stderr by default + Option -errfile stderr { + Send errors from test runs to the specified file. + } AcceptOutFile errorFile + trace variable Option(-errfile) w \ + [namespace code {errorChannel $Option(-errfile) ;#}] + +} + +##################################################################### + +# tcltest::Debug* -- +# +# Internal helper procedures to write out debug information +# dependent on the chosen level. A test shell may overide +# them, f.e. to redirect the output into a different +# channel, or even into a GUI. + +# tcltest::DebugPuts -- +# +# Prints the specified string if the current debug level is +# higher than the provided level argument. +# +# Arguments: +# level The lowest debug level triggering the output +# string The string to print out. +# +# Results: +# Prints the string. Nothing else is allowed. +# +# Side Effects: +# None. +# + +proc tcltest::DebugPuts {level string} { + variable debug + if {$debug >= $level} { + puts $string + } + return +} + +# tcltest::DebugPArray -- +# +# Prints the contents of the specified array if the current +# debug level is higher than the provided level argument +# +# Arguments: +# level The lowest debug level triggering the output +# arrayvar The name of the array to print out. +# +# Results: +# Prints the contents of the array. Nothing else is allowed. +# +# Side Effects: +# None. +# + +proc tcltest::DebugPArray {level arrayvar} { + variable debug + + if {$debug >= $level} { + catch {upvar $arrayvar $arrayvar} + parray $arrayvar + } + return +} + +# Define our own [parray] in ::tcltest that will inherit use of the [puts] +# defined in ::tcltest. NOTE: Ought to construct with [info args] and +# [info default], but can't be bothered now. If [parray] changes, then +# this will need changing too. +auto_load ::parray +proc tcltest::parray {a {pattern *}} [info body ::parray] + +# tcltest::DebugDo -- +# +# Executes the script if the current debug level is greater than +# the provided level argument +# +# Arguments: +# level The lowest debug level triggering the execution. +# script The tcl script executed upon a debug level high enough. +# +# Results: +# Arbitrary side effects, dependent on the executed script. +# +# Side Effects: +# None. +# + +proc tcltest::DebugDo {level script} { + variable debug + + if {$debug >= $level} { + uplevel 1 $script + } + return +} + +##################################################################### + +proc tcltest::Warn {msg} { + puts [outputChannel] "WARNING: $msg" +} + +# tcltest::mainThread +# +# Accessor command for tcltest variable mainThread. +# +proc tcltest::mainThread { {new ""} } { + variable mainThread + if {[llength [info level 0]] == 1} { + return $mainThread + } + set mainThread $new +} + +# tcltest::testConstraint -- +# +# sets a test constraint to a value; to do multiple constraints, +# call this proc multiple times. also returns the value of the +# named constraint if no value was supplied. +# +# Arguments: +# constraint - name of the constraint +# value - new value for constraint (should be boolean) - if not +# supplied, this is a query +# +# Results: +# content of tcltest::testConstraints($constraint) +# +# Side effects: +# none + +proc tcltest::testConstraint {constraint {value ""}} { + variable testConstraints + variable Option + DebugPuts 3 "entering testConstraint $constraint $value" + if {[llength [info level 0]] == 2} { + return $testConstraints($constraint) + } + # Check for boolean values + if {[catch {expr {$value && $value}} msg]} { + return -code error $msg + } + if {[limitConstraints] + && [lsearch -exact $Option(-constraints) $constraint] == -1} { + set value 0 + } + set testConstraints($constraint) $value +} + +# tcltest::interpreter -- +# +# the interpreter name stored in tcltest::tcltest +# +# Arguments: +# executable name +# +# Results: +# content of tcltest::tcltest +# +# Side effects: +# None. + +proc tcltest::interpreter { {interp ""} } { + variable tcltest + if {[llength [info level 0]] == 1} { + return $tcltest + } + if {[string equal {} $interp]} { + set tcltest {} + } else { + set tcltest $interp + } +} + +##################################################################### + +# tcltest::AddToSkippedBecause -- +# +# Increments the variable used to track how many tests were +# skipped because of a particular constraint. +# +# Arguments: +# constraint The name of the constraint to be modified +# +# Results: +# Modifies tcltest::skippedBecause; sets the variable to 1 if +# didn't previously exist - otherwise, it just increments it. +# +# Side effects: +# None. + +proc tcltest::AddToSkippedBecause { constraint {value 1}} { + # add the constraint to the list of constraints that kept tests + # from running + variable skippedBecause + + if {[info exists skippedBecause($constraint)]} { + incr skippedBecause($constraint) $value + } else { + set skippedBecause($constraint) $value + } + return +} + +# tcltest::PrintError -- +# +# Prints errors to tcltest::errorChannel and then flushes that +# channel, making sure that all messages are < 80 characters per +# line. +# +# Arguments: +# errorMsg String containing the error to be printed +# +# Results: +# None. +# +# Side effects: +# None. + +proc tcltest::PrintError {errorMsg} { + set InitialMessage "Error: " + set InitialMsgLen [string length $InitialMessage] + puts -nonewline [errorChannel] $InitialMessage + + # Keep track of where the end of the string is. + set endingIndex [string length $errorMsg] + + if {$endingIndex < (80 - $InitialMsgLen)} { + puts [errorChannel] $errorMsg + } else { + # Print up to 80 characters on the first line, including the + # InitialMessage. + set beginningIndex [string last " " [string range $errorMsg 0 \ + [expr {80 - $InitialMsgLen}]]] + puts [errorChannel] [string range $errorMsg 0 $beginningIndex] + + while {![string equal end $beginningIndex]} { + puts -nonewline [errorChannel] \ + [string repeat " " $InitialMsgLen] + if {($endingIndex - $beginningIndex) + < (80 - $InitialMsgLen)} { + puts [errorChannel] [string trim \ + [string range $errorMsg $beginningIndex end]] + break + } else { + set newEndingIndex [expr {[string last " " \ + [string range $errorMsg $beginningIndex \ + [expr {$beginningIndex + + (80 - $InitialMsgLen)}] + ]] + $beginningIndex}] + if {($newEndingIndex <= 0) + || ($newEndingIndex <= $beginningIndex)} { + set newEndingIndex end + } + puts [errorChannel] [string trim \ + [string range $errorMsg \ + $beginningIndex $newEndingIndex]] + set beginningIndex $newEndingIndex + } + } + } + flush [errorChannel] + return +} + +# tcltest::SafeFetch -- +# +# The following trace procedure makes it so that we can safely +# refer to non-existent members of the testConstraints array +# without causing an error. Instead, reading a non-existent +# member will return 0. This is necessary because tests are +# allowed to use constraint "X" without ensuring that +# testConstraints("X") is defined. +# +# Arguments: +# n1 - name of the array (testConstraints) +# n2 - array key value (constraint name) +# op - operation performed on testConstraints (generally r) +# +# Results: +# none +# +# Side effects: +# sets testConstraints($n2) to 0 if it's referenced but never +# before used + +proc tcltest::SafeFetch {n1 n2 op} { + variable testConstraints + DebugPuts 3 "entering SafeFetch $n1 $n2 $op" + if {[string equal {} $n2]} {return} + if {![info exists testConstraints($n2)]} { + if {[catch {testConstraint $n2 [eval [ConstraintInitializer $n2]]}]} { + testConstraint $n2 0 + } + } +} + +# tcltest::ConstraintInitializer -- +# +# Get or set a script that when evaluated in the tcltest namespace +# will return a boolean value with which to initialize the +# associated constraint. +# +# Arguments: +# constraint - name of the constraint initialized by the script +# script - the initializer script +# +# Results +# boolean value of the constraint - enabled or disabled +# +# Side effects: +# Constraint is initialized for future reference by [test] +proc tcltest::ConstraintInitializer {constraint {script ""}} { + variable ConstraintInitializer + DebugPuts 3 "entering ConstraintInitializer $constraint $script" + if {[llength [info level 0]] == 2} { + return $ConstraintInitializer($constraint) + } + # Check for boolean values + if {![info complete $script]} { + return -code error "ConstraintInitializer must be complete script" + } + set ConstraintInitializer($constraint) $script +} + +# tcltest::InitConstraints -- +# +# Call all registered constraint initializers to force initialization +# of all known constraints. +# See the tcltest man page for the list of built-in constraints defined +# in this procedure. +# +# Arguments: +# none +# +# Results: +# The testConstraints array is reset to have an index for each +# built-in test constraint. +# +# Side Effects: +# None. +# + +proc tcltest::InitConstraints {} { + variable ConstraintInitializer + initConstraintsHook + foreach constraint [array names ConstraintInitializer] { + testConstraint $constraint + } +} + +proc tcltest::DefineConstraintInitializers {} { + ConstraintInitializer singleTestInterp {singleProcess} + + # All the 'pc' constraints are here for backward compatibility and + # are not documented. They have been replaced with equivalent 'win' + # constraints. + + ConstraintInitializer unixOnly \ + {string equal $::tcl_platform(platform) unix} + ConstraintInitializer macOnly \ + {string equal $::tcl_platform(platform) macintosh} + ConstraintInitializer pcOnly \ + {string equal $::tcl_platform(platform) windows} + ConstraintInitializer winOnly \ + {string equal $::tcl_platform(platform) windows} + + ConstraintInitializer unix {testConstraint unixOnly} + ConstraintInitializer mac {testConstraint macOnly} + ConstraintInitializer pc {testConstraint pcOnly} + ConstraintInitializer win {testConstraint winOnly} + + ConstraintInitializer unixOrPc \ + {expr {[testConstraint unix] || [testConstraint pc]}} + ConstraintInitializer macOrPc \ + {expr {[testConstraint mac] || [testConstraint pc]}} + ConstraintInitializer unixOrWin \ + {expr {[testConstraint unix] || [testConstraint win]}} + ConstraintInitializer macOrWin \ + {expr {[testConstraint mac] || [testConstraint win]}} + ConstraintInitializer macOrUnix \ + {expr {[testConstraint mac] || [testConstraint unix]}} + + ConstraintInitializer nt {string equal $::tcl_platform(os) "Windows NT"} + ConstraintInitializer 95 {string equal $::tcl_platform(os) "Windows 95"} + ConstraintInitializer 98 {string equal $::tcl_platform(os) "Windows 98"} + + # The following Constraints switches are used to mark tests that + # should work, but have been temporarily disabled on certain + # platforms because they don't and we haven't gotten around to + # fixing the underlying problem. + + ConstraintInitializer tempNotPc {expr {![testConstraint pc]}} + ConstraintInitializer tempNotWin {expr {![testConstraint win]}} + ConstraintInitializer tempNotMac {expr {![testConstraint mac]}} + ConstraintInitializer tempNotUnix {expr {![testConstraint unix]}} + + # The following Constraints switches are used to mark tests that + # crash on certain platforms, so that they can be reactivated again + # when the underlying problem is fixed. + + ConstraintInitializer pcCrash {expr {![testConstraint pc]}} + ConstraintInitializer winCrash {expr {![testConstraint win]}} + ConstraintInitializer macCrash {expr {![testConstraint mac]}} + ConstraintInitializer unixCrash {expr {![testConstraint unix]}} + + # Skip empty tests + + ConstraintInitializer emptyTest {format 0} + + # By default, tests that expose known bugs are skipped. + + ConstraintInitializer knownBug {format 0} + + # By default, non-portable tests are skipped. + + ConstraintInitializer nonPortable {format 0} + + # Some tests require user interaction. + + ConstraintInitializer userInteraction {format 0} + + # Some tests must be skipped if the interpreter is not in + # interactive mode + + ConstraintInitializer interactive \ + {expr {[info exists ::tcl_interactive] && $::tcl_interactive}} + + # Some tests can only be run if the installation came from a CD + # image instead of a web image. Some tests must be skipped if you + # are running as root on Unix. Other tests can only be run if you + # are running as root on Unix. + + ConstraintInitializer root {expr \ + {[string equal unix $::tcl_platform(platform)] + && ([string equal root $::tcl_platform(user)] + || [string equal "" $::tcl_platform(user)])}} + ConstraintInitializer notRoot {expr {![testConstraint root]}} + + # Set nonBlockFiles constraint: 1 means this platform supports + # setting files into nonblocking mode. + + ConstraintInitializer nonBlockFiles { + set code [expr {[catch {set f [open defs r]}] + || [catch {fconfigure $f -blocking off}]}] + catch {close $f} + set code + } + + # Set asyncPipeClose constraint: 1 means this platform supports + # async flush and async close on a pipe. + # + # Test for SCO Unix - cannot run async flushing tests because a + # potential problem with select is apparently interfering. + # (Mark Diekhans). + + ConstraintInitializer asyncPipeClose {expr { + !([string equal unix $::tcl_platform(platform)] + && ([catch {exec uname -X | fgrep {Release = 3.2v}}] == 0))}} + + # Test to see if we have a broken version of sprintf with respect + # to the "e" format of floating-point numbers. + + ConstraintInitializer eformat {string equal [format %g 5e-5] 5e-05} + + # Test to see if execed commands such as cat, echo, rm and so forth + # are present on this machine. + + ConstraintInitializer unixExecs { + set code 1 + if {[string equal macintosh $::tcl_platform(platform)]} { + set code 0 + } + if {[string equal windows $::tcl_platform(platform)]} { + if {[catch { + set file _tcl_test_remove_me.txt + makeFile {hello} $file + }]} { + set code 0 + } elseif { + [catch {exec cat $file}] || + [catch {exec echo hello}] || + [catch {exec sh -c echo hello}] || + [catch {exec wc $file}] || + [catch {exec sleep 1}] || + [catch {exec echo abc > $file}] || + [catch {exec chmod 644 $file}] || + [catch {exec rm $file}] || + [llength [auto_execok mkdir]] == 0 || + [llength [auto_execok fgrep]] == 0 || + [llength [auto_execok grep]] == 0 || + [llength [auto_execok ps]] == 0 + } { + set code 0 + } + removeFile $file + } + set code + } + + ConstraintInitializer stdio { + set code 0 + if {![catch {set f [open "|[list [interpreter]]" w]}]} { + if {![catch {puts $f exit}]} { + if {![catch {close $f}]} { + set code 1 + } + } + } + set code + } + + # Deliberately call socket with the wrong number of arguments. The + # error message you get will indicate whether sockets are available + # on this system. + + ConstraintInitializer socket { + catch {socket} msg + string compare $msg "sockets are not available on this system" + } + + # Check for internationalization + ConstraintInitializer hasIsoLocale { + if {[llength [info commands testlocale]] == 0} { + set code 0 + } else { + set code [string length [SetIso8859_1_Locale]] + RestoreLocale + } + set code + } + +} +##################################################################### + +# Usage and command line arguments processing. + +# tcltest::PrintUsageInfo +# +# Prints out the usage information for package tcltest. This can +# be customized with the redefinition of [PrintUsageInfoHook]. +# +# Arguments: +# none +# +# Results: +# none +# +# Side Effects: +# none +proc tcltest::PrintUsageInfo {} { + puts [Usage] + PrintUsageInfoHook +} + +proc tcltest::Usage { {option ""} } { + variable Usage + variable Verify + if {[llength [info level 0]] == 1} { + set msg "Usage: [file tail [info nameofexecutable]] script " + append msg "?-help? ?flag value? ... \n" + append msg "Available flags (and valid input values) are:" + + set max 0 + set allOpts [concat -help [Configure]] + foreach opt $allOpts { + set foo [Usage $opt] + foreach [list x type($opt) usage($opt)] $foo break + set line($opt) " $opt $type($opt) " + set length($opt) [string length $line($opt)] + if {$length($opt) > $max} {set max $length($opt)} + } + set rest [expr {72 - $max}] + foreach opt $allOpts { + append msg \n$line($opt) + append msg [string repeat " " [expr {$max - $length($opt)}]] + set u [string trim $usage($opt)] + catch {append u " (default: \[[Configure $opt]])"} + regsub -all {\s*\n\s*} $u " " u + while {[string length $u] > $rest} { + set break [string wordstart $u $rest] + if {$break == 0} { + set break [string wordend $u 0] + } + append msg [string range $u 0 [expr {$break - 1}]] + set u [string trim [string range $u $break end]] + append msg \n[string repeat " " $max] + } + append msg $u + } + return $msg\n + } elseif {[string equal -help $option]} { + return [list -help "" "Display this usage information."] + } else { + set type [lindex [info args $Verify($option)] 0] + return [list $option $type $Usage($option)] + } +} + +# tcltest::ProcessFlags -- +# +# process command line arguments supplied in the flagArray - this +# is called by processCmdLineArgs. Modifies tcltest variables +# according to the content of the flagArray. +# +# Arguments: +# flagArray - array containing name/value pairs of flags +# +# Results: +# sets tcltest variables according to their values as defined by +# flagArray +# +# Side effects: +# None. + +proc tcltest::ProcessFlags {flagArray} { + # Process -help first + if {[lsearch -exact $flagArray {-help}] != -1} { + PrintUsageInfo + exit 1 + } + + if {[llength $flagArray] == 0} { + RemoveAutoConfigureTraces + } else { + set args $flagArray + while {[llength $args]>1 && [catch {eval configure $args} msg]} { + + # Something went wrong parsing $args for tcltest options + # Check whether the problem is "unknown option" + if {[regexp {^unknown option (\S+):} $msg -> option]} { + # Could be this is an option the Hook knows about + set moreOptions [processCmdLineArgsAddFlagsHook] + if {[lsearch -exact $moreOptions $option] == -1} { + # Nope. Report the error, including additional options, + # but keep going + if {[llength $moreOptions]} { + append msg ", " + append msg [join [lrange $moreOptions 0 end-1] ", "] + append msg "or [lindex $moreOptions end]" + } + Warn $msg + } + } else { + # error is something other than "unknown option" + # notify user of the error; and exit + puts [errorChannel] $msg + exit 1 + } + + # To recover, find that unknown option and remove up to it. + # then retry + while {![string equal [lindex $args 0] $option]} { + set args [lrange $args 2 end] + } + set args [lrange $args 2 end] + } + if {[llength $args] == 1} { + puts [errorChannel] \ + "missing value for option [lindex $args 0]" + exit 1 + } + } + + # Call the hook + catch { + array set flag $flagArray + processCmdLineArgsHook [array get flag] + } + return +} + +# tcltest::ProcessCmdLineArgs -- +# +# This procedure must be run after constraint initialization is +# set up (by [DefineConstraintInitializers]) because some constraints +# can be overridden. +# +# Perform configuration according to the command-line options. +# +# Arguments: +# none +# +# Results: +# Sets the above-named variables in the tcltest namespace. +# +# Side Effects: +# None. +# + +proc tcltest::ProcessCmdLineArgs {} { + variable originalEnv + variable testConstraints + + # The "argv" var doesn't exist in some cases, so use {}. + if {![info exists ::argv]} { + ProcessFlags {} + } else { + ProcessFlags $::argv + } + + # Spit out everything you know if we're at a debug level 2 or + # greater + DebugPuts 2 "Flags passed into tcltest:" + if {[info exists ::env(TCLTEST_OPTIONS)]} { + DebugPuts 2 \ + " ::env(TCLTEST_OPTIONS): $::env(TCLTEST_OPTIONS)" + } + if {[info exists ::argv]} { + DebugPuts 2 " argv: $::argv" + } + DebugPuts 2 "tcltest::debug = [debug]" + DebugPuts 2 "tcltest::testsDirectory = [testsDirectory]" + DebugPuts 2 "tcltest::workingDirectory = [workingDirectory]" + DebugPuts 2 "tcltest::temporaryDirectory = [temporaryDirectory]" + DebugPuts 2 "tcltest::outputChannel = [outputChannel]" + DebugPuts 2 "tcltest::errorChannel = [errorChannel]" + DebugPuts 2 "Original environment (tcltest::originalEnv):" + DebugPArray 2 originalEnv + DebugPuts 2 "Constraints:" + DebugPArray 2 testConstraints +} + +##################################################################### + +# Code to run the tests goes here. + +# tcltest::TestPuts -- +# +# Used to redefine puts in test environment. Stores whatever goes +# out on stdout in tcltest::outData and stderr in errData before +# sending it on to the regular puts. +# +# Arguments: +# same as standard puts +# +# Results: +# none +# +# Side effects: +# Intercepts puts; data that would otherwise go to stdout, stderr, +# or file channels specified in outputChannel and errorChannel +# does not get sent to the normal puts function. +namespace eval tcltest::Replace { + namespace export puts +} +proc tcltest::Replace::puts {args} { + variable [namespace parent]::outData + variable [namespace parent]::errData + switch [llength $args] { + 1 { + # Only the string to be printed is specified + append outData [lindex $args 0]\n + return + # return [Puts [lindex $args 0]] + } + 2 { + # Either -nonewline or channelId has been specified + if {[string equal -nonewline [lindex $args 0]]} { + append outData [lindex $args end] + return + # return [Puts -nonewline [lindex $args end]] + } else { + set channel [lindex $args 0] + set newline \n + } + } + 3 { + if {[string equal -nonewline [lindex $args 0]]} { + # Both -nonewline and channelId are specified, unless + # it's an error. -nonewline is supposed to be argv[0]. + set channel [lindex $args 1] + set newline "" + } + } + } + + if {[info exists channel]} { + if {[string equal $channel [[namespace parent]::outputChannel]] + || [string equal $channel stdout]} { + append outData [lindex $args end]$newline + return + } elseif {[string equal $channel [[namespace parent]::errorChannel]] + || [string equal $channel stderr]} { + append errData [lindex $args end]$newline + return + } + } + + # If we haven't returned by now, we don't know how to handle the + # input. Let puts handle it. + return [eval Puts $args] +} + +# tcltest::Eval -- +# +# Evaluate the script in the test environment. If ignoreOutput is +# false, store data sent to stderr and stdout in outData and +# errData. Otherwise, ignore this output altogether. +# +# Arguments: +# script Script to evaluate +# ?ignoreOutput? Indicates whether or not to ignore output +# sent to stdout & stderr +# +# Results: +# result from running the script +# +# Side effects: +# Empties the contents of outData and errData before running a +# test if ignoreOutput is set to 0. + +proc tcltest::Eval {script {ignoreOutput 1}} { + variable outData + variable errData + DebugPuts 3 "[lindex [info level 0] 0] called" + if {!$ignoreOutput} { + set outData {} + set errData {} + rename ::puts [namespace current]::Replace::Puts + namespace eval :: \ + [list namespace import [namespace origin Replace::puts]] + namespace import Replace::puts + } + set result [uplevel 1 $script] + if {!$ignoreOutput} { + namespace forget puts + namespace eval :: namespace forget puts + rename [namespace current]::Replace::Puts ::puts + } + return $result +} + +# tcltest::CompareStrings -- +# +# compares the expected answer to the actual answer, depending on +# the mode provided. Mode determines whether a regexp, exact, +# glob or custom comparison is done. +# +# Arguments: +# actual - string containing the actual result +# expected - pattern to be matched against +# mode - type of comparison to be done +# +# Results: +# result of the match +# +# Side effects: +# None. + +proc tcltest::CompareStrings {actual expected mode} { + variable CustomMatch + if {![info exists CustomMatch($mode)]} { + return -code error "No matching command registered for `-match $mode'" + } + set match [namespace eval :: $CustomMatch($mode) [list $expected $actual]] + if {[catch {expr {$match && $match}} result]} { + return -code error "Invalid result from `-match $mode' command: $result" + } + return $match +} + +# tcltest::customMatch -- +# +# registers a command to be called when a particular type of +# matching is required. +# +# Arguments: +# nickname - Keyword for the type of matching +# cmd - Incomplete command that implements that type of matching +# when completed with expected string and actual string +# and then evaluated. +# +# Results: +# None. +# +# Side effects: +# Sets the variable tcltest::CustomMatch + +proc tcltest::customMatch {mode script} { + variable CustomMatch + if {![info complete $script]} { + return -code error \ + "invalid customMatch script; can't evaluate after completion" + } + set CustomMatch($mode) $script +} + +# tcltest::SubstArguments list +# +# This helper function takes in a list of words, then perform a +# substitution on the list as though each word in the list is a separate +# argument to the Tcl function. For example, if this function is +# invoked as: +# +# SubstArguments {$a {$a}} +# +# Then it is as though the function is invoked as: +# +# SubstArguments $a {$a} +# +# This code is adapted from Paul Duffin's function "SplitIntoWords". +# The original function can be found on: +# +# http://purl.org/thecliff/tcl/wiki/858.html +# +# Results: +# a list containing the result of the substitution +# +# Exceptions: +# An error may occur if the list containing unbalanced quote or +# unknown variable. +# +# Side Effects: +# None. +# + +proc tcltest::SubstArguments {argList} { + + # We need to split the argList up into tokens but cannot use list + # operations as they throw away some significant quoting, and + # [split] ignores braces as it should. Therefore what we do is + # gradually build up a string out of whitespace seperated strings. + # We cannot use [split] to split the argList into whitespace + # separated strings as it throws away the whitespace which maybe + # important so we have to do it all by hand. + + set result {} + set token "" + + while {[string length $argList]} { + # Look for the next word containing a quote: " { } + if {[regexp -indices {[^ \t\n]*[\"\{\}]+[^ \t\n]*} \ + $argList all]} { + # Get the text leading up to this word, but not including + # this word, from the argList. + set text [string range $argList 0 \ + [expr {[lindex $all 0] - 1}]] + # Get the word with the quote + set word [string range $argList \ + [lindex $all 0] [lindex $all 1]] + + # Remove all text up to and including the word from the + # argList. + set argList [string range $argList \ + [expr {[lindex $all 1] + 1}] end] + } else { + # Take everything up to the end of the argList. + set text $argList + set word {} + set argList {} + } + + if {$token != {}} { + # If we saw a word with quote before, then there is a + # multi-word token starting with that word. In this case, + # add the text and the current word to this token. + append token $text $word + } else { + # Add the text to the result. There is no need to parse + # the text because it couldn't be a part of any multi-word + # token. Then start a new multi-word token with the word + # because we need to pass this token to the Tcl parser to + # check for balancing quotes + append result $text + set token $word + } + + if { [catch {llength $token} length] == 0 && $length == 1} { + # The token is a valid list so add it to the result. + # lappend result [string trim $token] + append result \{$token\} + set token {} + } + } + + # If the last token has not been added to the list then there + # is a problem. + if { [string length $token] } { + error "incomplete token \"$token\"" + } + + return $result +} + + +# tcltest::test -- +# +# This procedure runs a test and prints an error message if the test +# fails. If verbose has been set, it also prints a message even if the +# test succeeds. The test will be skipped if it doesn't match the +# match variable, if it matches an element in skip, or if one of the +# elements of "constraints" turns out not to be true. +# +# If testLevel is 1, then this is a top level test, and we record +# pass/fail information; otherwise, this information is not logged and +# is not added to running totals. +# +# Attributes: +# Only description is a required attribute. All others are optional. +# Default values are indicated. +# +# constraints - A list of one or more keywords, each of which +# must be the name of an element in the array +# "testConstraints". If any of these elements is +# zero, the test is skipped. This attribute is +# optional; default is {} +# body - Script to run to carry out the test. It must +# return a result that can be checked for +# correctness. This attribute is optional; +# default is {} +# result - Expected result from script. This attribute is +# optional; default is {}. +# output - Expected output sent to stdout. This attribute +# is optional; default is {}. +# errorOutput - Expected output sent to stderr. This attribute +# is optional; default is {}. +# returnCodes - Expected return codes. This attribute is +# optional; default is {0 2}. +# setup - Code to run before $script (above). This +# attribute is optional; default is {}. +# cleanup - Code to run after $script (above). This +# attribute is optional; default is {}. +# match - specifies type of matching to do on result, +# output, errorOutput; this must be a string +# previously registered by a call to [customMatch]. +# The strings exact, glob, and regexp are pre-registered +# by the tcltest package. Default value is exact. +# +# Arguments: +# name - Name of test, in the form foo-1.2. +# description - Short textual description of the test, to +# help humans understand what it does. +# +# Results: +# None. +# +# Side effects: +# Just about anything is possible depending on the test. +# + +proc tcltest::test {name description args} { + global tcl_platform + variable testLevel + variable coreModTime + DebugPuts 3 "test $name $args" + DebugDo 1 { + variable TestNames + catch { + puts "test name '$name' re-used; prior use in $TestNames($name)" + } + set TestNames($name) [info script] + } + + FillFilesExisted + incr testLevel + + # Pre-define everything to null except output and errorOutput. We + # determine whether or not to trap output based on whether or not + # these variables (output & errorOutput) are defined. + foreach item {constraints setup cleanup body result returnCodes + match} { + set $item {} + } + + # Set the default match mode + set match exact + + # Set the default match values for return codes (0 is the standard + # expected return value if everything went well; 2 represents + # 'return' being used in the test script). + set returnCodes [list 0 2] + + # The old test format can't have a 3rd argument (constraints or + # script) that starts with '-'. + if {[string match -* [lindex $args 0]] + || ([llength $args] <= 1)} { + if {[llength $args] == 1} { + set list [SubstArguments [lindex $args 0]] + foreach {element value} $list { + set testAttributes($element) $value + } + foreach item {constraints match setup body cleanup \ + result returnCodes output errorOutput} { + if {[info exists testAttributes(-$item)]} { + set testAttributes(-$item) [uplevel 1 \ + ::concat $testAttributes(-$item)] + } + } + } else { + array set testAttributes $args + } + + set validFlags {-setup -cleanup -body -result -returnCodes \ + -match -output -errorOutput -constraints} + + foreach flag [array names testAttributes] { + if {[lsearch -exact $validFlags $flag] == -1} { + incr testLevel -1 + set sorted [lsort $validFlags] + set options [join [lrange $sorted 0 end-1] ", "] + append options ", or [lindex $sorted end]" + return -code error "bad option \"$flag\": must be $options" + } + } + + # store whatever the user gave us + foreach item [array names testAttributes] { + set [string trimleft $item "-"] $testAttributes($item) + } + + # Check the values supplied for -match + variable CustomMatch + if {[lsearch [array names CustomMatch] $match] == -1} { + incr testLevel -1 + set sorted [lsort [array names CustomMatch]] + set values [join [lrange $sorted 0 end-1] ", "] + append values ", or [lindex $sorted end]" + return -code error "bad -match value \"$match\":\ + must be $values" + } + + # Replace symbolic valies supplied for -returnCodes + foreach {strcode numcode} {ok 0 normal 0 error 1 return 2 break 3 continue 4} { + set returnCodes [string map -nocase [list $strcode $numcode] $returnCodes] + } + } else { + # This is parsing for the old test command format; it is here + # for backward compatibility. + set result [lindex $args end] + if {[llength $args] == 2} { + set body [lindex $args 0] + } elseif {[llength $args] == 3} { + set constraints [lindex $args 0] + set body [lindex $args 1] + } else { + incr testLevel -1 + return -code error "wrong # args:\ + should be \"test name desc ?options?\"" + } + } + + if {[Skipped $name $constraints]} { + incr testLevel -1 + return + } + + # Save information about the core file. + if {[preserveCore]} { + if {[file exists [file join [workingDirectory] core]]} { + set coreModTime [file mtime [file join [workingDirectory] core]] + } + } + + # First, run the setup script + set code [catch {uplevel 1 $setup} setupMsg] + if {$code == 1} { + set errorInfo(setup) $::errorInfo + set errorCode(setup) $::errorCode + } + set setupFailure [expr {$code != 0}] + + # Only run the test body if the setup was successful + if {!$setupFailure} { + + # Verbose notification of $body start + if {[IsVerbose start]} { + puts [outputChannel] "---- $name start" + flush [outputChannel] + } + + set command [list [namespace origin RunTest] $name $body] + if {[info exists output] || [info exists errorOutput]} { + set testResult [uplevel 1 [list [namespace origin Eval] $command 0]] + } else { + set testResult [uplevel 1 [list [namespace origin Eval] $command 1]] + } + foreach {actualAnswer returnCode} $testResult break + if {$returnCode == 1} { + set errorInfo(body) $::errorInfo + set errorCode(body) $::errorCode + } + } + + # Always run the cleanup script + set code [catch {uplevel 1 $cleanup} cleanupMsg] + if {$code == 1} { + set errorInfo(cleanup) $::errorInfo + set errorCode(cleanup) $::errorCode + } + set cleanupFailure [expr {$code != 0}] + + set coreFailure 0 + set coreMsg "" + # check for a core file first - if one was created by the test, + # then the test failed + if {[preserveCore]} { + if {[file exists [file join [workingDirectory] core]]} { + # There's only a test failure if there is a core file + # and (1) there previously wasn't one or (2) the new + # one is different from the old one. + if {[info exists coreModTime]} { + if {$coreModTime != [file mtime \ + [file join [workingDirectory] core]]} { + set coreFailure 1 + } + } else { + set coreFailure 1 + } + + if {([preserveCore] > 1) && ($coreFailure)} { + append coreMsg "\nMoving file to:\ + [file join [temporaryDirectory] core-$name]" + catch {file rename -force \ + [file join [workingDirectory] core] \ + [file join [temporaryDirectory] core-$name] + } msg + if {[string length $msg] > 0} { + append coreMsg "\nError:\ + Problem renaming core file: $msg" + } + } + } + } + + # check if the return code matched the expected return code + set codeFailure 0 + if {!$setupFailure && [lsearch -exact $returnCodes $returnCode] == -1} { + set codeFailure 1 + } + + # If expected output/error strings exist, we have to compare + # them. If the comparison fails, then so did the test. + set outputFailure 0 + variable outData + if {[info exists output] && !$codeFailure} { + if {[set outputCompare [catch { + CompareStrings $outData $output $match + } outputMatch]] == 0} { + set outputFailure [expr {!$outputMatch}] + } else { + set outputFailure 1 + } + } + + set errorFailure 0 + variable errData + if {[info exists errorOutput] && !$codeFailure} { + if {[set errorCompare [catch { + CompareStrings $errData $errorOutput $match + } errorMatch]] == 0} { + set errorFailure [expr {!$errorMatch}] + } else { + set errorFailure 1 + } + } + + # check if the answer matched the expected answer + # Only check if we ran the body of the test (no setup failure) + if {$setupFailure || $codeFailure} { + set scriptFailure 0 + } elseif {[set scriptCompare [catch { + CompareStrings $actualAnswer $result $match + } scriptMatch]] == 0} { + set scriptFailure [expr {!$scriptMatch}] + } else { + set scriptFailure 1 + } + + # if we didn't experience any failures, then we passed + variable numTests + if {!($setupFailure || $cleanupFailure || $coreFailure + || $outputFailure || $errorFailure || $codeFailure + || $scriptFailure)} { + if {$testLevel == 1} { + incr numTests(Passed) + if {[IsVerbose pass]} { + puts [outputChannel] "++++ $name PASSED" + } + } + incr testLevel -1 + return + } + + # We know the test failed, tally it... + if {$testLevel == 1} { + incr numTests(Failed) + } + + # ... then report according to the type of failure + variable currentFailure true + if {![IsVerbose body]} { + set body "" + } + puts [outputChannel] "\n==== $name\ + [string trim $description] FAILED" + if {[string length $body]} { + puts [outputChannel] "==== Contents of test case:" + puts [outputChannel] $body + } + if {$setupFailure} { + puts [outputChannel] "---- Test setup\ + failed:\n$setupMsg" + if {[info exists errorInfo(setup)]} { + puts [outputChannel] "---- errorInfo(setup): $errorInfo(setup)" + puts [outputChannel] "---- errorCode(setup): $errorCode(setup)" + } + } + if {$scriptFailure} { + if {$scriptCompare} { + puts [outputChannel] "---- Error testing result: $scriptMatch" + } else { + puts [outputChannel] "---- Result was:\n$actualAnswer" + puts [outputChannel] "---- Result should have been\ + ($match matching):\n$result" + } + } + if {$codeFailure} { + switch -- $returnCode { + 0 { set msg "Test completed normally" } + 1 { set msg "Test generated error" } + 2 { set msg "Test generated return exception" } + 3 { set msg "Test generated break exception" } + 4 { set msg "Test generated continue exception" } + default { set msg "Test generated exception" } + } + puts [outputChannel] "---- $msg; Return code was: $returnCode" + puts [outputChannel] "---- Return code should have been\ + one of: $returnCodes" + if {[IsVerbose error]} { + if {[info exists errorInfo(body)] && ([lsearch $returnCodes 1]<0)} { + puts [outputChannel] "---- errorInfo: $errorInfo(body)" + puts [outputChannel] "---- errorCode: $errorCode(body)" + } + } + } + if {$outputFailure} { + if {$outputCompare} { + puts [outputChannel] "---- Error testing output: $outputMatch" + } else { + puts [outputChannel] "---- Output was:\n$outData" + puts [outputChannel] "---- Output should have been\ + ($match matching):\n$output" + } + } + if {$errorFailure} { + if {$errorCompare} { + puts [outputChannel] "---- Error testing errorOutput: $errorMatch" + } else { + puts [outputChannel] "---- Error output was:\n$errData" + puts [outputChannel] "---- Error output should have\ + been ($match matching):\n$errorOutput" + } + } + if {$cleanupFailure} { + puts [outputChannel] "---- Test cleanup failed:\n$cleanupMsg" + if {[info exists errorInfo(cleanup)]} { + puts [outputChannel] "---- errorInfo(cleanup): $errorInfo(cleanup)" + puts [outputChannel] "---- errorCode(cleanup): $errorCode(cleanup)" + } + } + if {$coreFailure} { + puts [outputChannel] "---- Core file produced while running\ + test! $coreMsg" + } + puts [outputChannel] "==== $name FAILED\n" + + incr testLevel -1 + return +} + +# Skipped -- +# +# Given a test name and it constraints, returns a boolean indicating +# whether the current configuration says the test should be skipped. +# +# Side Effects: Maintains tally of total tests seen and tests skipped. +# +proc tcltest::Skipped {name constraints} { + variable testLevel + variable numTests + variable testConstraints + + if {$testLevel == 1} { + incr numTests(Total) + } + # skip the test if it's name matches an element of skip + foreach pattern [skip] { + if {[string match $pattern $name]} { + if {$testLevel == 1} { + incr numTests(Skipped) + DebugDo 1 {AddToSkippedBecause userSpecifiedSkip} + } + return 1 + } + } + # skip the test if it's name doesn't match any element of match + set ok 0 + foreach pattern [match] { + if {[string match $pattern $name]} { + set ok 1 + break + } + } + if {!$ok} { + if {$testLevel == 1} { + incr numTests(Skipped) + DebugDo 1 {AddToSkippedBecause userSpecifiedNonMatch} + } + return 1 + } + if {[string equal {} $constraints]} { + # If we're limited to the listed constraints and there aren't + # any listed, then we shouldn't run the test. + if {[limitConstraints]} { + AddToSkippedBecause userSpecifiedLimitConstraint + if {$testLevel == 1} { + incr numTests(Skipped) + } + return 1 + } + } else { + # "constraints" argument exists; + # make sure that the constraints are satisfied. + + set doTest 0 + if {[string match {*[$\[]*} $constraints] != 0} { + # full expression, e.g. {$foo > [info tclversion]} + catch {set doTest [uplevel #0 expr $constraints]} + } elseif {[regexp {[^.a-zA-Z0-9 \n\r\t]+} $constraints] != 0} { + # something like {a || b} should be turned into + # $testConstraints(a) || $testConstraints(b). + regsub -all {[.\w]+} $constraints {$testConstraints(&)} c + catch {set doTest [eval expr $c]} + } elseif {![catch {llength $constraints}]} { + # just simple constraints such as {unixOnly fonts}. + set doTest 1 + foreach constraint $constraints { + if {(![info exists testConstraints($constraint)]) \ + || (!$testConstraints($constraint))} { + set doTest 0 + + # store the constraint that kept the test from + # running + set constraints $constraint + break + } + } + } + + if {$doTest == 0} { + if {[IsVerbose skip]} { + puts [outputChannel] "++++ $name SKIPPED: $constraints" + } + + if {$testLevel == 1} { + incr numTests(Skipped) + AddToSkippedBecause $constraints + } + return 1 + } + } + return 0 +} + +# RunTest -- +# +# This is where the body of a test is evaluated. The combination of +# [RunTest] and [Eval] allows the output and error output of the test +# body to be captured for comparison against the expected values. + +proc tcltest::RunTest {name script} { + DebugPuts 3 "Running $name {$script}" + + # If there is no "memory" command (because memory debugging isn't + # enabled), then don't attempt to use the command. + + if {[llength [info commands memory]] == 1} { + memory tag $name + } + + set code [catch {uplevel 1 $script} actualAnswer] + + return [list $actualAnswer $code] +} + +##################################################################### + +# tcltest::cleanupTestsHook -- +# +# This hook allows a harness that builds upon tcltest to specify +# additional things that should be done at cleanup. +# + +if {[llength [info commands tcltest::cleanupTestsHook]] == 0} { + proc tcltest::cleanupTestsHook {} {} +} + +# tcltest::cleanupTests -- +# +# Remove files and dirs created using the makeFile and makeDirectory +# commands since the last time this proc was invoked. +# +# Print the names of the files created without the makeFile command +# since the tests were invoked. +# +# Print the number tests (total, passed, failed, and skipped) since the +# tests were invoked. +# +# Restore original environment (as reported by special variable env). +# +# Arguments: +# calledFromAllFile - if 0, behave as if we are running a single +# test file within an entire suite of tests. if we aren't running +# a single test file, then don't report status. check for new +# files created during the test run and report on them. if 1, +# report collated status from all the test file runs. +# +# Results: +# None. +# +# Side Effects: +# None +# + +proc tcltest::cleanupTests {{calledFromAllFile 0}} { + variable filesMade + variable filesExisted + variable createdNewFiles + variable testSingleFile + variable numTests + variable numTestFiles + variable failFiles + variable skippedBecause + variable currentFailure + variable originalEnv + variable originalTclPlatform + variable coreModTime + + FillFilesExisted + set testFileName [file tail [info script]] + + # Call the cleanup hook + cleanupTestsHook + + # Remove files and directories created by the makeFile and + # makeDirectory procedures. Record the names of files in + # workingDirectory that were not pre-existing, and associate them + # with the test file that created them. + + if {!$calledFromAllFile} { + foreach file $filesMade { + if {[file exists $file]} { + DebugDo 1 {Warn "cleanupTests deleting $file..."} + catch {file delete -force $file} + } + } + set currentFiles {} + foreach file [glob -nocomplain \ + -directory [temporaryDirectory] *] { + lappend currentFiles [file tail $file] + } + set newFiles {} + foreach file $currentFiles { + if {[lsearch -exact $filesExisted $file] == -1} { + lappend newFiles $file + } + } + set filesExisted $currentFiles + if {[llength $newFiles] > 0} { + set createdNewFiles($testFileName) $newFiles + } + } + + if {$calledFromAllFile || $testSingleFile} { + + # print stats + + puts -nonewline [outputChannel] "$testFileName:" + foreach index [list "Total" "Passed" "Skipped" "Failed"] { + puts -nonewline [outputChannel] \ + "\t$index\t$numTests($index)" + } + puts [outputChannel] "" + + # print number test files sourced + # print names of files that ran tests which failed + + if {$calledFromAllFile} { + puts [outputChannel] \ + "Sourced $numTestFiles Test Files." + set numTestFiles 0 + if {[llength $failFiles] > 0} { + puts [outputChannel] \ + "Files with failing tests: $failFiles" + set failFiles {} + } + } + + # if any tests were skipped, print the constraints that kept + # them from running. + + set constraintList [array names skippedBecause] + if {[llength $constraintList] > 0} { + puts [outputChannel] \ + "Number of tests skipped for each constraint:" + foreach constraint [lsort $constraintList] { + puts [outputChannel] \ + "\t$skippedBecause($constraint)\t$constraint" + unset skippedBecause($constraint) + } + } + + # report the names of test files in createdNewFiles, and reset + # the array to be empty. + + set testFilesThatTurded [lsort [array names createdNewFiles]] + if {[llength $testFilesThatTurded] > 0} { + puts [outputChannel] "Warning: files left behind:" + foreach testFile $testFilesThatTurded { + puts [outputChannel] \ + "\t$testFile:\t$createdNewFiles($testFile)" + unset createdNewFiles($testFile) + } + } + + # reset filesMade, filesExisted, and numTests + + set filesMade {} + foreach index [list "Total" "Passed" "Skipped" "Failed"] { + set numTests($index) 0 + } + + # exit only if running Tk in non-interactive mode + # This should be changed to determine if an event + # loop is running, which is the real issue. + # Actually, this doesn't belong here at all. A package + # really has no business [exit]-ing an application. + if {![catch {package present Tk}] && ![testConstraint interactive]} { + exit + } + } else { + + # if we're deferring stat-reporting until all files are sourced, + # then add current file to failFile list if any tests in this + # file failed + + if {$currentFailure \ + && ([lsearch -exact $failFiles $testFileName] == -1)} { + lappend failFiles $testFileName + } + set currentFailure false + + # restore the environment to the state it was in before this package + # was loaded + + set newEnv {} + set changedEnv {} + set removedEnv {} + foreach index [array names ::env] { + if {![info exists originalEnv($index)]} { + lappend newEnv $index + unset ::env($index) + } else { + if {$::env($index) != $originalEnv($index)} { + lappend changedEnv $index + set ::env($index) $originalEnv($index) + } + } + } + foreach index [array names originalEnv] { + if {![info exists ::env($index)]} { + lappend removedEnv $index + set ::env($index) $originalEnv($index) + } + } + if {[llength $newEnv] > 0} { + puts [outputChannel] \ + "env array elements created:\t$newEnv" + } + if {[llength $changedEnv] > 0} { + puts [outputChannel] \ + "env array elements changed:\t$changedEnv" + } + if {[llength $removedEnv] > 0} { + puts [outputChannel] \ + "env array elements removed:\t$removedEnv" + } + + set changedTclPlatform {} + foreach index [array names originalTclPlatform] { + if {$::tcl_platform($index) \ + != $originalTclPlatform($index)} { + lappend changedTclPlatform $index + set ::tcl_platform($index) $originalTclPlatform($index) + } + } + if {[llength $changedTclPlatform] > 0} { + puts [outputChannel] "tcl_platform array elements\ + changed:\t$changedTclPlatform" + } + + if {[file exists [file join [workingDirectory] core]]} { + if {[preserveCore] > 1} { + puts "rename core file (> 1)" + puts [outputChannel] "produced core file! \ + Moving file to: \ + [file join [temporaryDirectory] core-$testFileName]" + catch {file rename -force \ + [file join [workingDirectory] core] \ + [file join [temporaryDirectory] core-$testFileName] + } msg + if {[string length $msg] > 0} { + PrintError "Problem renaming file: $msg" + } + } else { + # Print a message if there is a core file and (1) there + # previously wasn't one or (2) the new one is different + # from the old one. + + if {[info exists coreModTime]} { + if {$coreModTime != [file mtime \ + [file join [workingDirectory] core]]} { + puts [outputChannel] "A core file was created!" + } + } else { + puts [outputChannel] "A core file was created!" + } + } + } + } + flush [outputChannel] + flush [errorChannel] + return +} + +##################################################################### + +# Procs that determine which tests/test files to run + +# tcltest::GetMatchingFiles +# +# Looks at the patterns given to match and skip files and uses +# them to put together a list of the tests that will be run. +# +# Arguments: +# directory to search +# +# Results: +# The constructed list is returned to the user. This will +# primarily be used in 'all.tcl' files. It is used in +# runAllTests. +# +# Side Effects: +# None + +# a lower case version is needed for compatibility with tcltest 1.0 +proc tcltest::getMatchingFiles args {eval GetMatchingFiles $args} + +proc tcltest::GetMatchingFiles { args } { + if {[llength $args]} { + set dirList $args + } else { + # Finding tests only in [testsDirectory] is normal operation. + # This procedure is written to accept multiple directory arguments + # only to satisfy version 1 compatibility. + set dirList [list [testsDirectory]] + } + + set matchingFiles [list] + foreach directory $dirList { + + # List files in $directory that match patterns to run. + set matchFileList [list] + foreach match [matchFiles] { + set matchFileList [concat $matchFileList \ + [glob -directory $directory -types {b c f p s} \ + -nocomplain -- $match]] + } + + # List files in $directory that match patterns to skip. + set skipFileList [list] + foreach skip [skipFiles] { + set skipFileList [concat $skipFileList \ + [glob -directory $directory -types {b c f p s} \ + -nocomplain -- $skip]] + } + + # Add to result list all files in match list and not in skip list + foreach file $matchFileList { + if {[lsearch -exact $skipFileList $file] == -1} { + lappend matchingFiles $file + } + } + } + + if {[llength $matchingFiles] == 0} { + PrintError "No test files remain after applying your match and\ + skip patterns!" + } + return $matchingFiles +} + +# tcltest::GetMatchingDirectories -- +# +# Looks at the patterns given to match and skip directories and +# uses them to put together a list of the test directories that we +# should attempt to run. (Only subdirectories containing an +# "all.tcl" file are put into the list.) +# +# Arguments: +# root directory from which to search +# +# Results: +# The constructed list is returned to the user. This is used in +# the primary all.tcl file. +# +# Side Effects: +# None. + +proc tcltest::GetMatchingDirectories {rootdir} { + + # Determine the skip list first, to avoid [glob]-ing over subdirectories + # we're going to throw away anyway. Be sure we skip the $rootdir if it + # comes up to avoid infinite loops. + set skipDirs [list $rootdir] + foreach pattern [skipDirectories] { + set skipDirs [concat $skipDirs [glob -directory $rootdir -types d \ + -nocomplain -- $pattern]] + } + + # Now step through the matching directories, prune out the skipped ones + # as you go. + set matchDirs [list] + foreach pattern [matchDirectories] { + foreach path [glob -directory $rootdir -types d -nocomplain -- \ + $pattern] { + if {[lsearch -exact $skipDirs $path] == -1} { + set matchDirs [concat $matchDirs [GetMatchingDirectories $path]] + if {[file exists [file join $path all.tcl]]} { + lappend matchDirs $path + } + } + } + } + + if {[llength $matchDirs] == 0} { + DebugPuts 1 "No test directories remain after applying match\ + and skip patterns!" + } + return $matchDirs +} + +# tcltest::runAllTests -- +# +# prints output and sources test files according to the match and +# skip patterns provided. after sourcing test files, it goes on +# to source all.tcl files in matching test subdirectories. +# +# Arguments: +# shell being tested +# +# Results: +# None. +# +# Side effects: +# None. + +proc tcltest::runAllTests { {shell ""} } { + variable testSingleFile + variable numTestFiles + variable numTests + variable failFiles + + FillFilesExisted + if {[llength [info level 0]] == 1} { + set shell [interpreter] + } + + set testSingleFile false + + puts [outputChannel] "Tests running in interp: $shell" + puts [outputChannel] "Tests located in: [testsDirectory]" + puts [outputChannel] "Tests running in: [workingDirectory]" + puts [outputChannel] "Temporary files stored in\ + [temporaryDirectory]" + + # [file system] first available in Tcl 8.4 + if {![catch {file system [testsDirectory]} result] + && ![string equal native [lindex $result 0]]} { + # If we aren't running in the native filesystem, then we must + # run the tests in a single process (via 'source'), because + # trying to run then via a pipe will fail since the files don't + # really exist. + singleProcess 1 + } + + if {[singleProcess]} { + puts [outputChannel] \ + "Test files sourced into current interpreter" + } else { + puts [outputChannel] \ + "Test files run in separate interpreters" + } + if {[llength [skip]] > 0} { + puts [outputChannel] "Skipping tests that match: [skip]" + } + puts [outputChannel] "Running tests that match: [match]" + + if {[llength [skipFiles]] > 0} { + puts [outputChannel] \ + "Skipping test files that match: [skipFiles]" + } + if {[llength [matchFiles]] > 0} { + puts [outputChannel] \ + "Only running test files that match: [matchFiles]" + } + + set timeCmd {clock format [clock seconds]} + puts [outputChannel] "Tests began at [eval $timeCmd]" + + # Run each of the specified tests + foreach file [lsort [GetMatchingFiles]] { + set tail [file tail $file] + puts [outputChannel] $tail + flush [outputChannel] + + if {[singleProcess]} { + incr numTestFiles + uplevel 1 [list ::source $file] + } else { + # Pass along our configuration to the child processes. + # EXCEPT for the -outfile, because the parent process + # needs to read and process output of children. + set childargv [list] + foreach opt [Configure] { + if {[string equal $opt -outfile]} {continue} + lappend childargv $opt [Configure $opt] + } + set cmd [linsert $childargv 0 | $shell $file] + if {[catch { + incr numTestFiles + set pipeFd [open $cmd "r"] + while {[gets $pipeFd line] >= 0} { + if {[regexp [join { + {^([^:]+):\t} + {Total\t([0-9]+)\t} + {Passed\t([0-9]+)\t} + {Skipped\t([0-9]+)\t} + {Failed\t([0-9]+)} + } ""] $line null testFile \ + Total Passed Skipped Failed]} { + foreach index {Total Passed Skipped Failed} { + incr numTests($index) [set $index] + } + if {$Failed > 0} { + lappend failFiles $testFile + } + } elseif {[regexp [join { + {^Number of tests skipped } + {for each constraint:} + {|^\t(\d+)\t(.+)$} + } ""] $line match skipped constraint]} { + if {[string match \t* $match]} { + AddToSkippedBecause $constraint $skipped + } + } else { + puts [outputChannel] $line + } + } + close $pipeFd + } msg]} { + puts [outputChannel] "Test file error: $msg" + # append the name of the test to a list to be reported + # later + lappend testFileFailures $file + } + } + } + + # cleanup + puts [outputChannel] "\nTests ended at [eval $timeCmd]" + cleanupTests 1 + if {[info exists testFileFailures]} { + puts [outputChannel] "\nTest files exiting with errors: \n" + foreach file $testFileFailures { + puts [outputChannel] " [file tail $file]\n" + } + } + + # Checking for subdirectories in which to run tests + foreach directory [GetMatchingDirectories [testsDirectory]] { + set dir [file tail $directory] + puts [outputChannel] [string repeat ~ 44] + puts [outputChannel] "$dir test began at [eval $timeCmd]\n" + + uplevel 1 [list ::source [file join $directory all.tcl]] + + set endTime [eval $timeCmd] + puts [outputChannel] "\n$dir test ended at $endTime" + puts [outputChannel] "" + puts [outputChannel] [string repeat ~ 44] + } + return +} + +##################################################################### + +# Test utility procs - not used in tcltest, but may be useful for +# testing. + +# tcltest::loadTestedCommands -- +# +# Uses the specified script to load the commands to test. Allowed to +# be empty, as the tested commands could have been compiled into the +# interpreter. +# +# Arguments +# none +# +# Results +# none +# +# Side Effects: +# none. + +proc tcltest::loadTestedCommands {} { + variable l + if {[string equal {} [loadScript]]} { + return + } + + return [uplevel 1 [loadScript]] +} + +# tcltest::saveState -- +# +# Save information regarding what procs and variables exist. +# +# Arguments: +# none +# +# Results: +# Modifies the variable saveState +# +# Side effects: +# None. + +proc tcltest::saveState {} { + variable saveState + uplevel 1 [list ::set [namespace which -variable saveState]] \ + {[::list [::info procs] [::info vars]]} + DebugPuts 2 "[lindex [info level 0] 0]: $saveState" + return +} + +# tcltest::restoreState -- +# +# Remove procs and variables that didn't exist before the call to +# [saveState]. +# +# Arguments: +# none +# +# Results: +# Removes procs and variables from your environment if they don't +# exist in the saveState variable. +# +# Side effects: +# None. + +proc tcltest::restoreState {} { + variable saveState + foreach p [uplevel 1 {::info procs}] { + if {([lsearch [lindex $saveState 0] $p] < 0) + && ![string equal [namespace current]::$p \ + [uplevel 1 [list ::namespace origin $p]]]} { + + DebugPuts 2 "[lindex [info level 0] 0]: Removing proc $p" + uplevel 1 [list ::catch [list ::rename $p {}]] + } + } + foreach p [uplevel 1 {::info vars}] { + if {[lsearch [lindex $saveState 1] $p] < 0} { + DebugPuts 2 "[lindex [info level 0] 0]:\ + Removing variable $p" + uplevel 1 [list ::catch [list ::unset $p]] + } + } + return +} + +# tcltest::normalizeMsg -- +# +# Removes "extra" newlines from a string. +# +# Arguments: +# msg String to be modified +# +# Results: +# string with extra newlines removed +# +# Side effects: +# None. + +proc tcltest::normalizeMsg {msg} { + regsub "\n$" [string tolower $msg] "" msg + set msg [string map [list "\n\n" "\n"] $msg] + return [string map [list "\n\}" "\}"] $msg] +} + +# tcltest::makeFile -- +# +# Create a new file with the name , and write to it. +# +# If this file hasn't been created via makeFile since the last time +# cleanupTests was called, add it to the $filesMade list, so it will be +# removed by the next call to cleanupTests. +# +# Arguments: +# contents content of the new file +# name name of the new file +# directory directory name for new file +# +# Results: +# absolute path to the file created +# +# Side effects: +# None. + +proc tcltest::makeFile {contents name {directory ""}} { + variable filesMade + FillFilesExisted + + if {[llength [info level 0]] == 3} { + set directory [temporaryDirectory] + } + + set fullName [file join $directory $name] + + DebugPuts 3 "[lindex [info level 0] 0]:\ + putting ``$contents'' into $fullName" + + set fd [open $fullName w] + fconfigure $fd -translation lf + if {[string equal [string index $contents end] \n]} { + puts -nonewline $fd $contents + } else { + puts $fd $contents + } + close $fd + + if {[lsearch -exact $filesMade $fullName] == -1} { + lappend filesMade $fullName + } + return $fullName +} + +# tcltest::removeFile -- +# +# Removes the named file from the filesystem +# +# Arguments: +# name file to be removed +# directory directory from which to remove file +# +# Results: +# return value from [file delete] +# +# Side effects: +# None. + +proc tcltest::removeFile {name {directory ""}} { + variable filesMade + FillFilesExisted + if {[llength [info level 0]] == 2} { + set directory [temporaryDirectory] + } + set fullName [file join $directory $name] + DebugPuts 3 "[lindex [info level 0] 0]: removing $fullName" + set idx [lsearch -exact $filesMade $fullName] + set filesMade [lreplace $filesMade $idx $idx] + if {$idx == -1} { + DebugDo 1 { + Warn "removeFile removing \"$fullName\":\n not created by makeFile" + } + } + if {![file isfile $fullName]} { + DebugDo 1 { + Warn "removeFile removing \"$fullName\":\n not a file" + } + } + return [file delete $fullName] +} + +# tcltest::makeDirectory -- +# +# Create a new dir with the name . +# +# If this dir hasn't been created via makeDirectory since the last time +# cleanupTests was called, add it to the $directoriesMade list, so it +# will be removed by the next call to cleanupTests. +# +# Arguments: +# name name of the new directory +# directory directory in which to create new dir +# +# Results: +# absolute path to the directory created +# +# Side effects: +# None. + +proc tcltest::makeDirectory {name {directory ""}} { + variable filesMade + FillFilesExisted + if {[llength [info level 0]] == 2} { + set directory [temporaryDirectory] + } + set fullName [file join $directory $name] + DebugPuts 3 "[lindex [info level 0] 0]: creating $fullName" + file mkdir $fullName + if {[lsearch -exact $filesMade $fullName] == -1} { + lappend filesMade $fullName + } + return $fullName +} + +# tcltest::removeDirectory -- +# +# Removes a named directory from the file system. +# +# Arguments: +# name Name of the directory to remove +# directory Directory from which to remove +# +# Results: +# return value from [file delete] +# +# Side effects: +# None + +proc tcltest::removeDirectory {name {directory ""}} { + variable filesMade + FillFilesExisted + if {[llength [info level 0]] == 2} { + set directory [temporaryDirectory] + } + set fullName [file join $directory $name] + DebugPuts 3 "[lindex [info level 0] 0]: deleting $fullName" + set idx [lsearch -exact $filesMade $fullName] + set filesMade [lreplace $filesMade $idx $idx] + if {$idx == -1} { + DebugDo 1 { + Warn "removeDirectory removing \"$fullName\":\n not created\ + by makeDirectory" + } + } + if {![file isdirectory $fullName]} { + DebugDo 1 { + Warn "removeDirectory removing \"$fullName\":\n not a directory" + } + } + return [file delete -force $fullName] +} + +# tcltest::viewFile -- +# +# reads the content of a file and returns it +# +# Arguments: +# name of the file to read +# directory in which file is located +# +# Results: +# content of the named file +# +# Side effects: +# None. + +proc tcltest::viewFile {name {directory ""}} { + FillFilesExisted + if {[llength [info level 0]] == 2} { + set directory [temporaryDirectory] + } + set fullName [file join $directory $name] + set f [open $fullName] + set data [read -nonewline $f] + close $f + return $data +} + +# tcltest::bytestring -- +# +# Construct a string that consists of the requested sequence of bytes, +# as opposed to a string of properly formed UTF-8 characters. +# This allows the tester to +# 1. Create denormalized or improperly formed strings to pass to C +# procedures that are supposed to accept strings with embedded NULL +# bytes. +# 2. Confirm that a string result has a certain pattern of bytes, for +# instance to confirm that "\xe0\0" in a Tcl script is stored +# internally in UTF-8 as the sequence of bytes "\xc3\xa0\xc0\x80". +# +# Generally, it's a bad idea to examine the bytes in a Tcl string or to +# construct improperly formed strings in this manner, because it involves +# exposing that Tcl uses UTF-8 internally. +# +# Arguments: +# string being converted +# +# Results: +# result fom encoding +# +# Side effects: +# None + +proc tcltest::bytestring {string} { + return [encoding convertfrom identity $string] +} + +# tcltest::OpenFiles -- +# +# used in io tests, uses testchannel +# +# Arguments: +# None. +# +# Results: +# ??? +# +# Side effects: +# None. + +proc tcltest::OpenFiles {} { + if {[catch {testchannel open} result]} { + return {} + } + return $result +} + +# tcltest::LeakFiles -- +# +# used in io tests, uses testchannel +# +# Arguments: +# None. +# +# Results: +# ??? +# +# Side effects: +# None. + +proc tcltest::LeakFiles {old} { + if {[catch {testchannel open} new]} { + return {} + } + set leak {} + foreach p $new { + if {[lsearch $old $p] < 0} { + lappend leak $p + } + } + return $leak +} + +# +# Internationalization / ISO support procs -- dl +# + +# tcltest::SetIso8859_1_Locale -- +# +# used in cmdIL.test, uses testlocale +# +# Arguments: +# None. +# +# Results: +# None. +# +# Side effects: +# None. + +proc tcltest::SetIso8859_1_Locale {} { + variable previousLocale + variable isoLocale + if {[info commands testlocale] != ""} { + set previousLocale [testlocale ctype] + testlocale ctype $isoLocale + } + return +} + +# tcltest::RestoreLocale -- +# +# used in cmdIL.test, uses testlocale +# +# Arguments: +# None. +# +# Results: +# None. +# +# Side effects: +# None. + +proc tcltest::RestoreLocale {} { + variable previousLocale + if {[info commands testlocale] != ""} { + testlocale ctype $previousLocale + } + return +} + +# tcltest::threadReap -- +# +# Kill all threads except for the main thread. +# Do nothing if testthread is not defined. +# +# Arguments: +# none. +# +# Results: +# Returns the number of existing threads. +# +# Side Effects: +# none. +# + +proc tcltest::threadReap {} { + if {[info commands testthread] != {}} { + + # testthread built into tcltest + + testthread errorproc ThreadNullError + while {[llength [testthread names]] > 1} { + foreach tid [testthread names] { + if {$tid != [mainThread]} { + catch { + testthread send -async $tid {testthread exit} + } + } + } + ## Enter a bit a sleep to give the threads enough breathing + ## room to kill themselves off, otherwise the end up with a + ## massive queue of repeated events + after 1 + } + testthread errorproc ThreadError + return [llength [testthread names]] + } elseif {[info commands thread::id] != {}} { + + # Thread extension + + thread::errorproc ThreadNullError + while {[llength [thread::names]] > 1} { + foreach tid [thread::names] { + if {$tid != [mainThread]} { + catch {thread::send -async $tid {thread::exit}} + } + } + ## Enter a bit a sleep to give the threads enough breathing + ## room to kill themselves off, otherwise the end up with a + ## massive queue of repeated events + after 1 + } + thread::errorproc ThreadError + return [llength [thread::names]] + } else { + return 1 + } + return 0 +} + +# Initialize the constraints and set up command line arguments +namespace eval tcltest { + # Define initializers for all the built-in contraint definitions + DefineConstraintInitializers + + # Set up the constraints in the testConstraints array to be lazily + # initialized by a registered initializer, or by "false" if no + # initializer is registered. + trace variable testConstraints r [namespace code SafeFetch] + + # Only initialize constraints at package load time if an + # [initConstraintsHook] has been pre-defined. This is only + # for compatibility support. The modern way to add a custom + # test constraint is to just call the [testConstraint] command + # straight away, without all this "hook" nonsense. + if {[string equal [namespace current] \ + [namespace qualifiers [namespace which initConstraintsHook]]]} { + InitConstraints + } else { + proc initConstraintsHook {} {} + } + + # Define the standard match commands + customMatch exact [list string equal] + customMatch glob [list string match] + customMatch regexp [list regexp --] + + # If the TCLTEST_OPTIONS environment variable exists, configure + # tcltest according to the option values it specifies. This has + # the effect of resetting tcltest's default configuration. + proc ConfigureFromEnvironment {} { + upvar #0 env(TCLTEST_OPTIONS) options + if {[catch {llength $options} msg]} { + Warn "invalid TCLTEST_OPTIONS \"$options\":\n invalid\ + Tcl list: $msg" + return + } + if {[llength $::env(TCLTEST_OPTIONS)] % 2} { + Warn "invalid TCLTEST_OPTIONS: \"$options\":\n should be\ + -option value ?-option value ...?" + return + } + if {[catch {eval Configure $::env(TCLTEST_OPTIONS)} msg]} { + Warn "invalid TCLTEST_OPTIONS: \"$options\":\n $msg" + return + } + } + if {[info exists ::env(TCLTEST_OPTIONS)]} { + ConfigureFromEnvironment + } + + proc LoadTimeCmdLineArgParsingRequired {} { + set required false + if {[info exists ::argv] && [lsearch -exact $::argv -help] != -1} { + # The command line asks for -help, so give it (and exit) + # right now. ([configure] does not process -help) + set required true + } + foreach hook { PrintUsageInfoHook processCmdLineArgsHook + processCmdLineArgsAddFlagsHook } { + if {[string equal [namespace current] [namespace qualifiers \ + [namespace which $hook]]]} { + set required true + } else { + proc $hook args {} + } + } + return $required + } + + # Only initialize configurable options from the command line arguments + # at package load time if necessary for backward compatibility. This + # lets the tcltest user call [configure] for themselves if they wish. + # Traces are established for auto-configuration from the command line + # if any configurable options are accessed before the user calls + # [configure]. + if {[LoadTimeCmdLineArgParsingRequired]} { + ProcessCmdLineArgs + } else { + EstablishAutoConfigureTraces + } + + package provide [namespace tail [namespace current]] $Version +} diff --git a/test/test.dic b/test/test.dic new file mode 100644 index 00000000..27f2dafa --- /dev/null +++ b/test/test.dic @@ -0,0 +1,32 @@ +##NXDICT-1.0 +#--------------------------------------------------------------------- +# Dictionary file for testing NXdict +# +# Mark Koennecke, November 2006 +#-------------------------------------------------------------------- +text=/entry1,NXentry/SDS testtext -type NX_CHAR +testfloat=/entry1,NXentry/SDS testfloat +testint=/entry1,NXentry/SDS testint -type NX_INT32 +testmot=/entry1,NXentry/SDS position +testmot_null=/entry1,NXentry/SDS position_zeropoint +testcter_preset=/entry1,NXentry/control,NXmonitor/SDS preset +testcter_mode=/entry1,NXentry/control,NXmonitor/SDS mode -type NX_CHAR -dim {132} +testcter_time=/entry1,NXentry/control,NXmonitor/SDS time +testcter_00=/entry1,NXentry/control,NXmonitor/SDS counts0 -type NX_INT32 +testcter_01=/entry1,NXentry/control,NXmonitor/SDS counts1 -type NX_INT32 +testcter_02=/entry1,NXentry/control,NXmonitor/SDS counts2 -type NX_INT32 +testcter_03=/entry1,NXentry/control,NXmonitor/SDS counts3 -type NX_INT32 +testcter_04=/entry1,NXentry/control,NXmonitor/SDS counts4 -type NX_INT32 +testhm=/entry1,NXentry/detector,NXdata/SDS hmdata -type NX_INT32 -rank 1 \ + -dim {$(dim0)} +testhmtb=/entry1,NXentry/detector,NXdata/SDS time_binning +testar=/entry1,NXentry/detector,NXdata/SDS x_axis +testintar=/entry1,NXentry/detector,NXdata/SDS y_axis -type NX_INT32 +testsd=/entry1,NXentry/detector,NXdata/SDS gurke -rank 1 \ + -type NX_INT32 -dim {$(dim0)} +testslab=/entry,NXentry/SDS slappy -rank 1 -dim {-1} +testlink=/entry1,NXentry/detector,NXdata/NXVGROUP + + + + \ No newline at end of file diff --git a/test/test.hdd b/test/test.hdd new file mode 100644 index 00000000..2e05e49c --- /dev/null +++ b/test/test.hdd @@ -0,0 +1,7 @@ +*************************** Test Data File ******************************** +Original Filename = !!FILE!! +File Creation Date = !!DATE!! +**************************************************************************** +---------------------------------------------------------------------------- +!!SCANZERO!! +**************************** DATA ****************************************** diff --git a/test/testinc.tcl b/test/testinc.tcl new file mode 100644 index 00000000..d86d45a0 --- /dev/null +++ b/test/testinc.tcl @@ -0,0 +1,10 @@ +#------------------------------------------------------------------------------ +# This is a prelude to source into tcl for testing regression tests. +# copyright: see file COPYRIGHT +# +# Mark Koennecke, July 2006 +#------------------------------------------------------------------------------ +source tcltest.tcl +namespace import tcltest::* +source testutil.tcl +source sicstcldebug.tcl diff --git a/test/testini.tcl b/test/testini.tcl new file mode 100644 index 00000000..56c567c9 --- /dev/null +++ b/test/testini.tcl @@ -0,0 +1,513 @@ +# -------------------------------------------------------------------------- +# Initialization script for testing SICS +# +# Started: Dr. Mark Koennecke, July 2006 +#--------------------------------------------------------------------------- +# O P T I O N S + +# --------------- Initialize Tcl internals -------------------------------- + +# first all the server options are set + +ServerOption ReadTimeOut 10 +# timeout when checking for commands. In the main loop SICS checks for +# pending commands on each connection with the above timeout, has +# PERFORMANCE impact! + +ServerOption AcceptTimeOut 10 +# timeout when checking for connection req. +# Similar to above, but for connections + +ServerOption ReadUserPasswdTimeout 500000 +# time to wiat for a user/passwd to be sent from a client. Increase this +# if there is a problem connecting to a server due to network overload\ + +ServerOption ServerPort 2911 +# the port number the server is going to listen at. The client MUST know +# this number in order to connect. It is in client.ini + +ServerOption InterruptPort 2913 +# The UDP port where the server will wait for Interrupts from clients. +# Obviously, clients wishing to interrupt need to know this number. + + +#--------------------------------------------------------------------------- +# U S E R S + +# than the SICS users are specified +# Syntax: SicsUser name password userRightsCode +SicsUser Mugger Mugger 1 +SicsUser User User 2 +#SicsUser Spy Spy 3 +SicsUser Spy 007 1 + +#----------------- SICS Variable +VarMake lotte Text User + +#----------------- Motors --------------------------------------------------- +Motor brumm regress +MakeDrive +#----------------- Alias ---------------------------------------------------- +SicsAlias brumm miau +#----------------- Counters ------------------------------------------------- +MakeCounter aba regress +MakeCounter hugo SIM -1. +MakeCounter lieselotte SIM -1. +#------------------------------ +proc SICSValue {command} { + set txt [eval $command] + set l [split $txt =] + return [string trim [lindex $l 1]] +} +#----------------------------- +proc multitransfer {} { + append res [SICSValue "aba gettime"] " " + for {set i 0} {$i < 7} {incr i} { + append res [SICSValue "aba getmonitor $i"] " " + } + return $res +} +#----------------------------------- +MakeMultiCounter multi aba hugo lieselotte +multi transferscript multitransfer + +#------------- For Scanning --------------------------------------------- +# This is with the tricky bit set: we use a multicounter and use the +# scantransfer function to return values of a gaussian for a4 positions. +# This gives nice scan data which can be used to test all sorts of things. +#------------------------------------------------------------------------- +MakeDataNumber SicsDataNumber ./DataNumber +VarMake SicsDataPath Text Mugger +SicsDataPath ./ +SicsDataPath lock +VarMake SicsDataPrefix Text Mugger +SicsDataPrefix regression +SicsDataPrefix lock +VarMake SicsDataPostFix Text Mugger +SicsDataPostFix .dat +SicsDataPostFix lock + +Motor a1 SIM -2 180 -.1 10 +Motor a2 SIM 30 150 -.1 10 +Motor a3 SIM -360 360 -.1 10 +Motor a4 SIM -180 180 -.1 10 +Motor a5 SIM -180 180 -.1 10 +Motor a6 SIM -180 180 -.1 10 +Motor sgu SIM -20 20 -.1 10 +Motor sgl SIM -20 20 -.1 10 +MakeMultiCounter scanCter aba + +proc scantransfer {} { + set FWHM 1.5 + set pos 5.33 + set height 700 + set stddev [expr $FWHM/2.354] + set ftmp [expr ([SICSValue a4] - $pos)/$stddev] + set count [expr 10 + $height*0.4*exp(-.5*$ftmp*$ftmp)] + set counti [expr int($count)] + append res [SICSValue "lieselotte gettime"] " " + append res $counti " " + for {set i 1} {$i < 7} {incr i} { + append res [SICSValue "lieselotte getmonitor $i"] " " + } + return $res +} +scancter transferscript scantransfer + +MakeScanCommand xxxscan scancter test.hdd recover.bin +MakePeakCenter xxxscan +source scancommand.tcl +MakeOptimise opti scancter +MakeMaximize scancter + +#------------------------------------------------------------------------- +# Histogram Memory +#------------------------------------------------------------------------ +MakeHM hm regress +hm configure rank 1 +hm configure dim0 23 +hm configure testval 1 +hm configure errortype 0 +hm configure recover 1 +hm configure init 1 +hm init + +MakeHM tof regress +tof configure rank 1 +tof configure HistMode TOF +tof configure dim0 23 +tof configure testval 1 +tof configure errortype 0 +tof configure recover 1 +tof genbin 10 12 100 +tof configure init 1 +tof init + +#------------------------------------------------------------------------- +# NXscript +#------------------------------------------------------------------------- +MakeNXScript +#------------------------------------------------------------------------- +proc makearray {} { + global ar + for { set i 10} {$i < 20} {incr i} { + set ar([expr $i - 10]) [expr $i*1.0] + } +} +#------------------------------------------------------------------------ +proc makeintarray {} { + global ar + for { set i 10} {$i < 20} {incr i} { + set ar([expr $i - 10]) $i + } +} +Publish makearray User +Publish makeintarray User +Publish parray User + +#------------------------------------------------------------------------ +# SicsData +#------------------------------------------------------------------------ +sicsdatafactory new data +sicsdatafactory new duta +#----------------------------------------------------------------------- +# tasub +#----------------------------------------------------------------------- +MakeTasUB tasub +#----------------------------------------------------------------------- +# MultiMotors +#---------------------------------------------------------------------- +MakeMulti sa +sa alias a3 om +sa alias a4 stt +sa pos noeff a3 24 a4 48 +sa endconfig + +#----------------------------------------------------------------------- +# Hipadaba +#---------------------------------------------------------------------- +InstallHdb +hmake /instrument spy none +hmake /instrument/sample spy none +hattach /instrument/sample a3 omega +hattach /instrument/sample qh qh +hmake /instrument/detector spy none +hattach /instrument/detector hm data +hattach /instrument lotte title + +restore + +#------------------------------------------------- +proc farmFormat {par num} { + hsetprop /sics/farm/$par lastError none + return [format "$par %d" [string trim $num]] +} +#----------------------------------------------- +proc farmRead {par } { + hsetprop /sics/farm/$par lastError none + hsetprop /sics/farm/$par replyCommand "farmReply $par" + return $par +} +#----------------------------------------------- +proc farmReply {par reply} { + set action [string trim [hgetpropval /sics/farm/$par status]] + hsetprop /sics/farm/$par status idle + if {[string first OK $reply] >= 0} { + if {[string first get $action] >= 0} { + set idx [string first : $reply] + if {$idx > 0} { + set val [string trim [string range $reply [expr $idx +1] end]] + hupdate /sics/farm/$par $val + } + } else { + hget /sics/farm/$par + } + } else { + if {[string first ERROR $reply] < 0} { + set reply "ERROR: $reply" + } + clientPut $reply + error $reply + } +} +#============================================= +proc schconset {val} { + set com [farmFormat schnegge $val] + hsetprop /sics/farm/schneggecon replyCommand schreply + return $com +} +#---------------------------------------------- +proc schreply {reply} { + clientput "schreply $reply" + if {[string first OK $reply] >= 0} { + hsetprop /sics/farm/schneggerunning readCommand schrunget + hget /sics/farm/schneggerunning + hsetprop /sics/farm/schneggerunning readCommand \ + "farmReply schneggerunning" + } else { + hsetprop /sics/farm/schneggecon status idle + hsetprop /sics/farm/schneggecon lastError $reply + clientput "ERROR: $reply on schnegge" + } +} +#----------------------------------------------- +proc schrun {reply} { + clientput "schrun $reply" + hsetprop /sics/farm/schneggerunning status idle + if {[string first OK $reply] >= 0} { + set idx [string first : $reply] + if {$idx > 0} { + set val [string trim [string range $reply [expr $idx +1] end]] + hupdate /sics/farm/schneggerunning $val + if {$val == 1} { + clientput "schnegge creeping" + hsetprop /sics/farm/schneggerunning readCommand schrunget + hget /sics/farm/schneggerunning + hsetprop /sics/farm/schneggerunning readCommand \ + "farmReply schneggerunning" + } else { + clientput "schnegge finished" + hsetprop /sics/farm/schneggerunning readCommand \ + "farmRead schneggerunning" + hsetprop /sics/farm/schneggecon status idle + } + } + } else { + clientput "schnegge has error: $reply" + hsetprop /sics/farm/schneggerunning readCommand \ + "farmRead schneggerunning" + hsetprop /sics/farm/schneggecon status idle + hsetprop /sics/farm/schneggecon lastError $reply + hsetprop /sics/farm/schneggerunning lastError $reply + } +} +#---------------------------------------------- +proc schget {} { + hsetprop /sics/farm/schneggecon lastError none + hsetprop /sics/farm/schneggecon replyCommand "farmReply schneggecon" + return schnegge +} +#---------------------------------------------- +proc schrunget {} { + hsetprop /sics/farm/schneggerunning lastError none + hsetprop /sics/farm/schneggerunning replyCommand schrun + return schneggerunning +} +#----------------------------------------------- +set farm 0 +if {$farm == 1} { +# Generic Controller +#------------------------------------------------------------------- +MakeAsyncProtocol norma +MakeAsyncQueue farmQueue norma localhost 9090 +MakeGenController farm +genconfigure asynconnect farm farmQueue +#------------------------------------------------ +genconfigure makepar farm hase int +hsetprop /sics/farm/hase priv user +hsetprop /sics/farm/hase writeCommand "farmFormat hase" +hsetprop /sics/farm/hase readCommand "farmRead hase" +hsetprop /sics/farm/hase replyCommand "farmReply hase" + +genconfigure makepar farm schnegge int +hsetprop /sics/farm/schnegge priv user +hsetprop /sics/farm/schnegge writeCommand "farmFormat schnegge" +hsetprop /sics/farm/schnegge readCommand "farmRead schnegge" +hsetprop /sics/farm/schnegge replyCommand "farmReply schnegge" + +genconfigure makepar farm schneggerunning int +hsetprop /sics/farm/schneggerunning priv internal +hsetprop /sics/farm/schneggerunning readCommand "farmRead schneggerunning" +hsetprop /sics/farm/schneggerunning replyCommand "farmReply schneggerunning" + +genconfigure makepar farm schneggecon int +hsetprop /sics/farm/schneggecon priv user +hsetprop /sics/farm/schneggecon writeCommand schconset +hsetprop /sics/farm/schneggecon readCommand schget +hsetprop /sics/farm/schneggecon replyCommand schreply +} + +set farm 0 + +if {$farm == 1} { +#-------------- Test new async protocol controller +makesctcontroller farmser std localhost:7070 +MakeSICSObj farm TestObj +#--------------------------- +proc farmparcom {par} { + sct send $par + return parread +} +#------------------------ +proc farmparread {} { + set rply [sct result] + if {[string first ERR $rply] >= 0} { + sct geterror $rply + return idle + } + set data [string range $rply 3 end] + set node [sct] + sct update $data + return idle +} +#-------------------------- +proc farmcheck {} { + set val [sct target] + if {$val < -100 || $val > 100} { + error "Value out of range" + } + return OK +} +#--------------------------- +proc farmset {par} { + set val [sct target] + sct send "$par $val" + return setreply +} +#------------------------- +proc farmsetreply {} { + set rply [sct result] + if {[string first ERR $rply] >= 0} { + sct print $rply + } + return idle +} +#-------------------------- +hfactory /sics/farm/hase plain spy int +hsetprop /sics/farm/hase read farmparcom hase +hsetprop /sics/farm/hase parread farmparread + +hsetprop /sics/farm/hase check farmcheck +hsetprop /sics/farm/hase write farmset hase +hsetprop /sics/farm/hase setreply farmsetreply + +farmser poll /sics/farm/hase +farmser write /sics/farm/hase + +hfactory /sics/farm/hugo plain spy int +hsetprop /sics/farm/hugo read farmparcom hugo +hsetprop /sics/farm/hugo parread farmparread + +hsetprop /sics/farm/hugo check farmcheck +hsetprop /sics/farm/hugo write farmset hugo +hsetprop /sics/farm/hugo setreply farmsetreply + +farmser poll /sics/farm/hugo +farmser write /sics/farm/hugo + +hfactory /sics/farm/schnegge plain spy float +hsetprop /sics/farm/schnegge read farmparcom schnegge +hsetprop /sics/farm/schnegge parread farmparread + +hsetprop /sics/farm/schnegge check farmcheck +hsetprop /sics/farm/schnegge write farmset schnegge +hsetprop /sics/farm/schnegge setreply farmsetreply + +farmser poll /sics/farm/schnegge +farmser write /sics/farm/schnegge + +hfactory /sics/farm/schneggerunning plain spy int +hsetprop /sics/farm/schneggerunning read farmparcom schneggerunning +hsetprop /sics/farm/schneggerunning parread farmparread +farmser poll /sics/farm/schneggerunning + +hfactory /sics/farm/stone plain spy int +hsetprop /sics/farm/stone read farmparcom stone +hsetprop /sics/farm/stone parread farmparread +#farmser poll /sics/farm/stone + +farmser debug -1 + +#----------------- drivable scriptcontext adapter +proc schneggechecklimits {} { + return [farmcheck] +} +#----------------------------- +proc schneggestatus {} { + farmser queue /sics/farm/schneggerunning progress read + set status [sct writestatus] + switch $status { + commandsent { + set runtime [SICSValue "hgetprop /sics/farm/schneggerunning read_time"] + set starttime [sct write_time] + if {$runtime > $starttime} { + sct writestatus evalcheck + } + return busy + } + evalcheck { + set tst [hval /sics/farm/schneggerunning] + if {$tst == 1} { + return busy + } else { + return idle + } + } + default { + error "schneggestatus called in bad state $status" + } + } +} +#--------------------------------------------- +hsetprop /sics/farm/schnegge checklimits schneggechecklimits +hsetprop /sics/farm/schnegge checkstatus schneggestatus +#makesctdrive schnecke /sics/farm/schnegge farmser +makesctdriveobj schnecke /sics/farm/schnegge DriveAdapter farmser + +} + +#---------- test http +set httptest 1 + +if {$httptest == 1} { +makesctcontroller amorhmsct sinqhttp amorhm data 180 spy 007 +#makesctcontroller amorhmsct sinqhttp localhost:8080 data 60 spy 007 +MakeSICSObj amorhm HttpTest +amorhmsct debug -1 +#------------------ +proc statget {} { + sct send "admin/textstatus.egi" + return statrepl +} +#----------------- +proc statreply {} { + sct update [sct result] + sct utime readtime + return idle +} +#----------------- +proc readcollapse {} { + sct send "admin/processhmdata.egi?bank=0&command=sum:2:0:400" + return colread +} +#----------------- +proc colreply {} { + sct utime readtime + set data [sct result] + return idle +} +#------------------------- +hfactory /sics/amorhm/status plain spy text +hsetprop /sics/amorhm/status read statget +hsetprop /sics/amorhm/status statrepl statreply +amorhmsct poll /sics/amorhm/status 10 + +hattach /sics/amorhm data intvarar collapse +hsetprop /sics/amorhm/collapse read readcollapse +hsetprop /sics/amorhm/collapse colread colreply +amorhmsct poll /sics/amorhm/collapse 20 + +} + +#source sansdruck.tcl + + +#MakeRS232Controller sadu pc4639 4168 +#MakeRS232Controller sadu localhost 4168 +#sadu replyterminator 0x04 +#sadu sendterminator 0x04 +#sadu timeout 1000 + +#source ../sim/mars/julcho.tcl + + diff --git a/test/testmisc.tcl b/test/testmisc.tcl new file mode 100644 index 00000000..7122b49a --- /dev/null +++ b/test/testmisc.tcl @@ -0,0 +1,21 @@ +#-------------------------------------------------------------------- +# This is for testing odd bits and pieces +# +# Mark Koennecke, October 2006 +#-------------------------------------------------------------------- + +puts stdout "Testing variables and aliases" + +test misc-1.0 {Test Variables} -body { + testPar lotte Uuuuuurgs User + return OK +} -result OK + +test misc-1.1 {Test Alias} -body { + config rights User User + miau errortype 0 + testDrive miau 10 User + return OK +} -result OK + + diff --git a/test/testmumo.tcl b/test/testmumo.tcl new file mode 100644 index 00000000..3a40d2e8 --- /dev/null +++ b/test/testmumo.tcl @@ -0,0 +1,80 @@ +#----------------------------------------------------------------------- +# Some tests for SANS style MultiMotors. A MultiMotor with the name sa +# must have been initialized in the test initializaton file. +# +# Mark Koennecke, November 2006 +#---------------------------------------------------------------------- +puts stdout "Testing SANS MultiMotor Module..." + +proc testMumoPosition {omPos sttPos} { + set txt [sa] + set luf [split $txt "\n"] + set l1 [lindex $luf 0] + if {[string first "Status listing" $l1] < 0} { + error "Bad first line on MultiMotor: $l1" + } + set l2 [lindex $luf 1] + set li2 [split $l2 =] + if {abs([lindex $li2 1] - $omPos) > .1} { + error "Bad omega position: $li2, expected $omPos" + } + set l2 [lindex $luf 2] + set li2 [split $l2 =] + if {abs([lindex $li2 1] - $sttPos) > .1} { + error "Bad stt position: $li2, expected $sttPos" + } + return OK +} +#--------------------------------------------------------------------- +test mumo-1.0 {Test Reading} -body { + config rights Mugger Mugger + drive a3 0 a4 0 + return [testMumoPosition .0 .0] +} -result OK +#---------------------------------------------------------------------- +test mumo-1.1 {Test Named Position} -body { + sa noeff + return [testMumoPosition 24. 48.] +} -result OK +#--------------------------------------------------------------------- +test mumo-1.2 {Test Back} -body { + sa back + return [testMumoPosition 0. 0.] +} -result OK +#---------------------------------------------------------------------- +test mumo-1.3 {Test defpos} -body { + sa defpos fart om 10 stt 43 + sa fart + return [testMumoPosition 10. 43.] +} -result OK +#----------------------------------------------------------------------- +test mumo-1.4 {Test individual driving} -body { + sa noeff + sa om 27 + return [testMumoPosition 27 48.] +} -result OK +#----------------------------------------------------------------------- +test mumo-1.5 {Test pos definiton} -body { + sa pos gurke + sa back + sa gurke + return [testMumoPosition 27 48.] +} -result OK +#---------------------------------------------------------------------- +test mumo-1.6 {Test dropping named position} -body { + sa drop fart + set txt [sa fart] + if {[string first ERROR $txt] < 0} { + error "Did not trigger error when trying to drive a dropped position" + } + return OK +} -result OK +#---------------------------------------------------------------------- +test mumo-1.6 {Test Permission} -body { + config rights Spy Spy + set txt [sa neoff] + if {[string first ERROR $txt] < 0} { + error "Did not trigger error whithout permission" + } + return OK +} -result OK diff --git a/test/testsics b/test/testsics new file mode 100755 index 00000000..5fabdb39 --- /dev/null +++ b/test/testsics @@ -0,0 +1,60 @@ +#!/usr/bin/tclsh +#------------------------------------------------------------------------------ +# This is a regression test for SICS. Before this can be used a SICServer must +# have been started with: SICServer testini.tcl. This file uses the +# tcltest package which comes with SICS for tests. +# +# copyright: see file COPYRIGHT +# +# Started: Mark Koennecke, July 2006 +#------------------------------------------------------------------------------ +# as of now we have tcl8.3 which has an outdated version of tcltest. We use +# a better version in a local file. Once tcl8.4 has made it into the distro +# I use, use the line below. We need tcltest 2.+ +# package require tcltest +source tcltest.tcl +namespace import tcltest::* +source testutil.tcl +source sicstcldebug.tcl + +#--------------- Test Miscellaneous stuff +source testmisc.tcl + +#-------------- Test for motors +source mottest.tcl + +#-------------- Test Counter +set countername aba +set errorname aba +source countertest.tcl + +#-------------- Test Multi Counter +set countername multi +source countertest.tcl + +#-------------- Test batch processing +source batchtest.tcl + +#-------------- Test scans +source scantest.tcl + +#------------ Test peak optimization +source optitest.tcl + +#----------- test histogram memory +source histtest.tcl + +#----------- test sics data +source testsicsdata.tcl + + +#----------- test nxscript +source nxscripttest.tcl + +#------------ test SANS MultiMotor +source testmumo.tcl + +#------------ print test summary +cleanupTests +exit 1 + diff --git a/test/testsicsdata.tcl b/test/testsicsdata.tcl new file mode 100644 index 00000000..c42b5fa9 --- /dev/null +++ b/test/testsicsdata.tcl @@ -0,0 +1,218 @@ +#------------------------------------------------------------------------- +# This is a regression test for the SICS data module +# +# Mark Koennecke, November 2006 +#------------------------------------------------------------------------- +puts stdout "Testing SicsData" +data clear + +test sicsdata-1.0 {Test writing int} -body { + config rights User User + for {set i 0} {$i < 5} { incr i} { + testOK "data putint $i $i" + } + for {set i 0} {$i < 5} { incr i} { + set val [SICSValue "data get $i"] + if {$val != $i} { + error "SicsData returned a bad value: expected $i received $val" + } + } + return OK +} -result OK + +test sicsdata-1.1 {Test writing float} -body { + for {set i 0} {$i < 5} { incr i} { + set v [expr $i * 1.0] + testOK "data putfloat $i $v" + } + for {set i 0} {$i < 5} { incr i} { + set val [SICSValue "data get $i"] + if {abs($val - $i) > .000001} { + error "SicsData returned a bad value: expected $i received $val" + } + } + return OK +} -result OK + +test sicsdata-1.2 {Test used} -body { + set val [SICSValue "data used"] + if {$val != 5} { + error "Expected data used to be 5, not $val" + } + return OK +} -result OK + +test sicsdata-1.3 {Test clear} -body { + testOK "data clear" + set val [SICSValue "data used"] + if {$val != 0} { + error "Expected data used to be 0, not $val" + } + return OK +} -result OK + +xxxscan clear +xxxscan add a4 2. .2 +xxxscan run 30 timer 2 + +test sicsdata-1.4 {Testing scancounts} -body { + testOK "data copyscancounts 0 xxxscan" + set val [SICSValue "data used"] + if {$val != 30} { + error "Expected data used to be 30, not $val" + } + set val [SICSValue "data get 0"] + if {$val != 10} { + error "Expected data 0 to be 10, not $val" + } + set val [SICSValue "data get 10"] + if {$val != 41} { + error "Expected data 10 to be 41, not $val" + } + set val [SICSValue "data get 20"] + if {$val != 171} { + error "Expected data 10 to be 171, not $val" + } + return OK +} -result OK + +test sicsdata-1.5 {Testing scanmonitor} -body { + testOK "data clear" + testOK "data copyscanmon 0 xxxscan 2" + set val [SICSValue "data used"] + if {$val != 30} { + error "Expected data used to be 30, not $val" + } + set val [SICSValue "data get 0"] + if {$val != 0} { + error "Expected data 0 to be 0, not $val" + } + return OK +} -result OK + +test sicsdata-1.6 {Testing scanvar} -body { + testOK "data copyscanvar 0 xxxscan 0" + set val [SICSValue "data used"] + if {$val != 30} { + error "Expected data used to be 30, not $val" + } + set val [SICSValue "data get 0"] + if {abs($val - 2.0) > .001} { + error "Expected data 0 to be 2.0, not $val" + } + set val [SICSValue "data get 20"] + if {abs($val - 6.0) > .001} { + error "Expected data 20 to be 6.0, not $val" + } + set val [SICSValue "data get 29"] + if {abs($val - 7.8) > .001} { + error "Expected data 29 to be 7.8, not $val" + } + return OK +} -result OK + +config rights Mugger Mugger +tof genbin 20 10 50 +tof init + +test sicsdata-1.7 {Testing timebin} -body { + testOK "data clear" + testOK "data copytimebin 0 tof" + set val [SICSValue "data used"] + if {$val != 50} { + error "Expected data used to be 50, not $val" + } + set val [SICSValue "data get 0"] + if {abs($val - 20.0) > .001} { + error "Expected data 0 to be 20.0, not $val" + } + set val [SICSValue "data get 49"] + if {abs($val - 510.0) > .001} { + error "Expected data 49 to be 510.0, not $val" + } + return OK +} -result OK + +hm initval 32 + +test sicsdata-1.8 {Testing hm} -body { + testOK "data clear" + testOK "data copyhm 0 hm" + set val [SICSValue "data used"] + if {$val != 23} { + error "Expected data used to be 23, not $val" + } + set val [SICSValue "data get 0"] + if {abs($val - 32.0) > .001} { + error "Expected data 0 to be 32.0, not $val" + } + set val [SICSValue "data get 22"] + if {abs($val - 32.0) > .001} { + error "Expected data 22 to be 32.0, not $val" + } + return OK +} -result OK + +test sicsdata-1.8 {Testing UU write} -body { + set text [data writeuu hugo] + if {[string first "begin 622" $text] < 0} { + error "Bad reply on uuwrite: $text" + } + return OK +} -result OK + +test sicsdata-1.9 {Testing file dump} -body { + data clear + data copyhm 0 hm + testOK "data dump test.dat" + set status [catch {exec diff test.dat sicsdatasoll.dat} msg] + if {$status != 0} { + error "Difference in dump file: $msg" + } + return OK +} -result OK + +test sicsdata-1.10 {Copying sicsdata} -body { + duta clear + data clear + data copyhm 0 hm + testNoError "duta copydata 0 data 0 23" + set val [SICSValue "duta used"] + if {$val != 23} { + error "Expected data used to be 23, not $val" + } + for {set i 0} {$i < 23} {incr i} { + set val [SICSValue "duta get $"] + if {abs($val - 32.0) > .001} { + error "Expected data $i to be 32.0, not $val" + } + } + return OK +} -result OK + +test sicsdata-1.11 {Division} -body { + + config rights Mugger Mugger + duta clear + data clear + hm initval 32 + data copyhm 0 hm + hm initval 16 + duta copyhm 0 hm + testNoError "data divideby duta" + set val [SICSValue "data used"] + if {$val != 23} { + error "Expected data used to be 23, not $val" + } + for {set i 0} {$i < 23} {incr i} { + set val [SICSValue "data get $"] + if {abs($val - 2.0) > .001} { + error "Expected data $i to be 2.0, not $val" + } + } + return OK +} -result OK + + + + diff --git a/test/testsoll.xml b/test/testsoll.xml new file mode 100644 index 00000000..9a91e331 --- /dev/null +++ b/test/testsoll.xml @@ -0,0 +1,84 @@ + + + + Hugo ist eine Nassnase + + 27.8000 + + + 177 + + + 15.0000 + + + 0.0000 + + + + 10.0000 + + timer + + + 5 + + + 10 + + + 25 + + + 35 + + + 45 + + + + + 55 55 55 55 + 55 55 55 55 + 55 55 55 55 + 55 55 55 55 + 55 55 55 55 + 55 55 55 + + + 500.0000 800.0000 1100.0000 1400.0000 + 1700.0000 2000.0000 2300.0000 2600.0000 + 2900.0000 3200.0000 3500.0000 3800.0000 + 4100.0000 4400.0000 4700.0000 5000.0000 + 5300.0000 5600.0000 5900.0000 6200.0000 + + + 10.0000 11.0000 12.0000 13.0000 + 14.0000 15.0000 16.0000 17.0000 + 18.0000 19.0000 + + + 10 11 12 13 + 14 15 16 17 + 18 19 + + + + 23 23 23 23 + 23 23 23 23 + 23 23 23 23 + 23 23 23 23 + 23 23 23 23 + 23 23 23 + + + + + + 1.1000 2.2000 3.3000 + + + diff --git a/test/testtasub.tcl b/test/testtasub.tcl new file mode 100644 index 00000000..486ac33e --- /dev/null +++ b/test/testtasub.tcl @@ -0,0 +1,265 @@ +#---------------------------------------------------------------------- +# This is a set of regression tests for the tasub module. +# This module does the UB matrix algorithm as described by Mark Lumsden +# triple axis spectrometers. +# +# Mark Koennecke, November 2006 +#---------------------------------------------------------------------- +puts stdout "Testing Tasub" +#---------------------------------------------------------------------- +# testTasubCalculation tests the tasub calculation. The input is a list +# containg the cell constants and two lists denoting reflections. +# For each reflection the list must hold: +# 0 1 2 3 4 5 6 7 8 9 10 11 12 +# qh qk ql ei ef a1 a2 a3 a4 sgu sgl a5 a6 +# testTasubCalculation then inputs the cell and the reflections into +# tasub and calculates a UB from that. Then it tries to drive to the +# QE positions given for the reflections and checks if the angles are right +# It also checks QE positions in order to check if they have been properly +# updated. +# This then can be used with various inputs to check various configurations +# of the instrument. +#---------------------------------------------------------------------- +proc testTasubCalculation {cell ref1 ref2} { + if {[llength $cell] < 6} { + error "Not enough cell parameters" + } + if {[llength $ref1] < 13} { + error "Not enough parameters for reflection 1" + } + if {[llength $ref2] < 13} { + error "Not enough parameters for reflection 2" + } + checkSettingCell $cell + checkMakeUB $ref1 $ref2 + checkDrivingReflection $ref1 + checkDrivingReflection $ref2 +} +#------------------------------------------------------------------- +proc checkSettingCell {cell} { + config rights Mugger Mugger + append cmd "tasub cell " [join $cell] + testOK $cmd + set readback [string trim [SICSValue "tasub cell"]] + set l [split $readback] + for {set i 0} {$i < [llength $cell]} {incr i} { + set ori [lindex $cell $i] + set val [lindex $l $i] + if {abs($ori - $val) > .01} { + error "Bad cell readback, in $cell, back $readback" + } + } +} +#--------------------------------------------------------------------- +proc checkMakeUB {ref1 ref2} { + checkOK "tasub clear" + set cmd [format "tasub addref %f %f %f %f %f %f %f %f %f" \ + [lindex $ref1 0] [lindex $ref1 1] [lindex $ref1 2] \ + [lindex $ref1 7] [lindex $ref1 8] [lindex $ref1 9] \ + [lindex $ref1 10] \ + [lindex $ref1 3] [lindex $ref1 4]] + eval $cmd + set cmd [format "tasub addref %f %f %f %f %f %f %f %f %f" \ + [lindex $ref2 0] [lindex $ref2 1] [lindex $ref2 2] \ + [lindex $ref2 7] [lindex $ref2 8] [lindex $ref2 9] \ + [lindex $ref2 10] \ + [lindex $ref2 3] [lindex $ref2 4]] + eval $cmd + set test [tasub makeub 1 2] + if {[string first ERROR $test] > 0} { + error "Problem calculating UB: $test" + } +} +#-------------------------------------------------------------------- +proc checkDrivingReflection {ref} { + set cmd [format "drive qh %f qk %f ql %f ei %f ef %f" \ + [lindex $ref 0] [lindex $ref 1] [lindex $ref 2] \ + [lindex $ref 3] [lindex $ref 4]] + set test [eval $cmd] + puts $cmd + if {[string first ERROR $test] >= 0} { + error "Failed to drive reflection: $test" + } + set a1 [SICSValue a1] + set a1soll [lindex $ref 5] + if {abs($a1soll - $a1) >.01} { + error "Bad a1 position, should $a1soll, is $a1" + } + set a1 [SICSValue a1] + set a1soll [lindex $ref 5] + if {abs($a1soll - $a1) >.01} { + error "Bad a1 position, should $a1soll, is $a1" + } + set a2 [SICSValue a2] + set a2soll [lindex $ref 6] + if {abs($a2soll - $a2) >.01} { + error "Bad a2 position, should $a2soll, is $a2" + } + set a3 [SICSValue a3] + set a3soll [lindex $ref 7] + if {abs($a3soll - $a3) >.01} { + error "Bad a3 position, should $a3soll, is $a3" + } + set a4 [SICSValue a4] + set a4soll [lindex $ref 8] + if {abs($a4soll - $a4) >.01} { + error "Bad a4 position, should $a4soll, is $a4" + } + set sgu [SICSValue sgu] + set sgusoll [lindex $ref 9] + if {abs($sgusoll - $sgu) >.01} { + error "Bad sgu position, should $sgusoll, is $sgu" + } + set sgl [SICSValue sgl] + set sglsoll [lindex $ref 10] + if {abs($sglsoll - $sgl) >.01} { + error "Bad sgl position, should $sglsoll, is $sgl" + } + set a5 [SICSValue a5] + set a5soll [lindex $ref 11] + if {abs($a5soll - $a5) >.01} { + error "Bad a5 position, should $a5soll, is $a5" + } + set a6 [SICSValue a6] + set a6soll [lindex $ref 12] + if {abs($a6soll - $a6) >.01} { + error "Bad a6 position, should $a6soll, is $a6" + } + + set qh [SICSValue qh] + set qhsoll [lindex $ref 0] + if {abs($qhsoll - $qh) >.01} { + error "Bad qh position, should $qhsoll, is $qh" + } + set qk [SICSValue qk] + set qksoll [lindex $ref 1] + if {abs($qksoll - $qk) >.01} { + error "Bad qk position, should $qksoll, is $qk" + } + set ql [SICSValue ql] + set qlsoll [lindex $ref 2] + if {abs($qlsoll - $ql) >.01} { + error "Bad ql position, should $qlsoll, is $ql" + } + + set ei [SICSValue ei] + set eisoll [lindex $ref 3] + if {abs($eisoll - $ei) >.01} { + error "Bad ei position, should $eisoll, is $ei" + } + set ef [SICSValue ef] + set efsoll [lindex $ref 4] + if {abs($efsoll - $ef) >.01} { + error "Bad ef position, should $efsoll, is $ef" + } +} +#===================== tests ========================================= +test tasub-1.0 {Test setting dd} -body { + testPar "tasub mono dd" 3.35461 Mugger + testPar "tasub ana dd" 3.35461 Mugger + return OK +} -result OK + +test tasub-1.1 {Test setting ss} -body { + testPar "tasub mono ss" 1 Mugger + testPar "tasub ana ss" 1 Mugger + return OK +} -result OK + +test tasub-1.2 {Test setting sample configuration} -body { + testPar "tasub const" kf Mugger + testPar "tasub ss" -1 Mugger + return OK +} -result OK + +test tasub-1.3 {Test clearing tasub} -body { + testOK "tasub clear" + return OK +} -result OK + +test tasub-1.4 {Test setting cell} -body { + checkSettingCell [list 7. 7. 7. 90. 90. 90.] + return OK +} -result OK + +tasub mono dd 3.35461 +tasub ana dd 3.35461 +tasub mono ss 1 +tasub ana ss 1 +tasub const kf +tasub ss -1 + +test tasub-1.5 {Basic calculation test} -body { + set ref1 [list 1 0 0 5 5 37.075 74.150 168.27 -23.46 0 0 37.075 74.15] + set ref2 [list 0 0 1 5 5 37.075 74.150 84.78 -10.44 0 0 37.075 74.15] + set cell [list 9.95 9.95 22.24 90 90 90] + testTasubCalculation $cell $ref1 $ref2 + return OK +} -result OK + + +test tasub-1.6 {Test driving ei} -body { + drive ei 5.0 + set eit [SICSValue ei] + set a1 [SICSValue a1] + set a2 [SICSValue a2] + if {abs(5 - $eit) > .001} { + error "Readback of ei failed" + } + if {abs(37.07 - $a1) > .01} { + error "Bad a1 value, is $a1, should 37.07" + } + if {abs(74.15 - $a2) > .01} { + error "Bad a2 value, is $a2, should 74.15" + } + return OK +} -result OK + +test tasub-1.7 {Test driving ef} -body { + drive ef 5. + set eit [SICSValue ef] + set a1 [SICSValue a5] + set a2 [SICSValue a6] + if {abs(5. - $eit) > .001} { + error "Readback of ei failed" + } + if {abs(37.07 - $a1) > .01} { + error "Bad a5 value, is $a1, should 37.07" + } + if {abs(74.15 - $a2) > .01} { + error "Bad a6 value, is $a2, should 74.15" + } + return OK +} -result OK + +test tasub-1.8 {Test reading en} -body { + drive ei 5. ef 3.7 + set en [SICSValue en] + if {abs($en - 1.3) > .01} { + error "Bad en value: should: 1.3, is $en" + } + return OK +} -result OK + +test tasub-1.9 {Test driving ef, different scattering sense} -body { + tasub ana ss -1 + drive ef 5.0 + set eit [SICSValue ef] + set a1 [SICSValue a5] + set a2 [SICSValue a6] + if {abs(5 - $eit) > .001} { + error "Readback of ef failed" + } + if {abs(-37.07 - $a1) > .01} { + error "Bad a5 value, is $a1, should -37.07" + } + if {abs(-74.15 - $a2) > .01} { + error "Bad a6 value, is $a2, should -74.15" + } + return OK +} -result OK + + + + + \ No newline at end of file diff --git a/test/testutil.tcl b/test/testutil.tcl new file mode 100644 index 00000000..c8b3d1c5 --- /dev/null +++ b/test/testutil.tcl @@ -0,0 +1,165 @@ +#------------------------------------------------------------------------------ +# utility routines for testing SICS +# +# copyright: see file COPYRIGHT +# +# Mark Koennecke, July 2006 +#------------------------------------------------------------------------------ +proc SICSValue {command} { + set txt [eval $command] + set l [split $txt =] + return [string trim [lindex $l 1]] +} +#----------------------------------------------------------------------------- +proc compareValue {is should} { + if {[string is double $is] == 1} { + if {abs($should - $is) > .01} { + error "Bad compare is: $is, should $should" + } + } else { + if {[string compare $is $should] != 0} { + error "Bad compare is: $is, should $should" + } + } + return OK +} +#------------------------------------------------------------------------------ +proc testPar {name testval priv } { + config rights Spy Spy + set value [SICSValue $name] + set res [eval $name $testval] + if {[string first ERROR $res] < 0} { + error "Managed to set parameter even if not allowed" + } + config rights $priv $priv + set res [eval $name $testval] + if {[string first ERROR $res] >= 0} { + error "Setting parameter failed with $res" + } + set readback [SICSValue $name] + compareValue $readback $testval + eval $name $value + return "OK" +} +#------------------------------------------------------------------------------- +proc testROPar {name val} { + config rights Mugger Mugger + set value [SICSValue $name] + compareValue $value $val + catch {$name [expr $val + 1]} msg + set value [SICSValue $name] + compareValue $value $val + config rights Spy Spy + return OK +} +#------------------------------------------------------------------------------ +proc testDrive {name value priv} { + config rights Spy Spy + set ans [eval drive $name $value] + if {[string first ERROR $ans] < 0} { + error "Protection on drivable does not work" + } + config rights $priv $priv + set ans [eval drive $name $value] + if { [string first sucessfully $ans] < 0} { + error "Driving $name failed: $ans" + } + set readback [SICSValue $name] + compareValue $readback $value + config rights Spy Spy + return OK +} +#------------------------------------------------------------------------------ +proc testDriveInterrupt {name value} { + global socke + config rights Mugger Mugger + run $name $value + puts $socke "INT1712 3" + flush $socke + set ans [eval status] + config rights Spy Spy + if {[string first Interrupt $ans] < 0} { + puts stdout $ans + error "Failed to abort driving" + } + if { [string first Eager $ans] < 0} { + error "Failed to finish driving" + } + return OK +} +#--------------------------------------------------------------------- +proc testNBCounting {startCommand waitTime} { + set res [$startCommand] + if {[string first ERROR $res] >= 0} { + error "Starting count failed with $res" + } + exec sleep 1 + set res [SICSValue status] + if {[string first "Count" $res] < 0} { + error "Status does not say counting" + } + exec sleep $waitTime + set res [SICSValue status] + if {[string first "Eager" $res] < 0} { + error "Counting did not stop" + } + return "OK" +} +#---------------------------------------------------------------- +proc testBlockCounting {startCommand waitTime} { + set res [$startCommand] + if {[string first ERROR $res] >= 0} { + error "Starting count failed with $res" + } + exec sleep $waitTime + set res [SICSValue status] + if {[string first "Eager" $res] < 0} { + error "Counting did not stop" + } + return "OK" +} +#--------------------------------------------------------------- +proc testInterruptedCount {startCommand} { + global socke + set res [$startCommand] + if {[string first ERROR $res] >= 0} { + error "Starting count failed with $res" + } + puts $socke "INT1712 3" + flush $socke + exec sleep 10 + set ans [eval status] + config rights Spy Spy + if {[string first Interrupt $ans] < 0} { + puts stdout $ans + error "Failed to abort counting" + } + if { [string first Eager $ans] < 0} { + error "Failed to finish counting" + } + return OK +} +#------------------------------------------------------------------------ +proc testOK {command} { + set test [eval $command] + if {[string first OK $test] < 0} { + error [format "Expected OK, got %s" $test] + } + return OK +} +#------------------------------------------------------------------------ +proc testNoError {command} { + set test [eval $command] + if {[string first ERROR $test] >= 0} { + error [format "Located Error: %s" $test] + } + return OK +} +#------------------------------------------------------------------------ +proc testCommand {command response} { + set result [eval $command] + if {[string first $response $result] < 0} { + error "Expected $response, received $result" + } + return OK +} diff --git a/tmp/hdbscan.tcl b/tmp/hdbscan.tcl new file mode 100644 index 00000000..aadb0277 --- /dev/null +++ b/tmp/hdbscan.tcl @@ -0,0 +1,9 @@ +hset /commands/scan/scan_variables som +hset /commands/scan/scan_start 5 +hset /commands/scan/scan_increments .5 +hset /commands/scan/NP 10 +hset /commands/scan/mode Timer +hset /commands/scan/preset 2 + + + diff --git a/ubcalc.c b/ubcalc.c index e78bf3ea..6948264f 100644 --- a/ubcalc.c +++ b/ubcalc.c @@ -541,6 +541,7 @@ static int cellFromUBWrapper(pUBCALC self, SConnection *pCon){ int UBCalcWrapper(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]){ pUBCALC self = (pUBCALC)pData; + char pBuffer[512]; assert(self); if(argc < 2){ @@ -550,7 +551,15 @@ int UBCalcWrapper(SConnection *pCon, SicsInterp *pSics, void *pData, strtolower(argv[1]); if(strcmp(argv[1],"cell") == 0){ - return readCell(pCon, self, argc, argv); + if(argc > 3){ + return readCell(pCon, self, argc, argv); + } else { + snprintf(pBuffer,511,"ubcalc cell = %f %f %f %f %f %f", + self->direct.a, self->direct.b, self->direct.c, + self->direct.alpha, self->direct.beta,self->direct.gamma); + SCWrite(pCon,pBuffer,eValue); + return 1; + } } else if(strcmp(argv[1],"ref1") == 0){ return readReflection(pCon,pSics, &self->r1,argc,argv); } else if(strcmp(argv[1],"ref2") ==0){ diff --git a/varlog.c b/varlog.c index 16b5cd22..806821d6 100644 --- a/varlog.c +++ b/varlog.c @@ -120,6 +120,7 @@ { deleteCircular(self->pTail); } + free(self); /* M.Z. */ return 1; } /*--------------------------------------------------------------------------*/ diff --git a/velo.c b/velo.c index 3f8c260c..75c9465d 100644 --- a/velo.c +++ b/velo.c @@ -326,6 +326,7 @@ static void VSListForbidden(pVelSel self, SConnection *pCon){ pVelSel VSCreate(pMotor pTilt, pVelSelDriv pDriv) { pVelSel pNew = NULL; + SConnection *pCon = NULL; assert(pTilt); assert(pDriv); @@ -389,7 +390,12 @@ static void VSListForbidden(pVelSel self, SConnection *pCon){ /* deal with that motor, have him AccessCode Internal */ pNew->pTilt = pTilt; - ObParInit(pTilt->ParArray,8,"accesscode",(float)usInternal,usInternal); + pCon = SCCreateDummyConnection(pServ->pSics); + if(pCon != NULL) + { + MotorSetPar(pTilt,pCon,"accesscode",(float)usInternal); + SCDeleteConnection(pCon); + } /* enter driver */ pNew->pDriv = pDriv;