PSI sics-cvs-psi-2008-10-02
This commit is contained in:
40
.cdtproject
40
.cdtproject
@ -3,7 +3,13 @@
|
||||
|
||||
<cdtproject id="org.eclipse.cdt.make.core.make">
|
||||
<extension point="org.eclipse.cdt.core.BinaryParser" id="org.eclipse.cdt.core.ELF"/>
|
||||
<extension point="org.eclipse.cdt.core.CIndexer" id="org.eclipse.cdt.core.domsourceindexer"/>
|
||||
<extension point="org.eclipse.cdt.core.CIndexer" id="org.eclipse.cdt.core.ctagsindexer">
|
||||
<attribute key="ctagslocation" value=""/>
|
||||
<attribute key="ctagfiletype" value="ctags_internal"/>
|
||||
<attribute key="ctagsindexincludes" value="false"/>
|
||||
<attribute key="ctagslocationtype" value="ctags_path_default"/>
|
||||
<attribute key="ctagfilelocation" value=""/>
|
||||
</extension>
|
||||
<data>
|
||||
<item id="org.eclipse.cdt.core.pathentry">
|
||||
<pathentry kind="src" path=""/>
|
||||
@ -18,7 +24,7 @@
|
||||
<autodiscovery enabled="true" selectedProfileId="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile" problemReportingEnabled="true"/>
|
||||
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerProjectProfile">
|
||||
<buildOutputProvider>
|
||||
<openAction enabled="false" filePath=""/>
|
||||
<openAction enabled="true" filePath=""/>
|
||||
<parser enabled="true"/>
|
||||
</buildOutputProvider>
|
||||
<scannerInfoProvider id="specsFile">
|
||||
@ -26,6 +32,36 @@
|
||||
<parser enabled="true"/>
|
||||
</scannerInfoProvider>
|
||||
</profile>
|
||||
<profile id="org.eclipse.cdt.make.core.GCCStandardMakePerFileProfile">
|
||||
<buildOutputProvider>
|
||||
<openAction enabled="false" filePath=""/>
|
||||
<parser enabled="true"/>
|
||||
</buildOutputProvider>
|
||||
<scannerInfoProvider id="makefileGenerator">
|
||||
<runAction useDefault="true" command="make" arguments="-f ${project_name}_scd.mk"/>
|
||||
<parser enabled="false"/>
|
||||
</scannerInfoProvider>
|
||||
</profile>
|
||||
<profile id="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfile">
|
||||
<buildOutputProvider>
|
||||
<openAction enabled="false" filePath=""/>
|
||||
<parser enabled="true"/>
|
||||
</buildOutputProvider>
|
||||
<scannerInfoProvider id="specsFile">
|
||||
<runAction useDefault="true" command="gcc" arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}"/>
|
||||
<parser enabled="false"/>
|
||||
</scannerInfoProvider>
|
||||
</profile>
|
||||
<profile id="org.eclipse.cdt.managedbuilder.core.GCCWinManagedMakePerProjectProfile">
|
||||
<buildOutputProvider>
|
||||
<openAction enabled="false" filePath=""/>
|
||||
<parser enabled="true"/>
|
||||
</buildOutputProvider>
|
||||
<scannerInfoProvider id="specsFile">
|
||||
<runAction useDefault="true" command="gcc" arguments="-E -P -v -dD ${plugin_state_location}/${specs_file}"/>
|
||||
<parser enabled="false"/>
|
||||
</scannerInfoProvider>
|
||||
</profile>
|
||||
</item>
|
||||
</data>
|
||||
</cdtproject>
|
||||
|
2
.cvsignore
Normal file
2
.cvsignore
Normal file
@ -0,0 +1,2 @@
|
||||
emergency.scn
|
||||
recover.bin
|
33
.project
33
.project
@ -7,7 +7,12 @@
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.make.core.makeBuilder</name>
|
||||
<triggers>clean,full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.build.arguments</key>
|
||||
<value>-f makefile_slinux</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.core.errorOutputParser</key>
|
||||
<value>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;</value>
|
||||
@ -20,6 +25,10 @@
|
||||
<key>org.eclipse.cdt.make.core.incrementalBuildTarget</key>
|
||||
<value>all</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.environment</key>
|
||||
<value></value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.enableAutoBuild</key>
|
||||
<value>false</value>
|
||||
@ -28,6 +37,10 @@
|
||||
<key>org.eclipse.cdt.make.core.buildLocation</key>
|
||||
<value></value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.build.target.inc</key>
|
||||
<value>all</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.enableFullBuild</key>
|
||||
<value>true</value>
|
||||
@ -36,6 +49,18 @@
|
||||
<key>org.eclipse.cdt.make.core.enabledIncrementalBuild</key>
|
||||
<value>true</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.build.location</key>
|
||||
<value></value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.build.target.clean</key>
|
||||
<value>clean</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.build.command</key>
|
||||
<value>make</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.enableCleanBuild</key>
|
||||
<value>true</value>
|
||||
@ -44,6 +69,10 @@
|
||||
<key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
|
||||
<value>clean</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.append_environment</key>
|
||||
<value>true</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
|
||||
<value>false</value>
|
||||
@ -64,6 +93,10 @@
|
||||
<key>org.eclipse.cdt.make.core.stopOnError</key>
|
||||
<value>false</value>
|
||||
</dictionary>
|
||||
<dictionary>
|
||||
<key>org.eclipse.cdt.make.core.build.target.auto</key>
|
||||
<value>all</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
|
3
.settings/org.eclipse.cdt.core.prefs
Normal file
3
.settings/org.eclipse.cdt.core.prefs
Normal file
@ -0,0 +1,3 @@
|
||||
#Thu Jan 11 14:43:46 CET 2007
|
||||
eclipse.preferences.version=1
|
||||
indexerId=org.eclipse.cdt.core.fastIndexer
|
13
HistDriv.i
13
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
|
||||
|
16
HistMem.h
16
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
|
||||
|
@ -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
|
||||
|
70
README
Normal file
70
README
Normal file
@ -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
|
||||
|
||||
|
137
SCinter.c
137
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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -57,6 +59,8 @@
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <tcl.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#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,17 +458,31 @@ 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 != NULL)
|
||||
{
|
||||
tail = pCurrent;
|
||||
pCurrent = pCurrent->pNext;
|
||||
}
|
||||
|
||||
/* delete Commandlist (reversed order) */
|
||||
if (tail) {
|
||||
pCurrent = tail;
|
||||
while(pCurrent)
|
||||
{
|
||||
/* the line below fixes problems with kill functions
|
||||
* traversing the command list
|
||||
*/
|
||||
pCurrent->pNext = NULL;
|
||||
if(pCurrent->KFunc)
|
||||
{
|
||||
pCurrent->KFunc(pCurrent->pData);
|
||||
@ -464,10 +492,14 @@ extern char *SkipSpace(char *pPtr);
|
||||
/* printf("Deleting %s\n",pCurrent->pName); */
|
||||
free(pCurrent->pName);
|
||||
}
|
||||
pTemp = pCurrent->pNext;
|
||||
if (pCurrent->stat) {
|
||||
StatisticsKill(pCurrent->stat);
|
||||
}
|
||||
pTemp = pCurrent->pPrevious;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
27
SCinter.h
27
SCinter.h
@ -9,7 +9,9 @@
|
||||
---------------------------------------------------------------------------*/
|
||||
#ifndef SICSINTERPRETER
|
||||
#define SICSINTERPRETER
|
||||
#include "obdes.h"
|
||||
#include "Scommon.h"
|
||||
#include "statistics.h"
|
||||
#include <tcl.h>
|
||||
/* 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
|
||||
------------------------------------------------------------------------*/
|
||||
|
15
SICSmain.c
15
SICSmain.c
@ -21,6 +21,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#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);
|
||||
for (i=1; i<argc; i++) {
|
||||
if (strcmp(argv[i], "-nolog") == 0) {
|
||||
SICSLogEnable(0);
|
||||
} else if (file == NULL) {
|
||||
file = argv[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
iRet = InitServer(NULL,&pServ);
|
||||
}
|
||||
iRet = InitServer(file,&pServ);
|
||||
if(!iRet)
|
||||
{
|
||||
printf("Unrecoverable error on server startup, exiting.........\n");
|
||||
|
@ -50,7 +50,9 @@ typedef enum {
|
||||
eFinish,
|
||||
eEvent,
|
||||
eWarning,
|
||||
eError
|
||||
eError,
|
||||
eHdbValue,
|
||||
eHdbEvent
|
||||
} OutCode;
|
||||
|
||||
#include "interrupt.h"
|
||||
|
22
access.c
22
access.c
@ -3,8 +3,9 @@
|
||||
|
||||
Mark Koennecke, November 1996
|
||||
----------------------------------------------------------------------------*/
|
||||
#ifndef PCODE
|
||||
#define PCODE
|
||||
#include <string.h>
|
||||
#include <sics.h>
|
||||
#include <splitter.h>
|
||||
|
||||
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;
|
||||
}
|
||||
|
19
alias.c
19
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;
|
||||
}
|
||||
|
2
alias.h
2
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
|
||||
|
||||
|
@ -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);
|
||||
*/
|
||||
|
537
ascon.c
Normal file
537
ascon.c
Normal file
@ -0,0 +1,537 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#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<length; i++) {
|
||||
PrintChar(data[i]);
|
||||
}
|
||||
}
|
||||
printf("<written\n");
|
||||
*/
|
||||
FD_ZERO(&wmask);
|
||||
FD_SET(fd, &wmask);
|
||||
ret = select(fd + 1, NULL, &wmask, NULL, &tmo);
|
||||
if (ret <= 0) return ASCON_SELECT_ERROR;
|
||||
ret = send(fd, data, length, 0);
|
||||
if (ret > 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;
|
||||
}
|
82
ascon.h
Normal file
82
ascon.h
Normal file
@ -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
|
156
ascon.i
Normal file
156
ascon.i
Normal file
@ -0,0 +1,156 @@
|
||||
#ifndef ASCON_I
|
||||
#define ASCON_I
|
||||
|
||||
#include <sys/time.h>
|
||||
#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
|
341
asyncprotocol.c
Normal file
341
asyncprotocol.c
Normal file
@ -0,0 +1,341 @@
|
||||
#include <sics.h>
|
||||
#include <asyncprotocol.h>
|
||||
#include <asyncqueue.h>
|
||||
|
||||
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;
|
||||
}
|
62
asyncprotocol.h
Normal file
62
asyncprotocol.h
Normal file
@ -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 */
|
987
asyncqueue.c
Normal file
987
asyncqueue.c
Normal file
@ -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 <sys/time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netdb.h>
|
||||
#include <sics.h>
|
||||
#include <rs232controller.h>
|
||||
#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;
|
||||
}
|
||||
|
186
asyncqueue.h
Normal file
186
asyncqueue.h
Normal file
@ -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 */
|
4
choco.c
4
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;
|
||||
|
||||
|
1
choco.h
1
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,
|
||||
|
@ -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.
|
||||
|
||||
|
||||
|
||||
|
6
choco.w
6
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.
|
||||
|
||||
|
||||
|
||||
|
244
commandlog.c
244
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 <stdlib.h>
|
||||
#include <assert.h>
|
||||
@ -34,48 +39,30 @@
|
||||
/*-------------------- 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;
|
||||
|
||||
/*
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* supress status messages */
|
||||
if(strstr(pText,"status =") != NULL)
|
||||
{
|
||||
if(pCopy != NULL){
|
||||
free(pCopy);
|
||||
}
|
||||
/* suppress status messages */
|
||||
if (strstr(text,"status =") != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -83,66 +70,139 @@
|
||||
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);
|
||||
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';
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* create tail buffer as needed */
|
||||
if(!pTail)
|
||||
{
|
||||
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));
|
||||
if (pTail != NULL) {
|
||||
if (doStamp) {
|
||||
setCircular(pTail,strdup(stamp1));
|
||||
nextCircular(pTail);
|
||||
}
|
||||
if(pCopy != NULL){
|
||||
free(pCopy);
|
||||
setCircular(pTail,pCopy);
|
||||
nextCircular(pTail);
|
||||
}
|
||||
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);
|
||||
}
|
||||
SCPrintf(pCon,eValue,"%s.intervall [min] = %d", argv[0], iIntervall);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
else if(strcmp(argv[1],"compact") == 0)
|
||||
{
|
||||
sprintf(pBueffel,"autolog.intervall = %d", iIntervall);
|
||||
SCWrite(pCon,pBueffel,eValue);
|
||||
return 1;
|
||||
if(argc > 2)
|
||||
{
|
||||
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 */
|
||||
{
|
||||
|
@ -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[]);
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
402
conman.c
402
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;
|
||||
}
|
||||
|
||||
/* 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",-10);
|
||||
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)
|
||||
{
|
||||
/*
|
||||
* This is a fix to suppress cron messages in the success
|
||||
* case
|
||||
*/
|
||||
if(SCGetWriteFunc(self) != SCNotWrite)
|
||||
{
|
||||
sendingConnection = self;
|
||||
if(self->pSock != NULL)
|
||||
{
|
||||
sprintf(pBueffel,"sock %d>>",self->pSock->sockid);
|
||||
WriteToCommandLogCmd(self->pSock->sockid, pCommand);
|
||||
} else {
|
||||
WriteToCommandLog("CRON>>",pCommand);
|
||||
}
|
||||
else
|
||||
{
|
||||
strcat(pBueffel,"CONT or CRON>> ");
|
||||
sendingConnection = NULL;
|
||||
}
|
||||
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;
|
||||
}
|
||||
pCon->iUserRights = i;
|
||||
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;
|
||||
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';
|
||||
if (CompactCommandLog()) {
|
||||
if (iRet < 3) {
|
||||
NETInfo(self->pSock,pHost,131);
|
||||
sprintf(pBueffel,"Accepted connection on socket %d from %s",
|
||||
self->pSock->sockid, pHost);
|
||||
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,24 +2230,74 @@ 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;
|
||||
SConnection *SCLoad(SCStore *conStore) {
|
||||
SConnection *pCon = NULL;
|
||||
commandContext old;
|
||||
|
||||
pCon = con->pCon;
|
||||
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)
|
||||
|
37
conman.h
37
conman.h
@ -24,6 +24,7 @@
|
||||
#include "network.h"
|
||||
#include "obdes.h"
|
||||
#include "commandcontext.h"
|
||||
#include "dynstring.h"
|
||||
|
||||
#define MAXLOGFILES 10
|
||||
|
||||
@ -57,9 +58,14 @@ 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
|
||||
command context
|
||||
@ -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);
|
||||
|
||||
|
@ -88,4 +88,10 @@
|
||||
* file: mcstascounter.c
|
||||
*/
|
||||
pCounterDriver NewMcStasCounter(char *name);
|
||||
|
||||
/*
|
||||
* for regression testing
|
||||
* file: regresscter.c
|
||||
*/
|
||||
pCounterDriver NewRegressCounter(char *name);
|
||||
#endif
|
||||
|
19
counter.c
19
counter.c
@ -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
|
||||
*/
|
||||
@ -772,7 +779,8 @@
|
||||
{"preset",1,{FUPAOPT}},
|
||||
{"send",0,{0,0}},
|
||||
{"setpar",3,{FUPATEXT,FUPAINT,FUPAFLOAT}},
|
||||
{"getpar",2,{FUPATEXT,FUPAOPT}}
|
||||
{"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 */
|
||||
}
|
||||
|
5
danu.c
5
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
|
||||
|
298
devexec.c
298
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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#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,9 +503,6 @@
|
||||
LLDnodeDataTo(self->iList,&pDev);
|
||||
if(pDev)
|
||||
{
|
||||
/*
|
||||
SCSetContext(self->pOwner,pDev->cmdID,pDev->name);
|
||||
*/
|
||||
SCPushContext(self->pOwner, pDev->comCon.transID, pDev->comCon.deviceID);
|
||||
|
||||
pDrivInt = pDev->pDescriptor->GetInterface(pDev->pData,DRIVEID);
|
||||
@ -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)
|
||||
{
|
||||
@ -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");
|
||||
|
13
devexec.h
13
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
|
||||
|
@ -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}
|
||||
|
@ -258,6 +258,8 @@ to the global SICS device executor.
|
||||
/*-------------------------------------------------------------------------*/
|
||||
@<devstop@>
|
||||
/*-------------------------- 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
|
||||
@}
|
||||
|
||||
|
361
devser.c
Normal file
361
devser.c
Normal file
@ -0,0 +1,361 @@
|
||||
#include <math.h>
|
||||
#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++;
|
||||
}
|
123
devser.h
Normal file
123
devser.h
Normal file
@ -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
|
@ -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;
|
||||
|
@ -104,7 +104,7 @@ the dictionary file:
|
||||
would denote the normal counting tube at a scanning type of
|
||||
experiment.
|
||||
</dl>
|
||||
<dt>nxscript puthm hmAlias hmName ?start? ?length?
|
||||
<dt>nxscript puthm hmAlias hmName ?start? ?length? ?bank?
|
||||
<dd>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.
|
||||
<dt>nxscript puttimebinning aliasName hmName
|
||||
<dd>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.
|
||||
<dd>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.
|
||||
<dt>nxscript putintarray aliasName arrayName length
|
||||
<dd>The same as above, but the data is considered integer.
|
||||
<dt>nxsript putglobal attName bla bla bla
|
||||
<dd>This writes an global attribute attName. Everything after attName
|
||||
is concatenated to a string which then respresents the value of the
|
||||
attribute.
|
||||
<dt>nxscript putsicsdata alias dataname
|
||||
<dd>Writes the sicsdata array dataname to alias.
|
||||
<dt>nxscript putattribute alias name value
|
||||
<dd>Add another text attribute to alias. The name of the attribute is name, the
|
||||
value value.
|
||||
<dt>nxscript makelink targetAlias victimAlias
|
||||
<dd>This creates a symbolic link for victimAlias in the group
|
||||
designated by targetAlias.
|
||||
<dt>nxscript putslab alias startlist sizelist obj
|
||||
<dd>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.
|
||||
</dl>
|
||||
</p>
|
||||
<H1>Automatic Updating of NeXus Files</H1>
|
||||
|
@ -65,6 +65,9 @@ file.
|
||||
<dt>updateintervall
|
||||
<dd>The time intervall in seconds between updates. The defualt is
|
||||
1200, eg. 20 minutes.
|
||||
<dt>onoff
|
||||
<dd>can be 1 or 0. Switches automatic updates on or off. It might be usefule for
|
||||
scans to switch this off.
|
||||
</dl>
|
||||
</p>
|
||||
</BODY>
|
||||
|
@ -21,6 +21,37 @@ The TAS requires the following initializations in its instrument file:
|
||||
<dt>MakeTasUB tasub
|
||||
<dd>Installs the TAS crystallographic calculation module into SICS. It will
|
||||
have the name tasub (recommended).
|
||||
<dt>MakeTasUB tasub a1 a2 mcv mch a3 a4 sgu sgl a5 a6 acv ach
|
||||
<dd>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:
|
||||
<dl>
|
||||
<dt>a1
|
||||
<dd>monochormator rotation
|
||||
<dt>a2
|
||||
<dd>monochromator two theta
|
||||
<dt>mcv
|
||||
<dd>monochromator vertical curvature
|
||||
<dt>mch
|
||||
<dd>monochromator horizontal curvature
|
||||
<dt>a3
|
||||
<dd> sample rotation
|
||||
<dt>a4
|
||||
<dd>sample tow theta
|
||||
<dt>sgu
|
||||
<dd>sample tilt
|
||||
<dt>sgl
|
||||
<dd>second sample tilt
|
||||
<dt>a5
|
||||
<dd>analyzer rotation
|
||||
<dt>a6
|
||||
<dd>analyzer two theta
|
||||
<dt>acv
|
||||
<dd>analyzer vertical curvature
|
||||
<dt>ach
|
||||
<dd>analyzer horizontal curvature
|
||||
</dl>
|
||||
<dt>MakeTasScan iscan tasub
|
||||
<dd>Installs the module with the TAS specific scan functions into SICS. The
|
||||
TAS implements its own data format resembling the ILL TAS data format.
|
||||
|
@ -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.
|
||||
</P>
|
||||
<p>
|
||||
<b>Note:</b> with the command <it>config listen 1</it> you can have the output
|
||||
to the command log printed into your client, too. With <it>config listen 0</it> you can switch this off again. This is useful for listening into a running
|
||||
instrument.
|
||||
</p>
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
||||
|
@ -45,6 +45,13 @@ named buffer within the stack of nested buffers.
|
||||
<dt>Clears the queue of batch buffers
|
||||
<dt>exe queue
|
||||
<dd>Prints the content of the batch buffer queue.
|
||||
<dt>exe fullpath filename
|
||||
<dd>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.
|
||||
<dt>exe makepath filename
|
||||
<dd>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.
|
||||
<dt>exe run
|
||||
<dd>Starts executing the batch buffers in the queue.
|
||||
<dt>exe print buffername
|
||||
@ -58,7 +65,12 @@ most useful for SICS clients watching the progress of the experiment.
|
||||
<dt>exe append some text
|
||||
<dd> Appends a line with everything after append to the upload buffer
|
||||
<dt>exe save filename
|
||||
<dd>saves the recently uploaded buffer under filename on the SICS server.
|
||||
<dd>saves the recently uploaded buffer under filename on the SICS server. Does not overwrite
|
||||
existing files.
|
||||
<dt>exe forcesave filename
|
||||
<dd>saves the recently uploaded buffer under filename on the SICS server. Overwrites existing file.
|
||||
<dt>exe clearupload
|
||||
<dd>clears any pending upload operations.
|
||||
</dl>
|
||||
</P>
|
||||
</BODY>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<p>
|
||||
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:</p>
|
||||
<p>
|
||||
<b> fileeval <i>name</i> </b> tries to open the file name and
|
||||
<b> exe <i>name</i> </b> tries to open the file name and
|
||||
executes the script in this file.
|
||||
</p>
|
||||
<p>
|
||||
|
@ -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.
|
||||
</P>
|
||||
<p>
|
||||
The peak optimiser supports another optimisation algorithm which is faster but
|
||||
may not be as accurate. This is hill climbing:
|
||||
<pre>
|
||||
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
|
||||
</pre>
|
||||
</p>
|
||||
<p>
|
||||
The Peak Optimiser is implemented as an object with the name opti. It
|
||||
understand the following commands:
|
||||
<DL>
|
||||
@ -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.
|
||||
<DT>opti run
|
||||
<DD>Starts the optimiser. It will then optimise the peak. This may take some
|
||||
time.
|
||||
time as it uses a time consuming scan based algorithm.
|
||||
<DT>opti climb
|
||||
<DD>Starts the optimiser in hill climbing mode. Hill climbing is faster but may
|
||||
not be as accurate as a scan based optimization.
|
||||
</DL>
|
||||
The behaviour of the optimiser can be configured by modifying some
|
||||
parameters. The synatx is easy: <b>opti parameter</b> prints the value of the
|
||||
@ -72,5 +87,12 @@ status of the countmode parameter this is either a preset time or a preset
|
||||
monitor.
|
||||
</DL>
|
||||
</p>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
BIN
doc/user/sansdoc.tgz
Normal file
BIN
doc/user/sansdoc.tgz
Normal file
Binary file not shown.
5284
doc/user/sansdocbook.xml
Normal file
5284
doc/user/sansdocbook.xml
Normal file
File diff suppressed because it is too large
Load Diff
@ -66,6 +66,10 @@ above and restores SICS to the state it was in when the status was saved with
|
||||
read.
|
||||
</p>
|
||||
<p>
|
||||
<b>restore listerr</b> prints the list of lines which caused errors during the
|
||||
last restore.
|
||||
</p>
|
||||
<p>
|
||||
<b>killfile</b> 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
|
||||
|
@ -40,7 +40,7 @@
|
||||
<dt> Examples <dd> Whenever examples are shown, anything which is actually
|
||||
typed by the user is shown <em>like this</em>. It will generally be shown
|
||||
in lower case. E.g.
|
||||
<pre> <em>scan a1=0 da1=1 np=6</em></pre>
|
||||
<pre> <em>sc a1=0 da1=1 np=6</em></pre>
|
||||
indicates that everything between the <em>s</em> and <em>6</em>, inclusive,
|
||||
is to be typed by the user.
|
||||
<dt> Optional Arguments <dd> 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.
|
||||
<a href="#LIST">LI</a> 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.
|
||||
<a href="#LOG">LO</a> LOg : Controls terminal logging.
|
||||
<a href="#ONOFF">OF</a> OFf : Turns flipper off.
|
||||
<a href="#ONOFF">ON</a> ON : Turns flipper on.
|
||||
@ -129,17 +122,13 @@
|
||||
<a href="#PA">PA</a> Pol.An. : Defines a polarization analysis file (default
|
||||
file ext'n is .PAL).
|
||||
<a href="#PRINT">PR</a> PRint : Prints one ore more variables or parameters.
|
||||
<a href="#RUN">RU</a> RUn : Runs a jobfile.
|
||||
<a href="#SCAN">SC</a> SCan : Scans a variable with given or previously
|
||||
defined increment, number of points and
|
||||
time interval or monitor count.
|
||||
<a href="#SET">SE</a> SEt : Sets a parameter value.
|
||||
<a href="#SCANFAST">SF</a> ScanFast : Scans a variable quickly.
|
||||
<a href="#SCANFAST">FS</a> ScanFast : Scans a variable quickly.
|
||||
<a href="#SWITCHES">SW</a> SWitch : Sets some switches.
|
||||
<a href="#SETZERO">SZ</a> 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).
|
||||
<a href="#SETZERO">SZ</a> SetZero : Sets the zero point offset of a variable.
|
||||
</pre>
|
||||
<h3><a name="CLEAR">CLEAR</a></h3>
|
||||
<pre>
|
||||
@ -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>
|
||||
|
||||
</pre>
|
||||
@ -488,33 +476,6 @@
|
||||
e.g. PR A1,A5<CR>
|
||||
PR QH-EN,GM<CR>
|
||||
|
||||
</pre>
|
||||
<h3><a name="RUN">RUN</a></h3>
|
||||
<pre>
|
||||
|
||||
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:
|
||||
|
||||
|
||||
</pre>
|
||||
<h3><a name="SCAN">SCAN</a></h3>
|
||||
<pre>
|
||||
@ -619,7 +580,7 @@
|
||||
sets dM and dA to 3.355 <20>
|
||||
|
||||
</pre>
|
||||
<h3><a name="SCANFAST">SF</a></h3>
|
||||
<h3><a name="SCANFAST">FS</a></h3>
|
||||
<table>
|
||||
<tr><td valign=top><b>ScanFast</b>
|
||||
<td>Scans a simple variable quickly. The variable is driven
|
||||
@ -636,7 +597,7 @@
|
||||
to be specified directly.
|
||||
</tr>
|
||||
<tr><td><td>Example:<pre>
|
||||
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.</pre>
|
||||
</tr>
|
||||
<tr><td><td>All of the data is output to a disk file as with the
|
||||
@ -650,7 +611,7 @@
|
||||
<pre>
|
||||
|
||||
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:
|
||||
<dd>Sets the title string (up to 72 characters) to be written to the data
|
||||
file header.
|
||||
<dt><em>set user ...</em>
|
||||
<dd>Sets the experiment user's name (6 characters).
|
||||
<dd>Sets the experiment user's name.
|
||||
<dt><em>set local ...</em>
|
||||
<dd>Sets the local contact's name (6 characters).
|
||||
<dd>Sets the local contact's name.
|
||||
</dl>
|
||||
|
||||
|
||||
@ -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<39> 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
|
||||
<09>-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<EFBFBD>. 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)
|
||||
</pre>
|
||||
<h3>Polarisation Analysis Variables</h3>
|
||||
<pre>
|
||||
*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.
|
||||
</pre>
|
||||
|
||||
<h3><a name="Increments_Variables">Increments Variables</a></h3>
|
||||
<pre>
|
||||
For all variables A1 through T in the list of type (iv) variables
|
||||
|
@ -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:
|
||||
<pre>
|
||||
|
||||
/home/INST/data/YYYY
|
||||
/home/INST/data/YYYY/HHH
|
||||
|
||||
</pre>
|
||||
on the instrument computer or in
|
||||
<pre>
|
||||
|
||||
/data/lnslib/data/INST/data/YYYY
|
||||
/afs/psi.ch/project/sinqdata/YYYY/INST/HHH
|
||||
|
||||
</pre>
|
||||
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:
|
||||
<pre>
|
||||
instRRRRRYYYY.dat
|
||||
instYYYYnTRRRRR.dat
|
||||
</pre>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
|
@ -81,6 +81,12 @@ In order to calculate a UB matrix a list of reflections must be maintained. This
|
||||
<dd>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.
|
||||
<dt>tasub addauxref qh qk ql
|
||||
<dd>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.
|
||||
</dl>
|
||||
</p>
|
||||
<h3>Calculations</h3>
|
||||
@ -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.
|
||||
<dl>
|
||||
<dt>tasbub const ki | kf
|
||||
<dt>tasbub const ki | kf | elastic
|
||||
<dd>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.
|
||||
<dt>tasub const
|
||||
<dd>Prints if ki or kf is fixed.
|
||||
<dt>tasub ss
|
||||
<dd>Prints the sample scattering sense.
|
||||
<dt>tasub ss 1 | -1
|
||||
<dd>Sets the sample scattering sense. Allowed values are either 1 or -1.
|
||||
<dt>tasub silent 0 | 1
|
||||
<dd>Prints or sets the silent flag. If this is 0, the messages Driving motor ..
|
||||
from .. to .. are suppressed.</dd>
|
||||
<dt>tasub outofplane 0 | 1
|
||||
<dd>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.</dd>
|
||||
<dt>tasub makeub r1 r2
|
||||
<dd>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 <b>powder mode</b>. In this mode, only the sampl
|
||||
respective positions. THis is commonly used to analyze the energy transfer of powder samples.
|
||||
</p>
|
||||
<p>
|
||||
There is another important command:
|
||||
There are other important command:
|
||||
<dl>
|
||||
<dt>tasub update
|
||||
<dd>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.
|
||||
</dl>
|
||||
<dt>tasub updatetargets
|
||||
<dd>This command makes the QE targets macth the current position. This is
|
||||
useful after initialization in the instrument.tcl file.</dd>
|
||||
</p>
|
||||
<h3>Internal Commands</h3>
|
||||
<p>
|
||||
|
2
drive.c
2
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);
|
||||
|
68
errormsg.c
Normal file
68
errormsg.c
Normal file
@ -0,0 +1,68 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#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;
|
||||
}
|
32
errormsg.h
Normal file
32
errormsg.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef ERRORMSG_H
|
||||
#define ERRORMSG_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
/** \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
|
111
evcontroller.c
111
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 */
|
||||
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)
|
||||
{
|
||||
pCon = SCLoad(conn);
|
||||
}
|
||||
if(pCon)
|
||||
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);
|
||||
}
|
||||
/*--------------------------------------------------------------------------*/
|
||||
@ -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");
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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.
|
||||
|
@ -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[]);
|
||||
|
5
event.c
5
event.c
@ -64,6 +64,11 @@
|
||||
"BATCHAREA",
|
||||
"BATCHEND",
|
||||
"DRIVSTAT",
|
||||
"STATUS",
|
||||
"POSITION",
|
||||
"HDBVAL",
|
||||
"STATESTART",
|
||||
"STATEEND",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
17
event.h
17
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"
|
||||
|
||||
|
||||
|
||||
@ -43,20 +43,25 @@
|
||||
#define BATCHEND 16
|
||||
#define DRIVSTAT 17
|
||||
#define STATUS 18
|
||||
#define POSITION 19 /* Position event for motors - ffr */
|
||||
#line 104 "event.w"
|
||||
#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
|
||||
|
19
event.tex
19
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]
|
||||
|
11
event.w
11
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 @{
|
||||
/*----------------------------------------------------------------------------
|
||||
|
19
exe.w
19
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 @{
|
||||
|
55
exebuf.c
55
exebuf.c
@ -11,6 +11,7 @@
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <tcl.h>
|
||||
#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);
|
||||
|
||||
if(pCall != NULL){
|
||||
InvokeCallBack(pCall,BATCHSTART,self->name);
|
||||
}
|
||||
|
||||
if (echo) {
|
||||
SCsetMacro(pCon,0);
|
||||
}
|
||||
while((command = findBlockEnd(self)) != 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);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
17
exebuf.h
17
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
|
||||
|
4
exebuf.i
4
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"
|
||||
|
||||
|
||||
|
316
exeman.c
316
exeman.c
@ -15,14 +15,16 @@
|
||||
#include <ctype.h>
|
||||
#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,7 +177,44 @@ 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){
|
||||
pDynString filePath = 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,6 +1299,16 @@ 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){
|
||||
|
6
exeman.h
6
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
|
||||
|
6
exeman.i
6
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"
|
||||
|
||||
|
28
fitcenter.c
28
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);
|
||||
@ -467,9 +453,17 @@
|
||||
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;
|
||||
}
|
||||
|
338
fomerge.c
338
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 <stdlib.h>
|
||||
#include <assert.h>
|
||||
@ -27,6 +29,7 @@
|
||||
#include "fortify.h"
|
||||
#include "scan.h"
|
||||
#include "fitcenter.h"
|
||||
#include "sicsdata.h"
|
||||
|
||||
static pFit fitter = NULL;
|
||||
|
||||
@ -483,17 +486,17 @@ 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(iTime*sizeof(int));
|
||||
sum = (int *)malloc(iDet*sizeof(int));
|
||||
if(!sum)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
memset(sum,0,iTime*sizeof(int));
|
||||
memset(sum,0,iDet*sizeof(int));
|
||||
|
||||
for(i = 0; i < iDet; i++)
|
||||
{
|
||||
@ -505,6 +508,29 @@ static int *calculateSum(HistInt *data, int iDet, int iTime)
|
||||
}
|
||||
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)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
memset(sum,0,iTime*sizeof(int));
|
||||
|
||||
for(i = 0; i < iTime; i++)
|
||||
{
|
||||
for(j = 0; j < iDet; 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);
|
||||
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)
|
||||
|
24
fourlib.c
24
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
|
||||
|
@ -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
|
||||
|
622
genericcontroller.c
Normal file
622
genericcontroller.c
Normal file
@ -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 <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <tcl.h>
|
||||
#include <sics.h>
|
||||
#include <macro.h>
|
||||
#include <sicshipadaba.h>
|
||||
#include <sicsobj.h>
|
||||
#include <dynstring.h>
|
||||
#include <genericcontroller.h>
|
||||
/*--------------------------------------------------------------------------*/
|
||||
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;
|
||||
}
|
||||
|
37
genericcontroller.h
Normal file
37
genericcontroller.h
Normal file
@ -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 <asyncqueue.h>
|
||||
#include <sicshipadaba.h>
|
||||
|
||||
#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_*/
|
19
geninter.c
Normal file
19
geninter.c
Normal file
@ -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 <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <sics.h>
|
||||
#include <sicshipadaba.h>
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
int GenDrivableFactory(SConnection *pCon, SicsInterp *pSics,
|
||||
void *pData, int argc, char *argv[]){
|
||||
|
||||
return 1;
|
||||
}
|
19
geninter.h
Normal file
19
geninter.h
Normal file
@ -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_*/
|
497
hdbqueue.c
Normal file
497
hdbqueue.c
Normal file
@ -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 <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <sics.h>
|
||||
#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;
|
||||
}
|
15
hdbqueue.h
Normal file
15
hdbqueue.h
Normal file
@ -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_*/
|
956
hipadaba.c
Normal file
956
hipadaba.c
Normal file
@ -0,0 +1,956 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
The hierarchical parameter database code. For more information, see
|
||||
hipadaba.h
|
||||
|
||||
copyright: GPL
|
||||
|
||||
Mark Koennecke, June 2006
|
||||
---------------------------------------------------------------------------*/
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#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;
|
||||
}
|
||||
}
|
439
hipadaba.h
Normal file
439
hipadaba.h
Normal file
@ -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 <stringdict.h>
|
||||
|
||||
/*------- 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
|
85
histmem.c
85
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,6 +179,8 @@
|
||||
iRet = self->pDriv->Start(self->pDriv, pCon);
|
||||
if(iRet == OKOK)
|
||||
{
|
||||
/* send a COUNTSTART event */
|
||||
InvokeCallBack(self->pCall,COUNTSTART,pCon);
|
||||
updateHMData(self->pDriv->data);
|
||||
return iRet;
|
||||
}
|
||||
@ -452,6 +462,16 @@
|
||||
{
|
||||
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,6 +1248,12 @@ 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);
|
||||
@ -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 */
|
||||
@ -1379,6 +1435,11 @@ static int checkHMEnd(pHistMem self, char *text){
|
||||
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));
|
||||
if(!lData)
|
||||
@ -1387,8 +1448,14 @@ static int checkHMEnd(pHistMem self, char *text){
|
||||
return 0;
|
||||
}
|
||||
memset(lData,0,iEnd*sizeof(HistInt));
|
||||
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 */
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
273
histregress.c
Normal file
273
histregress.c
Normal file
@ -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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#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;
|
||||
}
|
31
histsim.c
31
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;
|
||||
|
87
hkl.c
87
hkl.c
@ -52,7 +52,10 @@
|
||||
return 1;
|
||||
}
|
||||
fprintf(fd,"#Crystallographic Settings\n");
|
||||
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 {
|
||||
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)
|
||||
{
|
||||
@ -788,9 +805,13 @@ static int calculateNormalBeam(MATRIX z1, pHKL self, SConnection *pCon,
|
||||
}
|
||||
if(checkNormalBeam(omnb, &gamma, nu,fSet,pCon,self)){
|
||||
return 1;
|
||||
} else {
|
||||
if(checkNormalBeam(omnb + 360., &gamma, nu, fSet,pCon,self)){
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------*/
|
||||
static int calculateNormalBeamOmega(MATRIX z1, pHKL self,
|
||||
@ -903,30 +924,6 @@ 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)
|
||||
{
|
||||
@ -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)
|
||||
{
|
||||
|
1
hkl.i
1
hkl.i
@ -15,7 +15,6 @@
|
||||
int iManual;
|
||||
double fLastHKL[5];
|
||||
int iNOR;
|
||||
int iOMPHI;
|
||||
int iQuad;
|
||||
int iHM;
|
||||
pMotor pTheta;
|
||||
|
1
hkl.tex
1
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;@\\
|
||||
|
1
hkl.w
1
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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
265
hmdata.c
265
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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include "splitter.h"
|
||||
#include "fortify.h"
|
||||
#include "hmdata.h"
|
||||
#include <nxdataset.h>
|
||||
#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){
|
||||
if(self->timeslave != NULL){
|
||||
return getNoOfTimebins(self->timeslave);
|
||||
} else {
|
||||
return self->nTimeChan;
|
||||
}
|
||||
}
|
||||
/*---------------------------------------------------------------------*/
|
||||
float *getTimeBinning(pHMdata self){
|
||||
if(self->timeslave != NULL){
|
||||
return getTimeBinning(self->timeslave);
|
||||
} else {
|
||||
return self->timeBinning;
|
||||
}
|
||||
}
|
||||
/*-------------------------------------------------------------------*/
|
||||
void clearTimeBinning(pHMdata 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,13 +409,14 @@ 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:
|
||||
if(isInTOFMode(self)){
|
||||
lSum = 0;
|
||||
for(i = iStart[0]; i < iEnd[0]; i++){
|
||||
iIndex = i*self->iDim[1];
|
||||
@ -381,11 +424,20 @@ long sumHMDataRectangle(pHistMem hist, SConnection *pCon,
|
||||
iIndex+iStart[1], iIndex+iEnd[1]);
|
||||
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;
|
||||
}
|
||||
|
7
hmdata.h
7
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
|
||||
|
||||
|
7
hmdata.w
7
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}
|
||||
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user