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