- Extended confvirtmot to allow for sequences of calls

- Extended confvirtmot to have  a checkscript
- Made sure that targets get updated when calling tasdrive
- Fixed some output codes in tasdrive.c
- Made tdchm invoke counters event message for Melone
- Fixed the ConID inefficiency by caching the host name in asynnet.c
- Added a traceActive function to trace


SKIPPED:
	psi/tdchm.c
This commit is contained in:
koennecke
2012-04-19 10:01:31 +00:00
parent 12b755de76
commit ecd96f9ab0
13 changed files with 165 additions and 31 deletions

View File

@ -57,6 +57,7 @@ typedef struct {
ANETcallback readCallback; ANETcallback readCallback;
void *userData; void *userData;
ANETkill killUser; ANETkill killUser;
char host[132];
} SocketDescriptor, *pSocketDescriptor; } SocketDescriptor, *pSocketDescriptor;
static SocketDescriptor connections[MAXCONNECTIONS]; static SocketDescriptor connections[MAXCONNECTIONS];
@ -480,6 +481,7 @@ int ANETinfo(int handle, char *hostname, int hostnameLen)
if (con == NULL) { if (con == NULL) {
return ANETDISCONNECTED; return ANETDISCONNECTED;
} else { } else {
if(strlen(con->host) < 3){
len = sizeof sin; len = sizeof sin;
if (getpeername(con->socket, (struct sockaddr *) &sin, &len) < 0) { if (getpeername(con->socket, (struct sockaddr *) &sin, &len) < 0) {
return ANETSOCKERROR; return ANETSOCKERROR;
@ -488,8 +490,10 @@ int ANETinfo(int handle, char *hostname, int hostnameLen)
sizeof(sin.sin_addr), AF_INET)) == NULL) { sizeof(sin.sin_addr), AF_INET)) == NULL) {
return ANETSOCKERROR; return ANETSOCKERROR;
} }
strlcpy(con->host,host->h_name, 132);
}
memset(hostname, 0, hostnameLen); memset(hostname, 0, hostnameLen);
strlcpy(hostname, host->h_name, hostnameLen); strlcpy(hostname, con->host, hostnameLen);
} }
return 1; return 1;
} }

View File

@ -12,6 +12,7 @@ typedef struct __CONFVIRTMOT {
char *scriptName; char *scriptName;
char *readScript; char *readScript;
char *checkScript; char *checkScript;
char *state;
int motorList; int motorList;
float targetValue; float targetValue;
int targetReached; int targetReached;

View File

@ -9,6 +9,22 @@
Mark Koennecke, August 2004 Mark Koennecke, August 2004
interest added: Ferdi Franceschini, April 2006 interest added: Ferdi Franceschini, April 2006
Additions:
- a checkscript to be run at the end of driving the motor
- a means to call the write script multiple times. This is useful if
you need to drive to the value in multiple steps. An example is the
BOA double crystal monochromator where you first have to pu the baldes to
0, then drive the translation and then drive the blades to the real theta.
All this to avoid collisions. In order to support this, a state field and
and a readable target field has been added. The mode of operation is such
that the on first run the, writescript set the state to something different
then idle. This causes after the first set of motors finished running the
writescript to be called again. This can figure out by lookin at the
state variable what to do. This can be doen in repetition until the
writescript sets state to idle again.
Mark Koennecke, April 2012
-------------------------------------------------------------------------*/ -------------------------------------------------------------------------*/
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
@ -299,7 +315,7 @@ static void invokeCheckScript(pConfigurableVirtualMotor self,
pTcl = InterpGetTcl(pServ->pSics); pTcl = InterpGetTcl(pServ->pSics);
if(self->checkScript != NULL){ if(self->checkScript != NULL){
status = Tcl_Eval(pTcl, self->readScript); status = Tcl_Eval(pTcl, self->checkScript);
if (status != TCL_OK) { if (status != TCL_OK) {
snprintf(self->scriptError, 510, "ERROR: Tcl subsystem reported %s", snprintf(self->scriptError, 510, "ERROR: Tcl subsystem reported %s",
Tcl_GetStringResult(pTcl)); Tcl_GetStringResult(pTcl));
@ -331,6 +347,10 @@ static int ConfCheckStatus(void *pData, SConnection * pCon)
break; break;
case HWFault: case HWFault:
case HWPosFault: case HWPosFault:
if(self->state != NULL){
free(self->state);
self->state = strdup("idle");
}
return status; return status;
break; break;
default: default:
@ -344,10 +364,20 @@ static int ConfCheckStatus(void *pData, SConnection * pCon)
} }
if (result == HWIdle) { if (result == HWIdle) {
/*
* Do we have to do another step? If so run it.
*/
if(strstr(self->state,"idle") == NULL){
status = ConfSetValue(self,pCon,self->targetValue);
if(status == OKOK){
result = HWBusy;
}
} else {
invokeCheckScript(self,pCon); invokeCheckScript(self,pCon);
event.pName = self->name; event.pName = self->name;
event.fVal = self->pDriv->GetValue(self, pCon); event.fVal = self->pDriv->GetValue(self, pCon);
InvokeCallBack(self->pCall, MOTDRIVE, &event); InvokeCallBack(self->pCall, MOTDRIVE, &event);
}
} else if (result == HWBusy) { } else if (result == HWBusy) {
self->posCount++; self->posCount++;
if (self->posCount >= 10 /*ObVal(self->ParArray,MOVECOUNT) */ ) { if (self->posCount >= 10 /*ObVal(self->ParArray,MOVECOUNT) */ ) {
@ -468,6 +498,10 @@ static void KillConfigurableVirtualMotor(void *data)
free(self->readScript); free(self->readScript);
self->readScript = NULL; self->readScript = NULL;
} }
if(self->state != NULL){
free(self->state);
self->state = NULL;
}
free(self); free(self);
self = NULL; self = NULL;
} }
@ -504,6 +538,7 @@ int MakeConfigurableVirtualMotor(SConnection * pCon, SicsInterp * pSics,
eError); eError);
return 0; return 0;
} }
pNew->state = strdup("idle");
/* /*
assign functions assign functions
@ -613,6 +648,31 @@ int ConfigurableVirtualMotorAction(SConnection * pCon, SicsInterp * pSics,
return 1; return 1;
} }
} }
} else if (strcmp(argv[1], "state") == 0) {
if (argc > 2) {
/* set case */
Arg2Text(argc - 2, &argv[2], pBueffel, 510);
self->state = strdup(pBueffel);
SCSendOK(pCon);
return 1;
} else {
/* inquiry */
if (self->state == NULL) {
snprintf(pBueffel, 510, "%s.state = UNDEFINED", argv[0]);
SCWrite(pCon, pBueffel, eValue);
return 1;
} else {
snprintf(pBueffel, 510, "%s.state = %s", argv[0],
self->state);
SCWrite(pCon, pBueffel, eValue);
return 1;
}
}
} else if (strcmp(argv[1], "target") == 0) {
/* inquiry */
snprintf(pBueffel, 510, "%s.target = %f", argv[0], self->targetValue);
SCWrite(pCon, pBueffel, eValue);
return 1;
} else if (strcmp(argv[1], "interest") == 0) { } else if (strcmp(argv[1], "interest") == 0) {
pRegInfo = (pRegisteredInfo) malloc(sizeof(RegisteredInfo)); pRegInfo = (pRegisteredInfo) malloc(sizeof(RegisteredInfo));
if (!pRegInfo) { if (!pRegInfo) {

View File

@ -17,6 +17,7 @@ typedef struct __CONFVIRTMOT {
char *scriptName; char *scriptName;
char *readScript; char *readScript;
char *checkScript; char *checkScript;
char *state;
int motorList; int motorList;
float targetValue; float targetValue;
int targetReached; int targetReached;

View File

@ -280,7 +280,7 @@ void ExeInterest(pExeList self, pDevEntry pDev, char *text)
} }
} }
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
static void InvokeNewTarget(pExeList self, char *name, float target) void InvokeNewTarget(pExeList self, char *name, float target)
{ {
NewTarget targetEvent; NewTarget targetEvent;
@ -772,25 +772,35 @@ int CheckExeList(pExeList self)
iRet = LLDnodePtr2First(self->iList); iRet = LLDnodePtr2First(self->iList);
return testFinish(self); return testFinish(self);
} }
/*--------------------------------------------------------------------------
Cludge to not move the current pointer for CheckExeList
*/
void *LLDgetCurrent(int List);
void LLDsetCurrent(int List, void *pointer);
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
int DevExecLevelRunning(pExeList self, int level) int DevExecLevelRunning(pExeList self, int level)
{ {
int iRet; int iRet;
pDevEntry pDev = NULL; pDevEntry pDev = NULL;
void *current;
if(self->lTask < 0){ if(self->lTask < 0){
return 0; return 0;
} }
current = LLDgetCurrent(self->iList);
iRet = LLDnodePtr2First(self->iList); iRet = LLDnodePtr2First(self->iList);
while (iRet != 0) { while (iRet != 0) {
LLDnodeDataTo(self->iList, &pDev); LLDnodeDataTo(self->iList, &pDev);
if (pDev) { if (pDev) {
if(pDev->level >= level){ if(pDev->level >= level){
LLDsetCurrent(self->iList,current);
return 1; return 1;
} }
} }
iRet = LLDnodePtr2Next(self->iList); iRet = LLDnodePtr2Next(self->iList);
} }
LLDsetCurrent(self->iList,current);
return 0; return 0;
} }

18
lld.c
View File

@ -22,6 +22,14 @@
- Node deletion via first and/or last node pointers. - Node deletion via first and/or last node pointers.
(as for access functions, then simplify ...) (as for access functions, then simplify ...)
** Comment by Mark Koennecke **
This implementation has problems when being accessed
concurrently in any form. The reason is the single
current pointer in the list data stucture. A improvement
would be to have a function to get an iterator which holds
this crucial value and functions to iterate on the iterator
*************** end of comment *********
_____ This version is Public Domain. _____ This version is Public Domain.
/_|__| A.Reitsma, Delft, The Netherlands. /_|__| A.Reitsma, Delft, The Netherlands.
/ | \ --------------------------------------------------------------- */ / | \ --------------------------------------------------------------- */
@ -647,3 +655,13 @@ int LLDnodeDataFrom(int List, void *source)
} }
/* ==== LLD.c end ==================================================== */ /* ==== LLD.c end ==================================================== */
/**
* These two functions help me implement a solution for a race condition
* in devexec.c, especially in DevexecLevelRunning.
*/
void *LLDgetCurrent(int List){
return ListControl[List].current;
}
void LLDsetCurrent(int List, void *pointer){
ListControl[List].current = (struct Node*)pointer;
}

View File

@ -364,6 +364,7 @@ static void finishDriving(pMotor self, SConnection * pCon)
InvokeCallBack(self->pCall, MOTDRIVE, &sCall); /* send also very last position */ InvokeCallBack(self->pCall, MOTDRIVE, &sCall); /* send also very last position */
InvokeCallBack(self->pCall, MOTEND, &sCall); InvokeCallBack(self->pCall, MOTEND, &sCall);
tracePar(self->name,"%f",sCall.fVal); tracePar(self->name,"%f",sCall.fVal);
self->running = 0;
} }
/*--------------------------------------------------------------------*/ /*--------------------------------------------------------------------*/
@ -484,6 +485,10 @@ static int MotorStatus(void *sulf, SConnection * pCon)
assert(sulf); assert(sulf);
self = (pMotor) sulf; self = (pMotor) sulf;
if(self->running != 1){
return HWIdle;
}
status = evaluateStatus(self, pCon); status = evaluateStatus(self, pCon);
if (self->pDrivInt->drivableStatus != status) { if (self->pDrivInt->drivableStatus != status) {
((SConnection *) pCon)->conEventType = STATUS; ((SConnection *) pCon)->conEventType = STATUS;
@ -752,10 +757,12 @@ static long MotorRunImpl(void *sulf, SConnection * pCon, float fNew)
case MOTREDO: case MOTREDO:
iRet = self->pDriver->RunTo(self->pDriver, fHard); iRet = self->pDriver->RunTo(self->pDriver, fHard);
if (iRet == OKOK) { if (iRet == OKOK) {
self->running = 1;
return OKOK; return OKOK;
} }
break; break;
case MOTOK: case MOTOK:
self->running = 1;
return OKOK; return OKOK;
break; break;
} }
@ -766,6 +773,7 @@ static long MotorRunImpl(void *sulf, SConnection * pCon, float fNew)
SCSetInterrupt(pCon, (int) ObVal(self->ParArray, INT)); SCSetInterrupt(pCon, (int) ObVal(self->ParArray, INT));
return HWFault; return HWFault;
} }
self->running = 1;
return OKOK; return OKOK;
} }
@ -1109,6 +1117,7 @@ void MotorReset(pMotor pM)
usUser); usUser);
ObParInit(pM->ParArray, SZERO, "softzero", ZEROINACTIVE, usUser); ObParInit(pM->ParArray, SZERO, "softzero", ZEROINACTIVE, usUser);
ObParInit(pM->ParArray, FIX, "fixed", -1, usUser); ObParInit(pM->ParArray, FIX, "fixed", -1, usUser);
pM->running = 0;
} }
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/

View File

@ -38,6 +38,7 @@ typedef struct __Motor {
int posFaultCount; int posFaultCount;
int stopped; int stopped;
int errorCount; int errorCount;
int running;
ObPar *ParArray; ObPar *ParArray;
void *pPrivate; void *pPrivate;
void (*KillPrivate) (void *); void (*KillPrivate) (void *);

View File

@ -472,6 +472,13 @@ static char *SctActionHandler(void *actionData, char *lastReply,
SCPrintf(con, eError, SCPrintf(con, eError,
"ERROR: action {%s} in {%s} node %s:\nERROR: %s", "ERROR: action {%s} in {%s} node %s:\nERROR: %s",
data->name, origScript, path, result); data->name, origScript, path, result);
if(data != NULL && data->controller != NULL){
traceIO(data->controller->node->name, "ERROR: action {%s} in {%s} node %s:\nERROR: %s",
data->name, origScript, path, result);
} else {
traceIO("sctunknown", "reply:%s", "ERROR: action {%s} in {%s} node %s:\nERROR: %s",
data->name, origScript, path, result);
}
} }
snprintf(msg, sizeof msg, "{%s} %s", origScript, result); snprintf(msg, sizeof msg, "{%s} %s", origScript, result);
if (strcasecmp(data->name, "read") == 0) { if (strcasecmp(data->name, "read") == 0) {
@ -776,13 +783,14 @@ static hdbCallbackReturn SctActionCallback(Hdb * node, void *userData,
*/ */
SCDeleteConnection(data->conCtx); SCDeleteConnection(data->conCtx);
} }
DeleteDynString(text);
data->conCtx = SCCopyConnection(con); data->conCtx = SCCopyConnection(con);
data->answered = 0; data->answered = 0;
data->inMacro = SCinMacro(con); data->inMacro = SCinMacro(con);
tracePar(node->name,"Queued %s to %s",node->name, GetCharArray(text));
DevQueue(data->controller->devser, data, prio, DevQueue(data->controller->devser, data, prio,
SctWriteHandler, SctMatch, NULL, SctDataInfo); SctWriteHandler, SctMatch, NULL, SctDataInfo);
/* no kill function in DevQueue: data is owned by the node (callback list) */ /* no kill function in DevQueue: data is owned by the node (callback list) */
DeleteDynString(text);
return hdbContinue; return hdbContinue;
} }

View File

@ -533,7 +533,7 @@ int VarWrapper(SConnection * pCon, SicsInterp * pInterp, void *pData,
Undocumented feauture: force a set even while driving etc. Undocumented feauture: force a set even while driving etc.
Internal privilege required to do this. Internal privilege required to do this.
*/ */
if (!SCMatchRights(pCon, usInternal)) { if (!SCMatchRights(pCon, usUser)) {
return 0; return 0;
} }
pCurrent = pCurrent->pNext; pCurrent = pCurrent->pNext;

View File

@ -43,7 +43,7 @@ static long TASSetValue(void *pData, SConnection * pCon, float value)
if (self->code > 5 && self->math->tasMode == ELASTIC) { if (self->code > 5 && self->math->tasMode == ELASTIC) {
SCWrite(pCon, "ERROR: cannot drive this motor in elastic mode", SCWrite(pCon, "ERROR: cannot drive this motor in elastic mode",
eError); eLogError);
return HWFault; return HWFault;
} }
setTasPar(&self->math->target, self->math->tasMode, self->code, value); setTasPar(&self->math->target, self->math->tasMode, self->code, value);
@ -146,7 +146,7 @@ static float TASGetValue(void *pData, SConnection * pCon)
&self->math->current); &self->math->current);
if (status < 0) { if (status < 0) {
SCWrite(pCon, "ERROR: out of memory calculating Q-E variables", SCWrite(pCon, "ERROR: out of memory calculating Q-E variables",
eError); eLogError);
return -999.99; return -999.99;
} }
if (self->math->tasMode == ELASTIC) { if (self->math->tasMode == ELASTIC) {
@ -182,7 +182,7 @@ static float TASQMGetValue(void *pData, SConnection * pCon)
angles, &self->math->current); angles, &self->math->current);
if (status < 0) { if (status < 0) {
SCWrite(pCon, "ERROR: out of memory calculating Q-E variables", SCWrite(pCon, "ERROR: out of memory calculating Q-E variables",
eError); eLogError);
return -999.99; return -999.99;
} }
if (self->math->tasMode == ELASTIC) { if (self->math->tasMode == ELASTIC) {
@ -250,6 +250,8 @@ static float getMotorValue(pMotor mot, SConnection * pCon)
GetDrivablePosition(mot, pCon, &val); GetDrivablePosition(mot, pCon, &val);
return val; return val;
} }
/*--------------------- In devexec.c --------------------------------------*/
void InvokeNewTarget(pExeList self, char *name, float target);
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
static int startTASMotor(pMotor mot, SConnection * pCon, char *name, static int startTASMotor(pMotor mot, SConnection * pCon, char *name,
@ -268,20 +270,25 @@ static int startTASMotor(pMotor mot, SConnection * pCon, char *name,
MotorGetPar(mot, "fixed", &fixed); MotorGetPar(mot, "fixed", &fixed);
if (ABS(fixed - 1.0) < .1) { if (ABS(fixed - 1.0) < .1) {
snprintf(buffer, 131, "WARNING: %s is FIXED", name); snprintf(buffer, 131, "WARNING: %s is FIXED", name);
SCWrite(pCon, buffer, eWarning); SCWrite(pCon, buffer, eLog);
return OKOK; return OKOK;
} }
} }
mot->stopped = 0; mot->stopped = 0;
if (ABS(val - target) > MOTPREC) { if (ABS(val - target) > MOTPREC) {
pDriv = GetDrivableInterface(mot); pDriv = GetDrivableInterface(mot);
SCStartBuffering(pCon); /* SCStartBuffering(pCon); */
status = pDriv->SetValue(mot, pCon, (float) target); status = pDriv->SetValue(mot, pCon, (float) target);
mes = SCEndBuffering(pCon); /*
to force updates on targets
*/
InvokeNewTarget(pServ->pExecutor, name, target);
/*mes = SCEndBuffering(pCon);
if (status != OKOK) { if (status != OKOK) {
SCPrintf(pCon,eLogError, GetCharArray(mes)); SCPrintf(pCon,eLogError, GetCharArray(mes));
return status; return status;
} }
*/
} }
writeMotPos(pCon, silent, name, val, target); writeMotPos(pCon, silent, name, val, target);
return status; return status;
@ -316,7 +323,7 @@ static int startMotors(ptasMot self, tasAngles angles,
status = startTASMotor(self->math->motors[MCV], pCon, "mcv", status = startTASMotor(self->math->motors[MCV], pCon, "mcv",
curve, silent); curve, silent);
if (status != OKOK) { if (status != OKOK) {
SCWrite(pCon,"WARNING: monochromator vertical curvature motor failed to start", eWarning); SCWrite(pCon,"WARNING: monochromator vertical curvature motor failed to start", eLog);
SCSetInterrupt(pCon,eContinue); SCSetInterrupt(pCon,eContinue);
} }
} }
@ -327,7 +334,7 @@ static int startMotors(ptasMot self, tasAngles angles,
status = startTASMotor(self->math->motors[MCH], pCon, "mch", status = startTASMotor(self->math->motors[MCH], pCon, "mch",
curve, silent); curve, silent);
if (status != OKOK) { if (status != OKOK) {
SCWrite(pCon,"WARNING: monochromator horizontal curvature motor failed to start", eWarning); SCWrite(pCon,"WARNING: monochromator horizontal curvature motor failed to start", eLog);
SCSetInterrupt(pCon,eContinue); SCSetInterrupt(pCon,eContinue);
} }
} }
@ -354,7 +361,7 @@ static int startMotors(ptasMot self, tasAngles angles,
status = startTASMotor(self->math->motors[ACV], pCon, "acv", status = startTASMotor(self->math->motors[ACV], pCon, "acv",
curve, silent); curve, silent);
if (status != OKOK) { if (status != OKOK) {
SCWrite(pCon,"WARNING: analyzer vertical curvature motor failed to start", eWarning); SCWrite(pCon,"WARNING: analyzer vertical curvature motor failed to start", eLog);
SCSetInterrupt(pCon,eContinue); SCSetInterrupt(pCon,eContinue);
} }
} }
@ -364,7 +371,7 @@ static int startMotors(ptasMot self, tasAngles angles,
status = startTASMotor(self->math->motors[ACH], pCon, "ach", status = startTASMotor(self->math->motors[ACH], pCon, "ach",
curve, silent); curve, silent);
if (status != OKOK) { if (status != OKOK) {
SCWrite(pCon,"WARNING: analyzer horizontal curvature motor failed to start", eWarning); SCWrite(pCon,"WARNING: analyzer horizontal curvature motor failed to start", eLog);
SCSetInterrupt(pCon,eContinue); SCSetInterrupt(pCon,eContinue);
} }
} }
@ -422,7 +429,7 @@ static int checkQMotorLimits(ptasMot self, SConnection * pCon,
if (status != 1) { if (status != 1) {
retVal = 0; retVal = 0;
snprintf(pBueffel, 256, "ERROR: limit violation an a3: %s", error); snprintf(pBueffel, 256, "ERROR: limit violation an a3: %s", error);
SCWrite(pCon, pBueffel, eError); SCWrite(pCon, pBueffel, eLogError);
} }
} }
@ -434,7 +441,7 @@ static int checkQMotorLimits(ptasMot self, SConnection * pCon,
if (status != 1) { if (status != 1) {
retVal = 0; retVal = 0;
snprintf(pBueffel, 256, "ERROR: limit violation an a4: %s", error); snprintf(pBueffel, 256, "ERROR: limit violation an a4: %s", error);
SCWrite(pCon, pBueffel, eError); SCWrite(pCon, pBueffel, eLogError);
} }
if (driveTilt == 1) { if (driveTilt == 1) {
@ -445,7 +452,7 @@ static int checkQMotorLimits(ptasMot self, SConnection * pCon,
if (status != 1) { if (status != 1) {
retVal = 0; retVal = 0;
snprintf(pBueffel, 256, "ERROR: limit violation an SGU: %s", error); snprintf(pBueffel, 256, "ERROR: limit violation an SGU: %s", error);
SCWrite(pCon, pBueffel, eError); SCWrite(pCon, pBueffel, eLogError);
} }
pDrivInt = GetDrivableInterface(self->math->motors[SGL]); pDrivInt = GetDrivableInterface(self->math->motors[SGL]);
@ -455,7 +462,7 @@ static int checkQMotorLimits(ptasMot self, SConnection * pCon,
if (status != 1) { if (status != 1) {
retVal = 0; retVal = 0;
snprintf(pBueffel, 256, "ERROR: limit violation an SGL: %s", error); snprintf(pBueffel, 256, "ERROR: limit violation an SGL: %s", error);
SCWrite(pCon, pBueffel, eError); SCWrite(pCon, pBueffel, eLogError);
} }
} }
return retVal; return retVal;

View File

@ -31,6 +31,15 @@ static int hdbInit = 0;
static int filterProv = 0; static int filterProv = 0;
static int debug = 0; static int debug = 0;
/*----------------------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------------------*/
int traceActive()
{
if (log == NULL){
return 0;
} else {
return 1;
}
}
/*----------------------------------------------------------------------------------------*/
static char *GetTracePath(pHdb node) static char *GetTracePath(pHdb node)
{ {
pHdb nodeStack[64]; pHdb nodeStack[64];

View File

@ -29,4 +29,10 @@ void traceprint(char *sub, char *id,char *data);
* A debugging trace. This has to be switched on separately * A debugging trace. This has to be switched on separately
*/ */
void traceDebug(char *id, char *format, ...); void traceDebug(char *id, char *format, ...);
/*
* 1 when tracing active, 0 else
*/
int traceActive();
#endif /* TRACE_H_ */ #endif /* TRACE_H_ */