From 35f2b6b810ce3e46fc00fe9eb65ec54bdf5acb05 Mon Sep 17 00:00:00 2001 From: zolliker Date: Thu, 19 Feb 2009 13:30:32 +0000 Subject: [PATCH] - allow scriptcontext objects to be dynamic - enhancements in scriptcontext (error messages stored as properties) --- SCinter.c | 12 +-- ascon.c | 172 ++++++++++++++++++++++++++---------------- ascon.h | 15 ++-- ascon.i | 14 +++- commandlog.c | 24 ++++-- devexec.c | 39 +++++++++- devexec.h | 16 ++-- devexec.tex | 12 +-- devexec.w | 2 + devser.c | 55 ++++++++------ devser.h | 14 ++-- errormsg.c | 23 ++++-- errormsg.h | 20 ++++- hipadaba.c | 1 + macro.c | 25 ++++-- make_gen | 4 +- motor.c | 29 ++++--- network.c | 53 ++++++++++--- network.h | 9 ++- nserver.c | 2 +- ofac.c | 5 +- script.c | 42 ++++++++++- script.h | 3 + scriptcontext.c | 188 ++++++++++++++++++++++++++++++++++------------ sctdriveadapter.c | 11 +-- sctdriveobj.c | 136 ++++++++++++++++++++------------- sicshipadaba.c | 29 +++---- sicshipadaba.h | 4 + sicsobj.c | 57 ++++++++++++-- sicsobj.h | 3 + stringdict.c | 11 +++ task.c | 27 +++++++ task.h | 6 +- 33 files changed, 753 insertions(+), 310 deletions(-) diff --git a/SCinter.c b/SCinter.c index 5103bb21..f640258e 100644 --- a/SCinter.c +++ b/SCinter.c @@ -213,7 +213,9 @@ int RemoveCommand(SicsInterp * pInterp, char *pName) /* found, remove it */ /* kall KillFunction first */ if (pVictim->KFunc) { - pVictim->KFunc(pVictim->pData); + void *data = pVictim->pData; + pVictim->pData = NULL; /* make data unreachable by FindCommandData before killing */ + pVictim->KFunc(data); } /* delete and unlink data */ @@ -237,7 +239,6 @@ int RemoveCommand(SicsInterp * pInterp, char *pName) return 1; } - #define MAXLEN 256 #define MAXCOM 50 extern char *stptok(char *s, char *tok, unsigned int toklen, char *brk); @@ -1047,10 +1048,9 @@ char *FindAliases(SicsInterp * pSics, char *name) } /*---------------------------------------------------------------------*/ -void -ForEachCommand(int (*scanFunction) - (char *name, pDummy object, void *userData) - , void *userData) +void ForEachCommand(int (*scanFunction) + (char *name, pDummy object, void *userData) + , void *userData) { CommandList *pCurrent; diff --git a/ascon.c b/ascon.c index b6e1d1c2..8516ad43 100644 --- a/ascon.c +++ b/ascon.c @@ -14,6 +14,8 @@ #include "ascon.i" #include "uselect.h" +static double lastClose = 0; /* time of last close operation */ + /* CreateSocketAdress stolen from Tcl. Thanks to John Ousterhout */ @@ -60,13 +62,13 @@ double DoubleTime(void) return now.tv_sec + now.tv_usec / 1e6; } -void AsconError(Ascon * a, char *msg, int errorno) +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", + static char *stateText[]={ + "state 0", "state 1", "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; @@ -76,13 +78,19 @@ void AsconError(Ascon * a, char *msg, int errorno) } else { state = stateText[a->state]; } - if (errorno != 0) { - a->errList = - ErrPutMsg(a->errList, "ASCERR: %s %s (during %s)", msg, - strerror(errorno), state); + DynStringCopy(a->errmsg, "ASCERR: "); + if (errorno == 0) { + DynStringConcat(a->errmsg, msg); + DynStringConcat(a->errmsg, " ("); + DynStringConcat(a->errmsg, state); + DynStringConcat(a->errmsg, " state)"); } else { - a->errList = - ErrPutMsg(a->errList, "ASCERR: %s (during %s)", msg, state); + DynStringConcat(a->errmsg, strerror(errorno)); + DynStringConcat(a->errmsg, " ("); + DynStringConcat(a->errmsg, state); + DynStringConcat(a->errmsg, " state, "); + DynStringConcat(a->errmsg, msg); + DynStringConcat(a->errmsg, ")"); } a->state |= AsconFailed; } @@ -96,11 +104,23 @@ static void AsconConnect(Ascon * a) char *colon; int port; int oldopts; + + /* wait 0.5 sec before connecting again after a close + 2 reasons for that: + - it seems that connecting immediately to a closed port fails. + We will avoid some "Connection refused" error messages. + - a bug in the lantronix terminal: if a channel is closed and reopened + within short time the connect may be succesful, but a message will be + sent on the channel! + In principle we need only to wait when connecting to the same address + and port, but bookkeeping would be too complicated. + */ + if (DoubleTime() < lastClose + 0.5) return; if (a->fd < 0) { a->fd = socket(AF_INET, SOCK_STREAM, 0); if (a->fd < 0) { - AsconError(a, "socket failed:", errno); + AsconError(a, "ASC1", errno); return; } } @@ -119,20 +139,19 @@ static void AsconConnect(Ascon * a) 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) { + switch(errno) { case EINPROGRESS: case EALREADY: case EISCONN: a->state = AsconConnecting; break; default: - AsconError(a, "connect failed:", errno); + AsconError(a, "ASC2", errno); return; } } @@ -140,11 +159,10 @@ static void AsconConnect(Ascon * a) return; } -int AsconStdInit(Ascon * a, SConnection * con, int argc, char *argv[]) +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]); @@ -154,7 +172,11 @@ int AsconStdInit(Ascon * a, SConnection * con, int argc, char *argv[]) if (argc > 3) { a->timeout = atof(argv[3]); } else { - a->timeout = 2.0; /* sec */ + a->timeout = 2.0; /* sec */ + } + a->replyTerminator == NULL; + if (argc > 4 && argv[4][0] != '\0') { + a->replyTerminator = strdup(argv[4]); } return 1; } @@ -172,9 +194,11 @@ int AsconReadGarbage(int fd) FD_SET(fd, &rmask); ret = uselect(fd + 1, &rmask, NULL, NULL, &tmo); if (ret > 0) { - l = recv(fd, garbage, sizeof garbage, 0); + l = recv(fd, garbage, sizeof garbage - 1, 0); if (l > 0) { /* swallow */ + garbage[l] = '\0'; + printf("(((%s)))\n", garbage); result += l; } else if (l == 0) { errno = ECONNRESET; @@ -307,7 +331,7 @@ int AsconStdHandler(Ascon * a) } else if (ret > 0) { a->state = AsconConnectDone; /* success */ } else if (ret < 0) { - AsconError(a, "AsconConnectSuccess failed:", errno); + AsconError(a, "ASC3", errno); } break; case AsconWriteStart: @@ -320,13 +344,22 @@ int AsconStdHandler(Ascon * a) } break; case AsconWriting: + if (a->readState) { /* last char was CR */ + ret = AsconReadChar(a->fd, &chr); + if (ret > 0) { + if (chr == '\n') { + /* swallow LF after CR */ + a->readState = 0; + } else { + /* garbage character found */ + } + } + } AsconReadGarbage(a->fd); l = GetDynStringLength(a->wrBuffer) - a->wrPos; ret = AsconWriteChars(a->fd, GetCharArray(a->wrBuffer) + a->wrPos, l); if (ret < 0) { - if (errno != EINTR && errno != EAGAIN) { - AsconError(a, "send failed:", errno); - } + AsconError(a, "ASC4", errno); /* * Ooops: which state shall we go to after a write fail? * This seems to retry. @@ -347,23 +380,33 @@ int AsconStdHandler(Ascon * a) 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 { + + if (a->replyTerminator != NULL) { + if (strchr(a->replyTerminator, chr) != NULL) { + DynStringConcatChar(a->rdBuffer, chr); 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 (chr == '\n') { + if (a->readState) { /* last char was CR */ + /* swallow LF after CR */ + a->readState = 0; + chr = 0; + } else { + DynStringConcatChar(a->rdBuffer, '\0'); + a->state = AsconReadDone; + break; + } + } else if (chr == '\r') { + a->readState = 1; /* set 'last char was CR' */ + DynStringConcatChar(a->rdBuffer, '\0'); + a->state = AsconReadDone; + break; + } + } + if (chr != 0) { if (DynStringConcatChar(a->rdBuffer, chr) == 0) { AsconError(a, "DynStringConcatChar failed:", ENOMEM); break; @@ -375,7 +418,7 @@ int AsconStdHandler(Ascon * a) if (ret < 0) { /* EINTR means we shall retry */ if (errno != EINTR && errno != EAGAIN) { - AsconError(a, "AsconReadChar failed:", errno); + AsconError(a, "ASC5", errno); } return 1; } @@ -384,30 +427,26 @@ int AsconStdHandler(Ascon * a) } else { if (a->timeout > 0) { if (DoubleTime() - a->start > a->timeout) { - AsconError(a, "read timeout", 0); + AsconError(a, "no response", 0); a->state = AsconTimeout; } } } break; default: - return 1; - } + break; + } return 1; } -/* define type AsconProtocolList and functions AsconProtocolAdd etc. */ -#define MC_NAME(T) AsconProtocol##T -#include "mclist.c" +static AsconProtocol *protocols = NULL; -static AsconProtocolList protocols = { 0 }; - -void AsconInsertProtocol(AsconProtocol * protocol) -{ - AsconProtocolAdd(&protocols, protocol); +void AsconInsertProtocol(AsconProtocol *protocol) { + protocol->next = protocols; + protocols = protocol; } -AsconHandler AsconSetHandler(Ascon * a, SConnection * con, +AsconHandler AsconSetHandler(Ascon *a, SConnection *con, int argc, char *argv[]) { AsconProtocol *p; @@ -420,7 +459,7 @@ AsconHandler AsconSetHandler(Ascon * a, SConnection * con, AsconStdInit(a, con, argc, argv); return AsconStdHandler; } - for (p = protocols.head; p != NULL; p = p->next) { + for (p = protocols; p!= NULL; p=p->next) { if (strcasecmp(p->name, argv[0]) == 0) { if (p->init(a, con, argc, argv)) { return p->handler; @@ -460,10 +499,8 @@ Ascon *AsconMake(SConnection * con, int argc, char *argv[]) } a->rdBuffer = CreateDynString(60, 63); a->wrBuffer = CreateDynString(60, 63); - a->errList = NULL; + a->errmsg = CreateDynString(60, 63); a->responseValid = 0; - a->reconnectInterval = 10; - a->lastReconnect = 0; return a; } @@ -471,15 +508,20 @@ void AsconKill(Ascon * a) { if (a->fd > 0) { close(a->fd); + lastClose = DoubleTime(); } DeleteDynString(a->rdBuffer); DeleteDynString(a->wrBuffer); + DeleteDynString(a->errmsg); if (a->hostport) { free(a->hostport); } - if (a->sendTerminator) { + if(a->sendTerminator){ free(a->sendTerminator); } + if(a->replyTerminator){ + free(a->replyTerminator); + } if (a->private != NULL && a->killPrivate != NULL) { a->killPrivate(a->private); } @@ -528,17 +570,15 @@ AsconStatus AsconTask(Ascon * a) case AsconStart: return AsconPending; case AsconFailed: - if (a->state != AsconTimeout) { - now = DoubleTime(); - if (now > a->lastReconnect + a->reconnectInterval) { - a->lastReconnect = now; - close(a->fd); - /* allow the system to cleanup the socket, otherwise a reconnect will fail */ - sleep(1); - a->fd = -1; - a->state = AsconConnectStart; - } + if (a->state == AsconTimeout) { + a->state = AsconIdle; + } else { + close(a->fd); + lastClose = DoubleTime(); + a->fd = -1; + a->state = AsconConnectStart; } + return AsconFailure; case AsconFinished: if (a->state < AsconConnectFailed) { @@ -580,7 +620,7 @@ char *AsconRead(Ascon * a) return NULL; } -ErrMsg *AsconGetErrList(Ascon * a) +char *AsconGetError(Ascon *a) { - return a->errList; + return GetCharArray(a->errmsg); } diff --git a/ascon.h b/ascon.h index 5a2f9af9..4fcf4f19 100644 --- a/ascon.h +++ b/ascon.h @@ -2,7 +2,6 @@ #define ASCON_H #include "sics.h" -#include "errormsg.h" /** \file * \brief Asynchronous connection handling for devices controlled over tcp-ip @@ -60,15 +59,17 @@ int AsconWrite(Ascon * a, char *command, int noResponse); * \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. + * The result is only valid until the next call to any AsconXxx function + * with the same connection and has to be duplicated if needed later. */ char *AsconRead(Ascon * a); -/** \brief get the connections error list - * \return the error list +/** \brief get the last error message + * \return the error message + * The result is only valid until the next call to any AsconXxx function + * with the same connection and has to be duplicated if needed later. */ -ErrMsg *AsconGetErrList(Ascon * a); +char *AsconGetError(Ascon *a); /** \brief a helper function * \param argc the number of args @@ -78,7 +79,7 @@ ErrMsg *AsconGetErrList(Ascon * a); */ char *ConcatArgs(int argc, char *argv[]); -/** \brief function for dealing with times with musec resolution +/** \brief function for dealing with times with microsec resolution * \return absolute time as double value */ double DoubleTime(void); diff --git a/ascon.i b/ascon.i index 25f01eaa..cfc9a9ab 100644 --- a/ascon.i +++ b/ascon.i @@ -12,7 +12,7 @@ * 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 + * The handler and init functions are normally a wrapper around AsconStdHandler * and AsconStdInit * * The functions with fd as the first argument are utility functions with @@ -64,8 +64,9 @@ struct Ascon { int wrPos; /**< write buffer position */ double timeout; /**< read timeout (sec) */ char *sendTerminator; /**< terminator for sending messages */ + char *replyTerminator; /**< terminator list for reply. NULL is the special case CR, LF or CR/LF */ char *hostport; /**< host:port to connect */ - ErrMsg *errList; /**< error message list */ + pDynString errmsg; /**< error message */ double start; /**< unix time when read was started */ void *private; /**< private data of protocol */ void (*killPrivate)(void *); /** < kill function for private */ @@ -73,7 +74,6 @@ struct Ascon { 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 @@ -92,7 +92,13 @@ 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) + * \param argc number of arguments + * \param argv arguments (":" [sendTerminator] [timeout] [replyTerminators]) + * sendTerminator is a character or string sent at the end of a command + * timoeut is in seconds + * replyTerminator is a string, meant as a list of terminator characters + * if not replyTerminator is given, CR, LF or CR/LF all are detected as + * terminators * * In most cases a custom init function may be a wrapper around AsconStdInit */ diff --git a/commandlog.c b/commandlog.c index 503a2c7d..d158473d 100644 --- a/commandlog.c +++ b/commandlog.c @@ -89,7 +89,6 @@ void WriteToCommandLogId(char *prompt, int id, char *text) return; strncpy(pCopy, text, l); pCopy[l] = '\0'; - if (prompt == cmdPrompt && iCompact) { pPtr = strstr(pCopy, "fulltransact "); if (pPtr && pPtr < pCopy + 3) { @@ -101,17 +100,30 @@ void WriteToCommandLogId(char *prompt, int id, char *text) } } + now = time(NULL); + /* create tail buffer as needed */ if (!pTail) { pTail = createCircular(MAXTAIL, free); } - now = time(NULL); - doStamp = 0; doStampId = 0; - if (id == NOID) { + if (iCompact == 1) { + nowTm = localtime(&now); + strftime(stamp1, sizeof stamp1, "%H:%M:%S", nowTm); + if (prompt == NULL) + prompt = ""; + if (id == NOID) { + if (!prompt) + prompt = " "; + snprintf(buffer, sizeof buffer, "%s %s ", stamp1, prompt); + } else { + snprintf(buffer, sizeof buffer, "%s %2.0d| ", stamp1, id); + } + prompt = buffer; + } else if (id == NOID) { if (!prompt) { prompt = ""; } else { @@ -125,7 +137,7 @@ void WriteToCommandLogId(char *prompt, int id, char *text) snprintf(buffer, sizeof buffer, "sock %d>%s ", id, prompt); } prompt = buffer; - } else { + } else if (iCompact > 1) { if (id != lastId) { lastId = id; doStampId = 1; @@ -138,7 +150,7 @@ void WriteToCommandLogId(char *prompt, int id, char *text) } } - if (iCompact > 0) { /* write time stamp */ + if (iCompact > 1) { /* write time stamp */ if (now / iCompact != lastStamp / iCompact) { doStamp = 1; doStampId = 1; diff --git a/devexec.c b/devexec.c index 6e54f432..6ce19f74 100644 --- a/devexec.c +++ b/devexec.c @@ -788,6 +788,40 @@ int StopExe(pExeList self, char *name) return 0; } +/*-------------------------------------------------------------------------*/ +int StopByData(pExeList self, void *data) +{ + int iRet; + pDevEntry pDev = NULL; + pIDrivable pDrivInt = NULL; + pICountable pCountInt = NULL; + + iRet = LLDnodePtr2First(self->iList); + while (iRet != 0) { + pDev = (pDevEntry) LLDnodePtr(self->iList); + if (pDev) { + if (pDev->pData == data) { + pDrivInt = pDev->pDescriptor->GetInterface(pDev->pData, DRIVEID); + pCountInt = pDev->pDescriptor->GetInterface(pDev->pData, COUNTID); + if (pDrivInt) { + pDrivInt->Halt(pDev->pData); + } else if (pCountInt) { + pCountInt->Halt(pDev->pData); + } + ExeInterest(self, pDev, "finished"); + DevexecLog("FINISHED", pDev->name); + DeleteDevEntry(pDev); + LLDnodeDelete(self->iList); + self->iStatus = DEVDONE; + SCWrite(self->pOwner, "", eFinish); + return 1; + } + } + iRet = LLDnodePtr2Next(self->iList); + } + return 0; +} + /*-------------------------------------------------------------------------*/ int StopExeWait(pExeList self) { @@ -795,7 +829,6 @@ int StopExeWait(pExeList self) Wait4Success(self); return 1; } - /*--------------------------------------------------------------------------*/ int PauseExecution(pExeList self) { @@ -1006,7 +1039,6 @@ int SicsIdle(SConnection * pCon, SicsInterp * pSics, void *pData, Usage: Success */ - int Success(SConnection * pCon, SicsInterp * pSics, void *pData, int argc, char *argv[]) { @@ -1020,6 +1052,8 @@ int Success(SConnection * pCon, SicsInterp * pSics, void *pData, if (SCGetInterrupt(pCon) == eAbortOperation) { SCSetInterrupt(pCon, eContinue); iRet = 0; + } else { + iRet = 1; } } else if (iRet == DEVDONE) { SCWrite(pCon, "All done", eValue); @@ -1031,7 +1065,6 @@ int Success(SConnection * pCon, SicsInterp * pSics, void *pData, SetStatus(eEager); return iRet; } - /*-------------------------------------------------------------------------*/ int PauseAction(SConnection * pCon, SicsInterp * pSics, void *pData, int argc, char *argv[]) diff --git a/devexec.h b/devexec.h index 59b662e7..8ea32768 100644 --- a/devexec.h +++ b/devexec.h @@ -1,5 +1,5 @@ -#line 195 "devexec.w" +#line 197 "devexec.w" /*---------------------------------------------------------------------------- @@ -54,7 +54,7 @@ int StartMotor(pExeList self, SicsInterp * pSics, SConnection * pCon, int StartCounter(pExeList self, SicsInterp * pSics, SConnection * pCon, char *name); -#line 239 "devexec.w" +#line 241 "devexec.w" /*------------------------------------------------------------------------*/ @@ -73,7 +73,7 @@ int DevExecTask(void *pEL); void DevExecSignal(void *pEL, int iSignal, void *pSigData); -#line 241 "devexec.w" +#line 243 "devexec.w" /* @@ -104,6 +104,9 @@ int StopExeWait(pExeList self); StopExeWait will stop all running things and wait for the stop to complete. */ + +int StopByData(pExeList self, void *data); + /* stop the entry with the given data from execution */ /*------------------------------------------------------------------------*/ void ClearExecutor(pExeList self); /* @@ -115,7 +118,7 @@ int PauseExecution(pExeList self); int ContinueExecution(pExeList self); -#line 259 "devexec.w" +#line 261 "devexec.w" /*-------------------------- Commands ------------------------------------*/ int DevexecAction(SConnection * pCon, SicsInterp * pSics, void *pData, @@ -157,16 +160,15 @@ int ContinueAction(SConnection * pCon, SicsInterp * pSics, void *pData, /*--------------------------- Locking ---------------------------------*/ -#line 183 "devexec.w" +#line 185 "devexec.w" void LockDeviceExecutor(pExeList self); void UnlockDeviceExecutor(pExeList self); -#line 299 "devexec.w" +#line 301 "devexec.w" /* -------------------------- Executor management -------------------------*/ - pExeList GetExecutor(void); void SetExecutor(pExeList pExe); /*----------------------- Logging -----------------------------------------*/ diff --git a/devexec.tex b/devexec.tex index 6ebd2463..f129290f 100644 --- a/devexec.tex +++ b/devexec.tex @@ -52,7 +52,7 @@ $\langle$devreg {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ char *name, float fNew);@\\ \mbox{}\verb@ int StartCounter(pExeList self, SicsInterp *pSics, SConnection *pCon,@\\ \mbox{}\verb@ char *name); @\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -115,7 +115,7 @@ $\langle$devcheck {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ int DevExecTask(void *pEL);@\\ \mbox{}\verb@ void DevExecSignal(void *pEL, int iSignal, void *pSigData);@\\ \mbox{}\verb@@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -174,6 +174,8 @@ $\langle$devstop {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ StopExeWait will stop all running things and wait for the stop@\\ \mbox{}\verb@ to complete.@\\ \mbox{}\verb@ */@\\ +\mbox{}\verb@ int StopByData(pExeList self, void *data);@\\ +\mbox{}\verb@ /* stop the entry with the given data from execution */@\\ \mbox{}\verb@/*------------------------------------------------------------------------*/@\\ \mbox{}\verb@ void ClearExecutor(pExeList self);@\\ \mbox{}\verb@ /*@\\ @@ -184,7 +186,7 @@ $\langle$devstop {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ int PauseExecution(pExeList self);@\\ \mbox{}\verb@ int ContinueExecution(pExeList self);@\\ \mbox{}\verb@@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -226,7 +228,7 @@ $\langle$devlock {\footnotesize ?}$\rangle\equiv$ \mbox{}\verb@ void LockDeviceExecutor(pExeList self);@\\ \mbox{}\verb@ void UnlockDeviceExecutor(pExeList self);@\\ \mbox{}\verb@@\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-1ex} \footnotesize\addtolength{\baselineskip}{-1ex} @@ -358,7 +360,7 @@ to the global SICS device executor. \mbox{}\verb@/*----------------------- Logging -----------------------------------------*/@\\ \mbox{}\verb@ void DevexecLog(char *op, char *device); @\\ \mbox{}\verb@#endif @\\ -\mbox{}\verb@@$\diamond$ +\mbox{}\verb@@$\Diamond$ \end{list} \vspace{-2ex} \end{minipage}\\[4ex] diff --git a/devexec.w b/devexec.w index 12acf662..ac53a445 100644 --- a/devexec.w +++ b/devexec.w @@ -144,6 +144,8 @@ This is done via the following interface. StopExeWait will stop all running things and wait for the stop to complete. */ + int StopByData(pExeList self, void *data); + /* stop the entry with the given data from execution */ /*------------------------------------------------------------------------*/ void ClearExecutor(pExeList self); /* diff --git a/devser.c b/devser.c index 040b1469..4cbf4bd6 100644 --- a/devser.c +++ b/devser.c @@ -24,14 +24,14 @@ struct DevSer { DevAction *current; int killCurrent; DevAction *actions; /* the action queue */ + DevAction *toKill; /* list of actions to be killed */ SchedHeader *headers; - ErrMsg *errmsg; int steps; int stopTask; }; static char *devPrio[NumberOfPRIO] = { - "null", "slow", "read", "progress", "write", "halt" + "null", "slow", "read", "progress", "write", "halt", "start" }; char *DevPrio2Text(DevPrio prio) @@ -100,6 +100,9 @@ DevAction *DevNextAction(DevSer * devser) header->timeDue = (floor(now / header->interval) + 1) * header->interval; } + } else if (header->prio == StartPRIO && header->actions != NULL) { + /* special case: poll with StartPRIO pending: block all other actions */ + return devser->current; } } if (header->followingAction != NULL) { @@ -123,13 +126,18 @@ int DevQueueTask(void *ds) AsconStatus status; DevAction *action; char *sendData; - char *replyData; - + char *replyData = NULL; + if (devser->steps == 0) return 1; if (devser->stopTask) { return 0; } + + /* deferred deallocation of removed actions */ + DevFreeActionList(devser->toKill); + devser->toKill = NULL; + action = devser->current; if (action == NULL) { action = DevNextAction(devser); @@ -138,19 +146,16 @@ int DevQueueTask(void *ds) while (action != NULL) { status = AsconTask(devser->asyncConn); if (status == AsconFailure) { - devser->errmsg = AsconGetErrList(devser->asyncConn); - } else if (status != AsconReady) { + replyData = AsconGetError(devser->asyncConn); + } else if (status == AsconReady) { + replyData = AsconRead(devser->asyncConn); + } else { 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); + sendData = action->hdl(action->data, replyData, (status == AsconFailure)); if (sendData != NULL) { AsconWrite(devser->asyncConn, sendData, 0); return 1; @@ -184,6 +189,7 @@ DevSer *DevMake(SConnection * con, int argc, char *argv[]) devser->actions = NULL; devser->headers = NULL; devser->stopTask = 0; + devser->toKill = NULL; devser->steps = -1; /* no debugging by default */ TaskRegister(pServ->pTasker, DevQueueTask, NULL, DevKillTask, devser, 0); return devser; @@ -216,6 +222,7 @@ void DevKill(DevSer * devser) AsconKill(devser->asyncConn); } DevFreeActionList(devser->actions); + DevFreeActionList(devser->toKill); h = devser->headers; while (h != NULL) { victim = h; @@ -223,6 +230,11 @@ void DevKill(DevSer * devser) DevFreeActionList(victim->actions); free(victim); } + if (devser->killCurrent) { + if (devser->current->kill != NULL) devser->current->kill(devser->current); + devser->killCurrent = 0; + free(devser->current); + } if (devser->stopTask) { free(devser); } else { @@ -286,9 +298,9 @@ int DevUnschedule(DevSer * devser, void *actionData, cnt++; /* remove from list */ *ptr2Last = action->next; - if (action->kill != NULL) - action->kill(action->data); - free(action); + /* add to kill list */ + action->next = devser->toKill; + devser->toKill = action; } else { ptr2Last = &action->next; } @@ -360,12 +372,13 @@ int DevRemoveAction(DevSer * devser, void *actionData) { SchedHeader *header = NULL; DevAction **ptr2Last = NULL; - DevAction *action = 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. */ + + + /* 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) { @@ -378,7 +391,7 @@ int DevRemoveAction(DevSer * devser, void *actionData) } /* remove from queue */ ptr2Last = &devser->actions; - for (action = devser->actions; action != NULL; action = action->next) { + for (action = devser->actions; action != NULL; action = *ptr2Last) { if (actionData == action->data) { cnt++; /* remove from list */ diff --git a/devser.h b/devser.h index 4bba95db..5675fbe4 100644 --- a/devser.h +++ b/devser.h @@ -10,10 +10,12 @@ 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 + * sent in the last action, or the error message (when commError == 1) + * \param commError 0: ok, 1: there was a communication error * \return the command to be sent or NULL if no command has to be sent */ -typedef char *DevActionHandler(void *actionData, char *lastReply); +typedef char *DevActionHandler(void *actionData, char *lastReply, + int commError); /** \brief Check if an action matches the call data * \param callData the callers data @@ -29,10 +31,12 @@ typedef void DevKillActionData(void *actionData); /** \brief possible priorities. * NullPRIO and NumberOfPRIO must not be used as priority + * if an action with StartPRIO is scheduled, all other activities + * are blocked until the action is unscheduled */ typedef enum { NullPRIO, SlowPRIO, ReadPRIO, ProgressPRIO, WritePRIO, HaltPRIO, - NumberOfPRIO + StartPRIO, NumberOfPRIO } DevPrio; /** \brief Make a new device serializer and async connection. @@ -100,7 +104,7 @@ int DevSchedule(DevSer * devser, void *actionData, /** \brief Unschedule matching actions * \param devser the device serializer - * \param callData the callers data to be as first argument of the match function + * \param actionData the callers data to be used 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 @@ -114,6 +118,7 @@ int DevUnschedule(DevSer * devser, void *actionData, */ int DevRemoveAction(DevSer * devser, void *actionData); + /** \brief Convert integer priority to text * \param prio * \return text @@ -126,5 +131,4 @@ char *DevPrio2Text(DevPrio prio); */ DevPrio DevText2Prio(char *text); - #endif diff --git a/errormsg.c b/errormsg.c index 59c5429b..d023c207 100644 --- a/errormsg.c +++ b/errormsg.c @@ -26,7 +26,7 @@ int ErrEqual(char *str1, char *str2) return 1; } -ErrMsg *ErrPutMsg(ErrMsg * dump, char *fmt, ...) +void ErrPutMsg(ErrList *list, char *fmt, ...) { ErrMsg *m = NULL; ErrMsg **last = NULL; @@ -34,7 +34,8 @@ ErrMsg *ErrPutMsg(ErrMsg * dump, char *fmt, ...) char buf[256]; char *text = NULL; int l; - + static long id = 0; + va_start(ap, fmt); l = vsnprintf(buf, sizeof buf, fmt, ap); va_end(ap); @@ -43,12 +44,13 @@ ErrMsg *ErrPutMsg(ErrMsg * dump, char *fmt, ...) } else { /* assuming we have a C99 conforming snprintf and need a larger buffer */ text = calloc(l, 1); + if (!text) return; va_start(ap, fmt); vsnprintf(text, l, fmt, ap); va_end(ap); } - last = &dump; - for (m = dump; m != NULL; m = m->next) { + last = &list->current; + for (m = list->current; m != NULL; m = m->next) { if (ErrEqual(text, m->text)) { *last = m->next; /* remove found item from list */ break; @@ -61,12 +63,21 @@ ErrMsg *ErrPutMsg(ErrMsg * dump, char *fmt, ...) m = calloc(1, sizeof(*m)); m->text = text; m->cnt = 1; + m->dirty = 0; + id++; + m->id = id; } else { if (text != buf) free(text); m->cnt++; } - m->next = dump; + m->next = list->current; time(&m->last); - return m; + list->current = m; +} + +char *ErrGetLastMsg(ErrList *list) { + if (list == NULL) return ""; + if (list->current == NULL) return ""; + return list->current->text; } diff --git a/errormsg.h b/errormsg.h index 80b24c89..5f792147 100644 --- a/errormsg.h +++ b/errormsg.h @@ -11,10 +11,17 @@ typedef struct ErrMsg { struct ErrMsg *next; char *text; /**< the message text */ - int cnt; /**< count */ + long cnt; /**< count */ time_t last; /**< time of last message */ + long dirty; /**< count since last reset */ + char *itemId; /**< an id for the item where the error occured */ + long id; } ErrMsg; +typedef struct { + ErrMsg *current; +} ErrList; + /** \brief Put a formatted message to the error message list * * The error message list contains only one entry for all messages @@ -23,10 +30,17 @@ typedef struct ErrMsg { * when comparing messages. * The new message is always at the head of the list. * - * \param dump the error message list + * \param list the error message list * \param fmt the format for the message * \return the new error message list head */ -ErrMsg *ErrPutMsg(ErrMsg * dump, char *fmt, ...); + +void ErrPutMsg(ErrList *list, char *fmt, ...); + +/** \brief Get the most recent error message + * \param list the error list + * \return the most recent error message + */ +char *ErrGetLastMsg(ErrList *list); #endif diff --git a/hipadaba.c b/hipadaba.c index d213331c..e9878872 100644 --- a/hipadaba.c +++ b/hipadaba.c @@ -86,6 +86,7 @@ void DeleteCallbackChain(pHdb node) InvokeCallbackChain(node, &killNodeMsg); current = node->callBackChain; + node->callBackChain = NULL; while (current != NULL) { if (current->killFunc != NULL) { current->killFunc(current->userData); diff --git a/macro.c b/macro.c index d71d9852..17e30e40 100644 --- a/macro.c +++ b/macro.c @@ -982,14 +982,6 @@ int TclPublish(SConnection * pCon, SicsInterp * pSics, void *pData, return 0; } - /* check user rights */ - if (!SCMatchRights(pCon, usMugger)) { - sprintf(pBueffel, "ERROR: you are not authorised to use %s", argv[0]); - SCWrite(pCon, pBueffel, eError); - return 0; - } - - /* try convert last parameter to user code */ iUser = decodeSICSPriv(argv[2]); if (iUser < 0) { @@ -1002,9 +994,26 @@ int TclPublish(SConnection * pCon, SicsInterp * pSics, void *pData, /* check if the macro already exists */ pNew = FindCommandData(pSics, argv[1], "Macro"); if (pNew) { /* yes -> overwrite access code */ + if (pNew->iUser == iUser) { + return 1; + } + /* check user rights */ + if (!SCMatchRights(pCon, usMugger)) { + sprintf(pBueffel, "ERROR: you are not authorised to use %s", argv[0]); + SCWrite(pCon, pBueffel, eError); + return 0; + } pNew->iUser = iUser; return 1; } + + /* check user rights */ + if (!SCMatchRights(pCon, usMugger)) { + sprintf(pBueffel, "ERROR: you are not authorised to use %s", argv[0]); + SCWrite(pCon, pBueffel, eError); + return 0; + } + /* do a job ! */ pNew = CreatePublish(argv[1], iUser); if (!pNew) { diff --git a/make_gen b/make_gen index 8e4cf5ed..05ed0dd0 100644 --- a/make_gen +++ b/make_gen @@ -31,7 +31,7 @@ SOBJ = network.o ifile.o conman.o SCinter.o splitter.o passwd.o \ hmdata.o nxscript.o tclintimpl.o sicsdata.o mcstascounter.o \ mcstashm.o initializer.o remob.o tclmotdriv.o protocol.o \ sinfox.o sicslist.o cone.o hipadaba.o sicshipadaba.o statistics.o \ - ascon.o errormsg.o scriptcontext.o logger.o logreader.o logsetup.o \ + ascon.o scriptcontext.o logger.o logreader.o logsetup.o \ savehdb.o statusfile.o sicshdbfactory.o proxy.o devser.o \ moregress.o multicounter.o regresscter.o histregress.o \ sicshdbadapter.o polldriv.o sicspoll.o statemon.o hmslave.o \ @@ -63,7 +63,7 @@ full: purge all SICServer: $(SOBJ) $(MOTOROBJ) $(COUNTEROBJ) \ $(VELOOBJ) $(DIFIL) $(EXTRA) \ $(SUBLIBS) - $(CC) -g -o SICServer \ + $(CC) -g -o SICServer \ $(SOBJ) $(MOTOROBJ) $(COUNTEROBJ) \ $(VELOOBJ) $(DIFOBJ) $(EXTRA) $(LIBS) diff --git a/motor.c b/motor.c index 9aec5433..60cec23c 100644 --- a/motor.c +++ b/motor.c @@ -128,28 +128,25 @@ static int MotorCheckBoundaryImpl(pMotor self, float fVal, float *fNew, { float fHard; float fZero; - char pBueffel[512]; - + float fLim; + assert(self); /* check for fixed */ if (ObVal(self->ParArray, FIX) >= 0) { - sprintf(pBueffel, "Motor %s is Fixed", self->name); - strncpy(pError, pBueffel, iErrLen); + snprintf(pError, iErrLen, "Motor %s is Fixed", self->name); return 0; /* is this an error? */ } /* check against software boundaries */ if (fVal > ObVal(self->ParArray, SUPP)) { - sprintf(pBueffel, "%f violates upper software limit %f on %s", - fVal, ObVal(self->ParArray, SUPP), self->name); - strncpy(pError, pBueffel, iErrLen); + snprintf(pError, iErrLen, "%g violates upper software limit %g on %s", + fVal, ObVal(self->ParArray, SUPP), self->name); return 0; } if (fVal < ObVal(self->ParArray, SLOW)) { - sprintf(pBueffel, "%f violates lower software limit %f on %s", + snprintf(pError, iErrLen, "%g violates lower software limit %g on %s", fVal, ObVal(self->ParArray, SLOW), self->name); - strncpy(pError, pBueffel, iErrLen); return 0; } @@ -163,15 +160,17 @@ static int MotorCheckBoundaryImpl(pMotor self, float fVal, float *fNew, /* check for hardware limits */ if (fHard > self->pDriver->fUpper) { - sprintf(pBueffel, "%f violates upper hardware limit %f on %s", - fVal, self->pDriver->fUpper, self->name); - strncpy(pError, pBueffel, iErrLen); + fLim = self->pDriver->fUpper * ObVal(self->ParArray,SIGN) + fZero; + snprintf(pError, iErrLen, + "%g violates upper hardware limit %g (%g) on %s", + fVal, fLim, self->pDriver->fUpper, self->name); return 0; } if (fHard < self->pDriver->fLower) { - sprintf(pBueffel, "%f violates lower hardware limit %f on %s", - fVal, self->pDriver->fLower, self->name); - strncpy(pError, pBueffel, iErrLen); + fLim = self->pDriver->fLower * ObVal(self->ParArray,SIGN) + fZero; + snprintf(pError, iErrLen, + "%g violates lower hardware limit %g (%g) on %s", + fVal, fLim, self->pDriver->fLower, self->name); return 0; } diff --git a/network.c b/network.c index 4cbe4f6a..ab97d8b6 100644 --- a/network.c +++ b/network.c @@ -51,6 +51,8 @@ #include #include #include +#include "sics.h" +#include "commandlog.h" #include "uselect.h" #define PORT 1 @@ -65,11 +67,6 @@ struct timeval lastclose = { -1, 0 }; /*----------------------------------------------------------------------- Redefine this function if another means of error reporting is necessary. */ -#include "Scommon.h" -extern void SICSLogWrite(char *pText, OutCode eCode); /* servlog.c */ - -void WriteToCommandLog(char *p, char *t); - static void NetError(char *pText) { /* @@ -674,8 +671,44 @@ int NETReadTillTerm(mkChannel * self, long timeout, status = NETAvailable(self, timeout - dif); }; }; - assert(bufPtr > 0); - return bufPtr; + return status; +} + +/*-------------------------------------------------------------------------*/ +int NETReadRemob(mkChannel * self, long timeout, long timeout2, + char term, char *pBuffer, int iBufLen) +{ + struct timeval start, now; + int bufPtr = 0, status, i, length, matchIndex = 1; + char c; + long dif; + + if (!VerifyChannel(self)) { + return -1; + } + + memset(pBuffer, 0, iBufLen); + + status = NETAvailable(self, timeout); + while (status > 0) { + status = recv(self->sockid, &c, 1, 0); + if (status <= 0) { + return status; + } + if (c == term) { + return bufPtr + 1; + } + if (bufPtr >= iBufLen - 1) { + return -1; /* overflow */ + } + pBuffer[bufPtr] = c; + bufPtr++; + /* + wait for more data + */ + status = NETAvailable(self, timeout2); + }; + return status; } /*---------------------------------------------------------------------------*/ @@ -701,7 +734,7 @@ int NETClosePort(mkChannel * self) /*---------------------------------------------------------------------------*/ int NETReconnectWithFlags(mkChannel * self, int flags) { - int iRet; + int iRet = 0; int sock; int oldopts; @@ -709,7 +742,9 @@ int NETReconnectWithFlags(mkChannel * self, int flags) * Get the flags and close the old socket */ oldopts = fcntl(self->sockid, F_GETFL, 0); - close(self->sockid); + if (self->sockid != 0) { + close(self->sockid); + } /* Reopen and try to get it on the olf fd */ sock = socket(AF_INET, SOCK_STREAM, 0); if (self->sockid != sock) { diff --git a/network.h b/network.h index 9d8452b5..925a9568 100644 --- a/network.h +++ b/network.h @@ -63,7 +63,7 @@ mkChannel *NETConnectWithFlags(char *name, int port, int flags); if (flags & 1): do not block on connect (use NETConnectFinished to check success) if (flags & 2): wait 1000 ms after last close (workaround for - a bug in Lantronix terminal server + a bug in the Lantronix terminal server) */ int NETConnectFinished(mkChannel * self); @@ -91,8 +91,8 @@ int NETReconnectWithFlags(mkChannel * self, int flags); /* *********************** DATA TRANSFER ******************************** */ int NETWrite(mkChannel * self, char *buffer, long lLen); - /* writes data to socket self, returns True if success, - false otherwise. + /* writes data to socket self, returns 1 if success, + 0 otherwise. */ long NETRead(mkChannel * self, char *buffer, long lLen, long timeout); @@ -122,6 +122,9 @@ int NETReadTillTerm(mkChannel * self, long timeout, If no terminator is given, the routine waits for iBufLen characters or timeout. */ +int NETReadRemob(mkChannel *self, long timeout, long timeout2, + char term, char *pBuffer, int iBufLen); + /* special version for remob */ /* ********************* KILLING FIELD ******************************** */ int NETClosePort(mkChannel * self); /* closes a port, do not forget to free the channel data- diff --git a/nserver.c b/nserver.c index b9192fba..be412f8b 100644 --- a/nserver.c +++ b/nserver.c @@ -448,7 +448,7 @@ int UserWait(SConnection * pCon, SicsInterp * pSics, void *pData, if (i < 1) { sprintf(pBueffel, "Expected numeric argument to %s, got %s", argv[0], argv[1]); - SCWrite(pCon, pBueffel, eInError); + SCWrite(pCon, pBueffel, eError); return 0; } diff --git a/ofac.c b/ofac.c index c2227d82..7c1791cc 100644 --- a/ofac.c +++ b/ofac.c @@ -236,6 +236,7 @@ static void InitIniCommands(SicsInterp * pInter, pTaskMan pTask) AddCommand(pInter, "SICSStatus", SICSStatus, NULL, NULL); AddCommand(pInter, "sicstime", SICSTime, NULL, NULL); AddCommand(pInter, "sicsdescriptor", SICSDescriptor, NULL, NULL); + AddCommand(pInter, "silent", SICSSilent, NULL, NULL); AddCommand(pInter, "SetStatus", SetSICSStatus, NULL, NULL); AddCommand(pInter, "db", SICSDebug, NULL, NULL); AddCommand(pInter, "EVFactory", EVControlFactory, NULL, NULL); @@ -439,7 +440,9 @@ void InitGeneral(void) INIT(InitializerInit); INIT(SaveHdbInit); /* must be after InitializerInit */ INIT(SctInit); - INIT(SctDriveInit); + INIT(SctDriveAdapterInit); + INIT(SctDriveObjInit); + INIT(SctDriveAdapterInit); INIT(LogReaderInit); INIT(LogSetupInit); INIT(StatusFileInit); diff --git a/script.c b/script.c index f60d9f7f..b3ec102f 100644 --- a/script.c +++ b/script.c @@ -485,7 +485,6 @@ int SicsPrompt(SConnection * pCon, SicsInterp * pSics, void *pData, /*----------------------- get object descriptor name ------------------------------- get the name of the object descriptor */ - int SICSDescriptor(SConnection * pCon, SicsInterp * pSics, void *pData, int argc, char *argv[]) { @@ -517,3 +516,44 @@ int SICSDescriptor(SConnection * pCon, SicsInterp * pSics, void *pData, SCWrite(pCon, "notfound", eValue); return 1; } +/*------------------------------------------------------------------------*/ +int SICSSilent(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]) { + Tcl_Interp *pTcl = NULL; + int iRet; + char *cmd; + char *result; + writeFunc oldWrite; + + assert(pCon); + assert(pSics); + + if (!SCinMacro(pCon)) { + SCPrintf(pCon, eError, + "ERROR: %s may only be called in scripts", argv[0]); + return 0; + } + pTcl = InterpGetTcl(pSics); + assert(pTcl); + if (argc < 3) { + SCWrite(pCon, "ERROR: should be: silent ", eError); + return 0; + } + cmd = Arg2Tcl(argc-2,&argv[2],NULL,0); + assert(cmd); + + oldWrite = SCGetWriteFunc(pCon); + SCSetWriteFunc(pCon,SCNotWrite); + iRet = Tcl_EvalEx(pTcl, cmd, strlen(cmd), 0); + SCSetWriteFunc(pCon,oldWrite); + + if (iRet == TCL_OK) { + result = strdup((char *)Tcl_GetStringResult(pTcl)); + SCWrite(pCon, result, eValue); + free(result); + } else { + SCWrite(pCon, argv[1], eValue); + } + free(cmd); + return 1; +} diff --git a/script.h b/script.h index b2adbec6..82859058 100644 --- a/script.h +++ b/script.h @@ -48,4 +48,7 @@ int SicsPrompt(SConnection * pCon, SicsInterp * pSics, void *pData, /*----------------------------------------------------------------------*/ int SICSDescriptor(SConnection * pCon, SicsInterp * pSics, void *pData, int argc, char *argv[]); +/*----------------------------------------------------------------------*/ +int SICSSilent(SConnection *pCon, SicsInterp *pSics, void *pData, + int argc, char *argv[]); #endif diff --git a/scriptcontext.c b/scriptcontext.c index 4f3e5a9f..6b4d12e4 100644 --- a/scriptcontext.c +++ b/scriptcontext.c @@ -47,7 +47,7 @@ typedef struct SctData { } SctData; static ScriptContext *sct = NULL; -static SConnection *currentCon = NULL; +static SctData *queueData = NULL; static struct { char *name; @@ -179,6 +179,7 @@ int SctCommand(SConnection * con, SicsInterp * sics, void *object, return 0; } UpdateHipadabaPar(node, v, con); + ReleaseHdbValue(&v); SetHdbProperty(node, "geterror", NULL); return 1; } @@ -187,6 +188,9 @@ int SctCommand(SConnection * con, SicsInterp * sics, void *object, * print */ if (strcmp(argv[1], "print") == 0) { + if (queueData != NULL) { + queueData->answered = 1; + } Arg2Text(argc - 2, argv + 2, value, sizeof value); SCWrite(con, value, eLog); return 1; @@ -201,7 +205,7 @@ int SctCommand(SConnection * con, SicsInterp * sics, void *object, } /* - * time stamping + * time stamping (obsolete ? -> see SctActionHandler: timeKey, timeVal) */ if (strcmp(argv[1], "utime") == 0) { if (argc < 3) { @@ -248,7 +252,7 @@ int SctCallInContext(SConnection * con, char *script, Hdb * node, PushContext(node, controller->node); if (verbose) { - SCPrintf(con, eLog, "\nscript: %s\n", script); + SCPrintf(con, eLog, "script: %s", script); } MacroPush(con); @@ -257,7 +261,7 @@ int SctCallInContext(SConnection * con, char *script, Hdb * node, result = (char *) Tcl_GetStringResult(pTcl); if (ret != TCL_OK && result[0] != '\0') { if (verbose) { - SCPrintf(con, eLog, "\nerror: %s\n", result); + SCPrintf(con, eLog, "error: %s", result); } iRet = 0; } @@ -276,26 +280,38 @@ static int SctMatch(void *data1, void *data2) return a->node == b->node && strcasecmp(a->name, b->name) == 0; } -static char *SctActionHandler(void *actionData, char *lastReply) +static char *SctActionHandler(void *actionData, char *lastReply, + int commError) { SctData *data = actionData; Hdb *node = data->node; SctController *controller = data->controller; char *state; char *result; - char *script; + char *script = NULL; + char *errorScript = NULL; char *send = NULL; int i; SConnection *con; + char eprop[80]; + char msg[1024]; + char path[1024]; + char origScript[80]; + char *blank; + char *emsg; + size_t l; + int cnt; char timeKey[50], timeVal[50]; - if (currentCon) { - con = currentCon; + if (queueData != NULL && queueData->conCtx != NULL) { + con = queueData->conCtx; } else { con = controller->conn; } SetProp(node, controller->node, "result", lastReply); - if (controller->verbose && lastReply != NULL && *lastReply != '\0') { + script = NULL; + if (!commError && controller->verbose && lastReply != NULL + && *lastReply != '\0') { SCPrintf(con, eLog, "reply : %s", lastReply); } state = GetProp(node, controller->node, "state"); @@ -304,29 +320,75 @@ static char *SctActionHandler(void *actionData, char *lastReply) SetProp(node, controller->node, "state", state); } for (i = 0; i < 10; i++) { + SetProp(node, controller->node, "sent", + GetProp(node, controller->node, "send")); SetProp(node, controller->node, "send", NULL); script = GetProp(node, controller->node, state); if (script == NULL) script = state; - + snprintf(origScript, sizeof origScript, "%s", script); + if (commError) { + errorScript = GetProp(node, controller->node, "commerror"); + if (errorScript != NULL) + script = errorScript; + commError = 0; + } + script = strdup(script); if (!SctCallInContext(con, script, node, data->controller, &result)) { - SCPrintf(con, eError, "ERROR: %s", result); + snprintf(eprop, sizeof eprop, "error_during_%s", data->name); + if (strncmp(result, "ERROR: ", 7) == 0) { + result += 7; + } + emsg = GetHdbProp(node, eprop); + if (emsg == NULL) { + GetHdbPath(node, path, sizeof path); + SCPrintf(con, eError, + "ERROR: action <%s> in {%s} node %s:\nERROR: %s", + data->name, origScript, path, result); + } + snprintf(msg, sizeof msg, "<%s> %s", origScript, result); + SetHdbProperty(node, eprop, msg); + blank = strchr(origScript, ' '); + if (blank != NULL) { + l = blank - origScript; + } else { + l = strlen(origScript); + } + snprintf(eprop, sizeof eprop, "error_in_%.*s", l, origScript); + emsg = GetHdbProp(node, eprop); + cnt = 0; + if (emsg != NULL) { + sscanf(emsg, "%d", &cnt); + } + cnt++; + snprintf(msg, sizeof msg, "%dx {%s} %s", cnt, origScript, result); + SetHdbProperty(node, eprop, msg); + send = NULL; goto finish; } state = result; - if (strcasecmp(state, "idle") == 0) { - if (currentCon && !data->answered) { - SCWrite(con, "o.k.", eValue); + if (strcasecmp(state, "idle") == 0 || strcasecmp(state, "unpoll") == 0) { + if (queueData == data && !data->answered) { + SCWrite(con, "o.k.", eStatus); } - if (strcmp(data->name, "write") == 0) { - SetHdbProperty(data->node, "writestatus", "commandsent"); + snprintf(eprop, sizeof eprop, "error_during_%s", data->name); + emsg = GetHdbProp(node, eprop); + if (emsg != NULL) { + GetHdbPath(node, path, sizeof path); + SCPrintf(con, eError, + "action <%s>: success after error message (%s)\n %s", + data->name, path, emsg); + SetHdbProperty(node, eprop, NULL); } - snprintf(timeKey, 50, "%s_time", data->name); - snprintf(timeVal, 50, "%.3f", DoubleTime()); + snprintf(timeKey, sizeof timeKey, "%s_time", data->name); + snprintf(timeVal, sizeof timeVal, "%.3f", DoubleTime()); SetHdbProperty(data->node, timeKey, timeVal); + send = NULL; goto finish; } SetProp(node, controller->node, "state", state); + free(script); + script = NULL; send = GetProp(node, controller->node, "send"); if (send != NULL && send[0] != '\0') { if (controller->verbose) { @@ -337,6 +399,16 @@ static char *SctActionHandler(void *actionData, char *lastReply) } SCPrintf(con, eLogError, "ERROR: too many quick scripts chained"); finish: + if (strcmp(data->name, "write") == 0) { + /* check if it would be ok to do this only when writestatus property exists: + GetHdbProp(data->node, "writestatus") != NULL + */ + SetHdbProperty(data->node, "writestatus", "commandsent"); + } + if (strcasecmp(state, "unpoll") == 0) { + DevUnschedule(controller->devser, data, SctActionHandler, SctMatch); + send = NULL; + } SetProp(node, controller->node, "state", "idle"); if (data->conCtx != NULL) { SCDeleteConnection(data->conCtx); @@ -345,16 +417,17 @@ finish: return send; } -static char *SctWriteHandler(void *actionData, char *lastReply) +static char *SctWriteHandler(void *actionData, char *lastReply, + int commError) { SctData *data = actionData; - SConnection *old; + SctData *old; char *result; - old = currentCon; - currentCon = data->conCtx; - result = SctActionHandler(data, lastReply); - currentCon = old; + old = queueData; + queueData = data; + result = SctActionHandler(data, lastReply, commError); + queueData = old; return result; } @@ -379,6 +452,10 @@ static hdbCallbackReturn SctMainCallback(Hdb * node, void *userData, char *geterror; char error[256]; + if (controller->devser == NULL) { + /* defunct controller */ + return hdbContinue; + } pm = GetKillPtrMessage(msg); if (pm != NULL) { if (controller == pm->pPtr) { @@ -440,7 +517,6 @@ static hdbCallbackReturn SctActionCallback(Hdb * node, void *userData, char *writeprio; char *error; SConnection *con; - SConnection *con2; char path[MAX_HDB_PATH]; pm = GetKillPtrMessage(msg); @@ -510,16 +586,16 @@ static hdbCallbackReturn SctActionCallback(Hdb * node, void *userData, mm = GetHdbUpdateMessage(msg); if (mm != NULL) { - if (currentCon) { /* update called from a write action */ + if (queueData != NULL && queueData->conCtx != NULL && !data->answered) { + /* update called from a write action */ data->answered = 1; GetHdbPath(node, path, sizeof path); - con = currentCon; text = formatValue(*(mm->v), node); /* Markus: who is receiving this message? - It gave unwanted output to me. MK - SCPrintf(con, eLog, "%s = %s", path, GetCharArray(text)); + It gave unwanted output to me. MK + Answer: a commandline client -> outcode eStatus correct? */ - + SCPrintf(queueData->conCtx, eStatus, "%s = %s", path, GetCharArray(text)); DeleteDynString(text); } return hdbContinue; @@ -577,7 +653,9 @@ static void SctKillCBData(void *d) { SctData *data = d; - DevRemoveAction(data->controller->devser, data); + if (data->controller->devser != NULL) { + DevRemoveAction(data->controller->devser, data); + } SctKillData(d); } @@ -866,7 +944,8 @@ static void KillSctTransact(void *data) free(self); } -static char *TransactionHandler(void *actionData, char *lastReply) +static char *TransactionHandler(void *actionData, char *lastReply, + int commError) { pSctTransact st = (pSctTransact) actionData; @@ -976,6 +1055,12 @@ static hdbCallbackReturn SctDebugCallback(Hdb * node, void *userData, return hdbContinue; } +static int SctDeferredFree(void *data) +{ + free(data); + return 0; +} + static void SctKillController(void *c) { SctController *controller = c; @@ -987,7 +1072,13 @@ static void SctKillController(void *c) SCDeleteConnection(controller->conn); } DevKill(controller->devser); - free(controller); + controller->devser = NULL; + if (pServ->pTasker) { + TaskRegister(pServ->pTasker, SctDeferredFree, NULL, NULL, controller, + 0); + } else { + free(controller); + } } static int SctMakeController(SConnection * con, SicsInterp * sics, @@ -995,33 +1086,33 @@ static int SctMakeController(SConnection * con, SicsInterp * sics, { SICSOBJ *ccmd; Hdb *parent, *par, *cmd; - char *nodeName; + char *objName; hdbCallback *cb; SctController *controller; if (argc < 2) { SCPrintf(con, eError, - "ERROR: should be %s ...", argv[0]); + "ERROR: should be %s ...", + argv[0]); return 0; } - /* - * Install into the Hipadaba when full path given - */ - if(strstr(argv[1],"/") != NULL){ - parent = FindHdbParent(NULL, argv[1], &nodeName, con); - if (parent == NULL) - return 0; /* error message already written */ + if (strchr(argv[1], '/') == NULL) { + /* object name only -> do not anchor in tree */ + parent = NULL; + objName = argv[1]; } else { - nodeName = argv[1]; - parent = NULL; + /* full path given -> install into the hipadaba */ + parent = FindHdbParent(NULL, argv[1], &objName, con); + if (parent == NULL) + return 0; /* error message already written */ } controller = calloc(1, sizeof(*controller)); assert(controller); controller->verbose = 0; - ccmd = MakeSICSOBJv(nodeName, "SctController", HIPNONE, usSpy); + ccmd = MakeSICSOBJv(objName, "SctController", HIPNONE, usSpy); controller->node = ccmd->objectNode; controller->conn = SCCreateDummyConnection(pServ->pSics); @@ -1030,15 +1121,16 @@ static int SctMakeController(SConnection * con, SicsInterp * sics, ccmd->pPrivate = controller; ccmd->KillPrivate = SctKillController; - if(parent != NULL){ - AddHipadabaChild(parent, controller->node, con); + if (parent != NULL) { + AddHipadabaChild(parent, controller->node, con); } controller->devser = DevMake(con, argc - 2, argv + 2); if (!controller->devser) return 0; - AddCommand(pServ->pSics, nodeName, InvokeSICSOBJ, KillSICSOBJ, ccmd); + AddCommand(pServ->pSics, objName, InterInvokeSICSOBJ, KillSICSOBJ, ccmd); + RegisterSICSOBJKillCmd(ccmd, objName); SetDescriptorKey(ccmd->pDes, "creationCommand", "0"); cmd = AddSICSHdbPar(controller->node, diff --git a/sctdriveadapter.c b/sctdriveadapter.c index cdc60fe3..b8e78fb5 100644 --- a/sctdriveadapter.c +++ b/sctdriveadapter.c @@ -306,8 +306,9 @@ int SctMakeDriveAdapter(SConnection * pCon, SicsInterp * pSics, } if (argc < 4) { - SCWrite(pCon, "ERROR: not enough arguments for SctMakeDriveAdapter", - eError); + SCPrintf(pCon, eError, + "ERROR: should be %s ", + argv[0]); return 0; } @@ -335,12 +336,8 @@ int SctMakeDriveAdapter(SConnection * pCon, SicsInterp * pSics, } /*---------------------------------------------------------------*/ -int SctMakeDriveObject(SConnection * pCon, SicsInterp * pSics, - void *object, int argc, char *argv[]); - -void SctDriveInit(void) +void SctDriveAdapterInit(void) { AddCmd("makesctdrive", SctMakeDriveAdapter); AddCmd("dynsctdrive", SctMakeDriveAdapter); - AddCmd("makesctdriveobj", SctMakeDriveObject); } diff --git a/sctdriveobj.c b/sctdriveobj.c index a9d20e94..9b69bc8e 100644 --- a/sctdriveobj.c +++ b/sctdriveobj.c @@ -3,6 +3,19 @@ * scriptcontext to control the actual driving operation. This can also * serve as a drivable adapter to nodes in other objects. * + * Some cooperation from the node is required: It has to provide + * certain properties the value of which define scripts which + * have to be called at various stages. These are: + * + * checklimits, for limits checking + * checkstatus, for evaluating progress + * halt , for halting things + * + * If no checklimits script is given, nothing is checked. + * If no checkstatus scripts is given, the value of the status property is + * returned. In that case the status value should be updated by some poll script. + * If no halt script is given, the status is set to idle. M.Z. Sept. 08 + * * copyright: see file COPYRIGHT * * Mark Koennecke, June-July 2008 @@ -22,18 +35,19 @@ typedef struct { /*--------------------------------------------------------------*/ static void KillDrivePriv(void *data) { - pDrivObjPriv priv = (pDrivObjPriv)data; - if(priv == NULL){ + pDrivObjPriv priv = (pDrivObjPriv) data; + if (priv == NULL) { return; } - if(priv->pDriv != NULL){ + if (priv->pDriv != NULL) { free(priv->pDriv); } - if(priv->pCon != NULL){ + if (priv->pCon != NULL) { SCDeleteConnection(priv->pCon); } free(priv); } + /*---------------------------------------------------------------*/ static void *SCTDRIVGetInterface(void *data, int iD) { @@ -65,8 +79,8 @@ static int SCTDRIVHalt(void *data) self = (pSICSOBJ) data; pPriv = (pDrivObjPriv) self->pPrivate; if (GetHdbProperty(self->objectNode, "halt", dummy, sizeof dummy)) { - SctQueueNode(pPriv->c, self->objectNode, HaltPRIO, "halt", - pPriv->pCon); + SctQueueNode(pPriv->c, self->objectNode, HaltPRIO, "halt", + pPriv->pCon); } else if (GetHdbProperty(self->objectNode, "status", dummy, sizeof dummy)) { @@ -128,11 +142,13 @@ static long SCTDRIVSetValue(void *data, SConnection * pCon, float val) self = (pSICSOBJ) data; pPriv = (pDrivObjPriv) self->pPrivate; - if(pPriv->pCon != NULL){ + if (pPriv->pCon != NULL) { SCDeleteConnection(pPriv->pCon); } pPriv->pCon = SCCopyConnection(pCon); + StopByData(pServ->pExecutor, data); + v.dataType = HIPFLOAT; v.v.doubleValue = (double) val; SetHdbProperty(self->objectNode, "writestatus", "start"); @@ -258,24 +274,17 @@ static void KillDriveOBJ(void *data) } pPriv = (pDrivObjPriv) self->pPrivate; - if (pPriv->doNotKillNode && self->pDes != NULL) { - if (self->pDes->name) - free(self->pDes->name); - if (self->pDes->pKeys) - IFDeleteOptions(self->pDes->pKeys); - free(self->pDes); - } else { - DeleteDescriptor(self->pDes); /* kill descriptor including node */ + if (pPriv->doNotKillNode && GetDescriptorKey(self->pDes, "creationCommand") == NULL /* it's not a dynamic object */ + && self->pDes != NULL) { + self->objectNode = NULL; /* do not call RemoveHdbNodeFromParent in KillSICSOBJ */ + self->pDes->parNode = NULL; /* do not kill the node in KillSICSOBJ/DeleteDescriptor */ } - if (self->KillPrivate != NULL && self->pPrivate != NULL) { - self->KillPrivate(self->pPrivate); - } - RemoveHdbNodeFromParent(self->objectNode, pServ->dummyCon); - free(self); + KillSICSOBJ(self); } /*-----------------------------------------------------------------------------*/ -pSICSOBJ MakeSctDriveObj(pHdb node, char *class, SctController * c) +pSICSOBJ MakeSctDriveObj(pHdb node, char *class, SctController * c, + int doNotKillNode) { pSICSOBJ pNew = NULL; pDrivObjPriv pPriv = NULL; @@ -299,6 +308,7 @@ pSICSOBJ MakeSctDriveObj(pHdb node, char *class, SctController * c) pNew->objectNode = node; AssignSctDrive(pPriv->pDriv); pPriv->c = c; + pPriv->doNotKillNode = doNotKillNode; pNew->pDes->parNode = pNew->objectNode; pNew->pDes->GetInterface = SCTDRIVGetInterface; pNew->pPrivate = pPriv; @@ -309,7 +319,7 @@ pSICSOBJ MakeSctDriveObj(pHdb node, char *class, SctController * c) /*-------------------------------------------------------------------------- * This actually has two syntaxes: * makesctdrive name path-to-existing-node class SctController - * makesctdrive name type priv class SctController + * makesctdrive name float priv class SctController *--------------------------------------------------------------------------*/ int SctMakeDriveObject(SConnection * pCon, SicsInterp * pSics, void *object, int argc, char *argv[]) @@ -319,54 +329,58 @@ int SctMakeDriveObject(SConnection * pCon, SicsInterp * pSics, pSICSOBJ pNew = NULL; pSICSOBJ pSct = NULL; SctController *c = NULL; - int priv, type, status; + int priv, type, status, doNotKillNode; hdbValue val; + char *sctName; + char *class; - if (argc < 5) { - SCWrite(pCon, "ERROR: not enough arguments to SctMakeDriveObject", - eError); - return 0; - } + if (argc < 5) + goto Usage; - node = FindHdbNode(NULL, argv[2], pCon); - if (node != NULL) { - pSct = FindCommandData(pSics, argv[4], "SctController"); - if (pSct == NULL) { - SCPrintf(pCon, eError, "ERROR: SctController %s not found", argv[4]); - return 0; + if (argc == 5) { + node = FindHdbNode(NULL, argv[2], pCon); + if (node == NULL) { + SCPrintf(pCon, eError, "ERROR: node %s not found", argv[2]); + goto Usage; } - c = (SctController *) pSct->pPrivate; - pNew = MakeSctDriveObj(node, argv[3], c); - pPriv = (pDrivObjPriv) pNew->pPrivate; - pPriv->doNotKillNode = 1; /* the node is owned by someone else */ + sctName = argv[4]; + class = argv[3]; + doNotKillNode = 1; /* the node is owned by someone else */ } else { + /* convert datatype */ + type = convertHdbType(argv[2]); /* convert privilege */ priv = decodeSICSPriv(argv[3]); - /* convert datatype */ - strtolower(argv[4]); - type = convertHdbType(argv[2]); - if (type == HIPNONE) { - node = MakeHipadabaNode(argv[1], HIPNONE, 1); - } else { - val = makeHdbValue(type, 0); - node = MakeSICSHdbPar(argv[1], priv, val); - ReleaseHdbValue(&val); - } + if (priv < 0 || type != HIPFLOAT) + goto Usage; + node = MakeSICSHdbPar(argv[1], priv, MakeHdbFloat(0.0)); if (node == NULL) { SCWrite(pCon, "ERROR: node creation failed", eError); return 0; } - c = FindCommandData(pSics, argv[5], "SctController"); - if (c == NULL) { - SCPrintf(pCon, eError, "ERROR: SctController %s not found", argv[4]); - return 0; - } - pNew = MakeSctDriveObj(node, argv[3], c); + sctName = argv[5]; + class = argv[4]; + doNotKillNode = 0; /* kill the node with the command */ } + + pSct = FindCommandData(pSics, sctName, "SctController"); + if (pSct == NULL) { + SCPrintf(pCon, eError, "ERROR: SctController %s not found", sctName); + goto Usage; + } + c = (SctController *) pSct->pPrivate; + pNew = MakeSctDriveObj(node, class, c, doNotKillNode); + SetHdbProperty(node, "objectName", argv[1]); + if (pNew == NULL) { SCWrite(pCon, "ERROR: failed to create drive object", eError); return 0; } + if (strcasecmp(argv[0], "dynsctdriveobj") == 0) { + /* make object dynamic by defining a creation command */ + SetDescriptorKey(pNew->pDes, "creationCommand", "0"); + RegisterSICSOBJKillCmd(pNew, argv[1]); + } status = AddCommand(pSics, argv[1], InterInvokeSICSOBJ, KillDriveOBJ, pNew); if (status != 1) { @@ -376,4 +390,18 @@ int SctMakeDriveObject(SConnection * pCon, SicsInterp * pSics, return 0; } return 1; +Usage: + SCPrintf(pCon, eError, + "ERROR: should be %s ", + argv[0]); + SCPrintf(pCon, eError, + "ERROR: or %s ", + argv[0]); + return 0; +} +/*---------------------------------------------------------------*/ +void SctDriveObjInit(void) +{ + AddCmd("makesctdriveobj", SctMakeDriveObject); + AddCmd("dynsctdriveobj", SctMakeDriveObject); } diff --git a/sicshipadaba.c b/sicshipadaba.c index 82d3cdcb..2c1f84d6 100644 --- a/sicshipadaba.c +++ b/sicshipadaba.c @@ -1346,6 +1346,7 @@ static void SICSDeleteNodeData(pHdb node) } removeNodeFromUpdateList(node); + DeleteCallbackChain(node); while (node->child != NULL) { tmp = node->child; node->child = node->child->next; @@ -1354,7 +1355,6 @@ static void SICSDeleteNodeData(pHdb node) if (node->properties != NULL) { DeleteStringDict(node->properties); } - DeleteCallbackChain(node); if (node->name != NULL) { free(node->name); @@ -1616,7 +1616,7 @@ static int RemoveParNodeCallback(char *name, pDummy object, m.type = killPtr; m.pPtr = internalID; - if (object->pDescriptor->parNode) { + if (object && object->pDescriptor->parNode) { RecurseCallbackChains(object->pDescriptor->parNode, (pHdbMessage) & m); } return 1; @@ -1805,7 +1805,8 @@ int ProcessSICSHdbPar(pHdb root, SConnection * pCon, SCWrite(pCon, "ERROR: out of memory processing parameter", eError); return 0; } - for (i = 1; i < argc; i++) { + DynStringConcat(parData, argv[1]); + for (i = 2; i < argc; i++) { DynStringConcat(parData, " "); DynStringConcat(parData, argv[i]); } @@ -2048,7 +2049,7 @@ int readHdbValue(hdbValue * v, char *data, char *error, int errlen) getNextHdbNumber(data, number); status = sscanf(number, "%d", &v->v.intValue); if (status != 1) { - snprintf(error, errlen, "Failed to convert %s to integer", data); + snprintf(error, errlen, "Failed to convert [%.32s] to integer", data); return 0; } break; @@ -2056,7 +2057,7 @@ int readHdbValue(hdbValue * v, char *data, char *error, int errlen) getNextHdbNumber(data, number); status = sscanf(number, "%lf", &v->v.doubleValue); if (status != 1) { - snprintf(error, errlen, "Failed to convert %s to double", data); + snprintf(error, errlen, "Failed to convert [%.32s] to double", data); return 0; } break; @@ -2082,7 +2083,7 @@ int readHdbValue(hdbValue * v, char *data, char *error, int errlen) } status = sscanf(number, "%d", &lValue); if (status != 1) { - snprintf(error, errlen, "Failed to convert %s to integer", data); + snprintf(error, errlen, "Failed to convert [%.32s] to integer", data); return 0; } v->v.intArray[i] = lValue; @@ -2104,7 +2105,7 @@ int readHdbValue(hdbValue * v, char *data, char *error, int errlen) } status = sscanf(number, "%lf", &dValue); if (status != 1) { - snprintf(error, errlen, "Failed to convert %s to double", data); + snprintf(error, errlen, "Failed to convert [%.32s] to double", data); return 0; } v->v.floatArray[i] = dValue; @@ -2154,7 +2155,7 @@ int convertHdbType(char *text) type = 0; while (hdbTypes[type] != NULL) { - if (strcmp(hdbTypes[type], text) == 0) { + if (strcasecmp(hdbTypes[type], text) == 0) { break; } type++; @@ -2196,7 +2197,6 @@ static int MakeHdbNode(SConnection * pCon, SicsInterp * pSics, void *pData, /* * convert datatype */ - strtolower(argv[3]); type = convertHdbType(argv[3]); if (type > HIPFLOATVARAR) { SCWrite(pCon, @@ -2210,7 +2210,7 @@ static int MakeHdbNode(SConnection * pCon, SicsInterp * pSics, void *pData, eError); return 0; } else { - length = atoi(argv[3]); + length = atoi(argv[4]); } } @@ -2439,7 +2439,8 @@ static int UpdateHdbNode(SConnection * pCon, SicsInterp * pSics, SCWrite(pCon, "ERROR: out of memory reading parameter", eError); return 0; } - for (i = 2; i < argc; i++) { + DynStringConcat(parData, argv[2]); + for (i = 3; i < argc; i++) { DynStringConcat(parData, " "); DynStringConcat(parData, argv[i]); } @@ -3310,7 +3311,7 @@ static int GetSICSHdbProperty(SConnection * pCon, SicsInterp * pSics, } status = GetHdbProperty(targetNode, argv[2], buffer, 511); if (status != 1) { - SCPrintf(pCon, eError, "ERROR: property %s not found", argv[2]); + SCPrintf(pCon, eError, "ERROR: %s has no property %s", argv[1], argv[2]); return 0; } SCPrintf(pCon, eValue, "%s.%s = %s", argv[1], argv[2], buffer); @@ -3331,12 +3332,12 @@ static int GetSICSHdbPropertyVal(SConnection * pCon, SicsInterp * pSics, } targetNode = FindHdbNode(NULL, argv[1], pCon); if (targetNode == NULL) { - SCWrite(pCon, "ERROR: node not found", eValue); + SCWrite(pCon, "ERROR: node not found", eError); return 0; } status = GetHdbProperty(targetNode, argv[2], buffer, 511); if (status != 1) { - SCWrite(pCon, "ERROR: attribute not found", eValue); + SCPrintf(pCon, eError, "ERROR: %s has no property %s", argv[1], argv[2]); return 0; } SCPrintf(pCon, eValue, "%s", buffer); diff --git a/sicshipadaba.h b/sicshipadaba.h index e948b3d3..26cb089f 100644 --- a/sicshipadaba.h +++ b/sicshipadaba.h @@ -284,6 +284,10 @@ int GetHdbPath(pHdb nodeArg, char *path, size_t pathlen); * @param internalID the internalID to be looked for */ void RemoveSICSInternalCallback(void *internalID); +/** Remove all Callbacks rooted in the node + * @param internalID the internalID to be looked for + */ +void RemoveSICSInternalCallbackFrom(pHdb node, void *internalID); /** * SICSHdbGetPar returns the value of a parameter. * @param obj The object for which to get a parameter. diff --git a/sicsobj.c b/sicsobj.c index cdaa9164..6878202f 100644 --- a/sicsobj.c +++ b/sicsobj.c @@ -17,7 +17,11 @@ #include "initializer.h" #include "splitter.h" -extern int decodeSICSPriv(char *txt); /* from access.c */ +typedef struct { + char *name; + char *class; +} NameClass; + /*--------------------------------------------------------------------------*/ void DefaultKill(void *data) { @@ -74,6 +78,42 @@ int SaveSICSOBJ(void *data, char *name, FILE * fd) return 1; } +/*---------------------------------------------------------------------------*/ +static void KillSICSOBJfromNode(void *userData) +{ + NameClass *nameClass = userData; + SICSOBJ *sicsobj; + + sicsobj = FindCommandData(pServ->pSics, nameClass->name, nameClass->class); + if (sicsobj) { + sicsobj->objectNode = NULL; /* do not call RemoveHdbNodeFromParent in KillSICSOBJ */ + sicsobj->pDes->parNode = NULL; /* do not free the node twice in KillSICSOBJ/DeleteDescriptor */ + RemoveCommand(pServ->pSics, nameClass->name); + } + free(nameClass->name); + free(nameClass->class); + free(nameClass); +} +/*---------------------------------------------------------------------------*/ +static hdbCallbackReturn DummyCallback(Hdb *node, void *userData, + hdbMessage *msg) +{ + return hdbContinue; +} +/*---------------------------------------------------------------------------*/ +void RegisterSICSOBJKillCmd(pSICSOBJ sicsobj, char *name) +{ + hdbCallback *cb; + NameClass *nameClass; + + nameClass = calloc(1, sizeof(*nameClass)); + nameClass->name = strdup(name); + nameClass->class = strdup(sicsobj->pDes->name); + /* use only the kill function */ + cb = MakeHipadabaCallback(DummyCallback, nameClass, KillSICSOBJfromNode); + assert(cb); + AppendHipadabaCallback(sicsobj->objectNode, cb); +} /*---------------------------------------------------------------------------*/ pSICSOBJ MakeSICSOBJv(char *name, char *class, int type, int priv) { @@ -119,8 +159,11 @@ void KillSICSOBJ(void *data) if (self->KillPrivate != NULL && self->pPrivate != NULL) { self->KillPrivate(self->pPrivate); } - RemoveHdbNodeFromParent(self->objectNode, pServ->dummyCon); + if (self->objectNode != NULL) { + RemoveHdbNodeFromParent(self->objectNode, pServ->dummyCon); + } if (self->pDes != NULL) { + self->objectNode = NULL; /* inhibit SICSOBJcallback from deleting twice */ DeleteDescriptor(self->pDes); /* kill descriptor including node */ } free(self); @@ -412,11 +455,11 @@ int InterInvokeSICSOBJ(SConnection * pCon, SicsInterp * pSics, void *pData, if (status == -1) { status = 0; if (argc > 1) { - snprintf(buffer, 131, - "ERROR: no command or parameter found for key: %s", - argv[1]); + snprintf(buffer, sizeof buffer, + "ERROR: %s %s not found", + argv[0], argv[1]); } else { - snprintf(buffer, 131, "ERROR: no argument found"); + snprintf(buffer, sizeof buffer, "ERROR: no argument found"); } SCWrite(pCon, buffer, eError); status = 0; @@ -444,7 +487,6 @@ pSICSOBJ SetupSICSOBJ(SConnection * pCon, SicsInterp * pSics, void *pData, /* convert privilege */ priv = decodeSICSPriv(argv[3]); /* convert datatype */ - strtolower(argv[4]); type = convertHdbType(argv[4]); if (type > HIPFLOAT) { SCWrite(pCon, @@ -464,6 +506,7 @@ pSICSOBJ SetupSICSOBJ(SConnection * pCon, SicsInterp * pSics, void *pData, SetDescriptorKey(pNew->pDes, "creationCommand", "0"); } + SetHdbProperty(pNew->objectNode, "objectName", argv[1]); status = AddCommand(pSics, argv[1], InterInvokeSICSOBJ, KillSICSOBJ, pNew); if (status != 1) { diff --git a/sicsobj.h b/sicsobj.h index 5af02cab..11444e0e 100644 --- a/sicsobj.h +++ b/sicsobj.h @@ -31,6 +31,9 @@ void KillSICSOBJ(void *data); void DefaultKill(void *data); void DefaultFree(void *data); +/* register a callback for killing the command when the node is killed */ +void RegisterSICSOBJKillCmd(pSICSOBJ sicsobj, char *name); + int SaveSICSOBJ(void *data, char *name, FILE * fd); /** diff --git a/stringdict.c b/stringdict.c index 36892844..c5b3d870 100644 --- a/stringdict.c +++ b/stringdict.c @@ -259,6 +259,12 @@ const char *StringDictGetNext(pStringDict self, char *pValue, int iValLen) } else { LLDnodeDataTo(self->iList, &sVal); strncpy(pValue, sVal.value, iValLen); + /* strncpy is not guaranteed to be '\0' terminated */ + if (iValLen > 0 && pValue[iValLen-1] != '\0') { + /* overflow */ + pValue[iValLen-1] = '\0'; + } + return sVal.name; } } else { /* start a new one */ @@ -270,6 +276,11 @@ const char *StringDictGetNext(pStringDict self, char *pValue, int iValLen) self->iTraverse = 1; LLDnodeDataTo(self->iList, &sVal); strncpy(pValue, sVal.value, iValLen); + /* strncpy is not guaranteed to be '\0' terminated */ + if (iValLen > 0 && pValue[iValLen-1] != '\0') { + /* overflow */ + pValue[iValLen-1] = '\0'; + } return sVal.name; } } diff --git a/task.c b/task.c index 3239c76e..a2cd4da1 100644 --- a/task.c +++ b/task.c @@ -344,3 +344,30 @@ int TaskContinue(pTaskMan self) self->iStop = 0; return 1; } + +/*--------------------------------------------------------------------------*/ +void TaskRemove(pTaskMan self, TaskFunc pTaskRun, void *pData) +{ + int iRet; + pTaskHead pCurrent, pNext; + + assert(self); + assert(self->iID == TASKERID); + + pNext = self->pHead->pNext; /* skip dummy task */ + while (pNext != NULL) { + pCurrent = pNext; + pNext = pCurrent->pNext; + if (pCurrent->pRun == pTaskRun && pCurrent->pData == pData) { + /* unlink */ + if (pCurrent->pPrevious != NULL) { + pCurrent->pPrevious->pNext = pCurrent->pNext; + } + if (pCurrent->pNext != NULL) { + pCurrent->pNext->pPrevious = pCurrent->pPrevious; + } + free(pCurrent); + } + } + return; +} diff --git a/task.h b/task.h index e6db8a7d..93e1f92d 100644 --- a/task.h +++ b/task.h @@ -113,5 +113,9 @@ int TaskSignal(pTaskMan self, int iSignal, void *pSigData); pSigData. */ /*-------------------------------------------------------------------------*/ - + void TaskRemove(pTaskMan self, TaskFunc pTaskRun, void *pData); + /* + remove the task with the given task function and the given data pointer + */ + #endif