/*----------------------------------------------------------------------- remob.c implements remote driveable objects living on an other sics server M. Zolliker July 04 ------------------------------------------------------------------------*/ #include #include #include #include #include #include "fortify.h" #include "sics.h" #include "devexec.h" #include "remob.h" #include "splitter.h" #include "status.h" #include "servlog.h" #include "site.h" /*-------------------------------------------------------------------------*/ #define INTERRUPTMODE 0 #define ACCESSCODE 1 /*------------------------------------------------------------------------ */ typedef struct Remob Remob; typedef struct RemServer { pObjectDescriptor desc; char *name; char *host; int port; int actChan; /* 0 or 1 */ mkChannel *chans[2]; /* 0: Spy, 1: User */ int incomplete; char line[256]; Remob *objList; int matchMap; int timeout; int taskActive; SCStore conn; } RemServer; struct Remob { pObjectDescriptor desc; char *name; pIDrivable pDrivInt; pICallBack pCall; RemServer *server; int status; Remob *next; int markForDel; }; typedef struct { float fVal; char *pName; } RemobCallback; typedef struct { char *pName; SConnection *pCon; } RemobInfo; /*-------------------------------------------------------------------------*/ static char *StartsWith(char *line, char *name) { /* if line does not start with name, return NULL else return a pointer to the next non-white space character */ char *str; int l; l = strlen(name); if (0 != strncmp(line, name, l)) return NULL; str = line + l; while (*str == ' ') { str++; } return str; } /*-------------------------------------------------------------------------*/ static int RemWrite(RemServer *remserver, char *line) { int iret; mkChannel *chan; chan = remserver->chans[remserver->actChan]; if (chan) { /* printf("> %s\n", line); */ iret = NETWrite(chan, line, strlen(line)); if (iret == 0) iret = -1; return iret; } else { return -1; } } /*-------------------------------------------------------------------------*/ static int RemRead(RemServer *remserver, long tmo) { int iRet; mkChannel *chan; chan = remserver->chans[remserver->actChan]; if (chan == NULL) return 0; /* no data */ iRet = NETReadTillTermNew(chan, tmo, "\n", remserver->line+remserver->incomplete, sizeof(remserver->line)-remserver->incomplete); if (iRet == 0) { remserver->incomplete = strlen(remserver->line); /* number of chars already received */ return 0; /* timeout */ } else { remserver->incomplete=0; } return iRet; } /*-------------------------------------------------------------------------*/ static int RemHandle(RemServer *remserver) { char *line, *par, *str; Remob *remob; /* skip whitespace at the beginning */ line=remserver->line; while (*line < ' ' && *line != '\0') { line++; } memmove(remserver->line, line, strlen(line)); /* handle drivstat messages */ line = remserver->line; for (remob = remserver->objList; remob != NULL; remob = remob->next) { par=StartsWith(line, remob->name); if (par != NULL) { if ((str = StartsWith(par, "finished"))) { if (*str == '\0') { remob->status = HWIdle; } else { remob->status = HWFault; } line[0]='\0'; return 1; } if ((str = StartsWith(par, "started"))) { remob->status = HWBusy; line[0]='\0'; return 1; } } } return 0; } /*-------------------------------------------------------------------------*/ static void RemCopy(RemServer *remserver, SConnection *pCon) { if (pCon != NULL && remserver->line[0] != '\0') { SCPrintf(pCon, eStatus, " %s", remserver->line); } } /*-------------------------------------------------------------------------*/ static void RemDisconnect(RemServer *remserver) { int i; for (i=0; i<2; i++) { if (remserver->chans[i] != NULL) { NETClosePort(remserver->chans[i]); free(remserver->chans[i]); remserver->chans[i]=NULL; /* printf("disconnected\n"); */ } } } /*-------------------------------------------------------------------------*/ static void RemConnect(RemServer *remserver) { int iRet; char buf[256]; mkChannel *chan; chan = remserver->chans[remserver->actChan]; if (!chan) { remserver->timeout = 0; chan = NETConnect(remserver->host, remserver->port); if (!chan) { return; } remserver->chans[remserver->actChan] = chan; if (remserver->actChan == 0) { iRet = RemWrite(remserver, "Spy 007\ntransact listexe interest\n"); } else { iRet = RemWrite(remserver, "remuser sesam\ntransact listexe interest\n"); } if (iRet < 0) goto close; iRet = RemRead(remserver, 1000); while (iRet > 0) { /* eat login response */ if (StartsWith(remserver->line, "TRANSACTIONFINISHED")) { /* printf("connected\n"); */ return; } iRet = RemRead(remserver, 1000); } goto close; } return; close: RemDisconnect(remserver); return; } /*-------------------------------------------------------------------------*/ static int RemServerTask(void *data) { RemServer *remserver=data; int iRet; SConnection *pCon; if (!remserver->taskActive) return 0; /* remove task */ if (RemRead(remserver, 0) <= 0) return 1; /* continue */ /* printf("< %s\n", buf); */ if (RemHandle(remserver)) { /* handle drivstat messages */ return 1; } /* forward oll other messages */ pCon = SCLoad(&remserver->conn); if (pCon) { RemCopy(remserver, pCon); } return 1; } /*-------------------------------------------------------------------------*/ static int RemTransact(RemServer *remserver, SConnection *pCon, char *cmd, ...) { /* the variable arguments are for filtering: "" write untreated lines to pCon */ char buf[256]; int iRet; int i, typ; char *arg, *val, *endp; float *f; va_list ap; int try; int argMask; try=2; if (remserver->timeout) { /* eat old responses */ while (RemRead(remserver, 0) > 0) { RemHandle(remserver); } } tryagain: strcpy(buf, "transact "); strcat(buf, cmd); strcat(buf,"\n"); RemConnect(remserver); iRet = RemWrite(remserver, buf); if (iRet < 0) goto close; iRet = RemRead(remserver, 2000); if (iRet <= 0) goto close; while (!StartsWith(remserver->line, "TRANSACTIONFINISHED")) { RemHandle(remserver); va_start(ap, cmd); arg = va_arg(ap, char *); argMask=1; remserver->matchMap = 0; while (arg != NULL) { if (*arg == '>') { RemCopy(remserver, pCon); } else if (*arg == '<') { f = va_arg(ap, float *); val = StartsWith(remserver->line, arg+1); if (val != NULL) { val = StartsWith(val, "="); if (val != NULL) { *f = strtod(val, &endp); break; } } } else if (*arg == '!') { if (StartsWith(remserver->line, arg+1)) { remserver->matchMap |= argMask; argMask = argMask*2; break; } } else { printf("unknown argument in RemTransact: %s\n", arg); assert(0); } arg = va_arg(ap, char *); } va_end(ap); iRet = RemRead(remserver, 2000); if (iRet <= 0) goto close; } return 1; close: if (iRet == 0) { snprintf(buf, sizeof(buf), "ERROR: timeout on %s", remserver->name); SCWrite(pCon,buf,eError); remserver->timeout = 1; return iRet; } RemDisconnect(remserver); try--; if (try>0) goto tryagain; snprintf(buf, sizeof(buf), "ERROR: no connection to %s", remserver->name); SCWrite(pCon,buf,eError); return iRet; } /*-------------------------------------------------------------------------*/ static void *RemobGetInterface(void *pData, int iID) { Remob *self = pData; assert(self); if(iID == DRIVEID) { return self->pDrivInt; } else if(iID == CALLBACKINTERFACE) { return self->pCall; } return NULL; } /*------------------------------------------------------------------------*/ static int RemobHalt(void *self) { Remob *remob=self; RemServer *remserver; char buf[64]; assert(remob); remserver = remob->server; RemConnect(remserver); snprintf(buf, sizeof(buf), "stopexe %s\n", remob->name); return RemWrite(remserver, buf); } /*--------------------------------------------------------------------------*/ static int RemobLimits(void *self, float fVal, char *error, int iErrLen) { float fHard; Remob *remob=self; assert(remob); /* check is done one remote server */ return 1; } /*---------------------------------------------------------------------------*/ static float RemobGetValue(void *pData, SConnection *pCon) { Remob *remob=pData; char buf[80]; float none, value; int iRet; assert(remob); SCSave(&remob->server->conn, pCon); none = -1.25e6; value= none; snprintf(buf, sizeof(buf), "<%s", remob->name); iRet = RemTransact(remob->server, pCon, remob->name, buf, &value, ">", NULL); if (iRet <= 0) { return 0.0; } if (value != none) { return value; } snprintf(buf, sizeof(buf), "can not get %s", remob->name); SCWrite(pCon, buf, eWarning); return 0.0; } /*------------------------------------------------------------------------*/ static int RemobSaveStatus(void *pData, char *name, FILE *fil) { Remob *self = pData; assert(self); assert(fil); /* data is stored on remote server */ return 1; } /*------------------------------------------------------------------------*/ static int RemServerSaveStatus(void *pData, char *name, FILE *fil) { RemServer *remserver = pData; Remob *remob; assert(remserver); assert(fil); for (remob = remserver->objList; remob != NULL; remob = remob->next) { fprintf(fil, "catch { remob new %s %s }\n", remob->name, remserver->name); } return 1; } /*------------------------------------------------------------------------*/ static int RemobStatus(void *pData, SConnection *pCon) { Remob *remob=pData; assert(remob); SCSave(&remob->server->conn, pCon); return remob->status; } /*---------------------------------------------------------------------------*/ static long RemobRun(void *self, SConnection *pCon, float fNew) { Remob *remob=self; float fHard; int i, iRet, iCode; char buf[512], sBuf[64]; char pError[132]; Remob *p; RemServer *remserver; long lTime; float fDelta; remserver = remob->server; SCSave(&remserver->conn, pCon); assert(remob); assert(pCon); remob->status = HWIdle; snprintf(buf, sizeof(buf), "run %s %f", remob->name, fNew); iRet = RemTransact(remserver, pCon, buf, "!ERROR: somebody else", "!ERROR: cannot", ">", NULL); if (iRet <= 0) return 0; if (remserver->matchMap & 1) { /* already running, stop first */ remob->status = HWBusy; snprintf(sBuf, sizeof(sBuf), "stopexe %s", remob->name); iRet = RemTransact(remserver, pCon, sBuf, ">", NULL); if (iRet <= 0) return 0; while (remob->status == HWBusy) { iRet = RemRead(remserver, 1000); if (iRet <= 0) break; RemCopy(remserver, pCon); } iRet = RemTransact(remserver, pCon, buf, ">", NULL); if (iRet <= 0) return 0; } if (remob->status != HWBusy) { return 0; } return OKOK; } /*-----------------------------------------------------------------------*/ static void KillInfo(void *pData) { RemobInfo *self = pData; assert(self); if (self->pName) { free(self->pName); } free(self); } /*------------------- The CallBack function for interest ------------------*/ static int InterestCallback(int iEvent, void *pEvent, void *pUser) { RemobCallback *psCall = pEvent; RemobInfo *pInfo = pUser; char buf[80]; assert(psCall); assert(pInfo); snprintf(buf, sizeof(buf),"%s.position = %f ", pInfo->pName, psCall->fVal); SCWrite(pInfo->pCon,buf,eValue); return 1; } /*---------------------------------------------------------------------------*/ int RemobAction(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) { Remob *remob = pData; char buf[512]; TokenList *pList = NULL; TokenList *pCurrent; TokenList *pName; int iRet; int i; int pos; float fValue; long lID; char *endp; char acce[128], inte[128]; assert(pCon); assert(pSics); assert(remob); if (SCGetRights(pCon) > usUser) { remob->server->actChan = 0; } else { remob->server->actChan = 1; } SCSave(&remob->server->conn, pCon); if (argc == 1) { iRet = RemTransact(remob->server, pCon, argv[0], ">", NULL); } else if (strcasecmp(argv[1],"list") == 0) { snprintf(buf, sizeof(buf), "%s ", remob->name); i = strlen(buf); Arg2Text(argc-1, argv+1, buf + i, sizeof buf - i); /* snprintf(acce, sizeof(acce), "!%s.accesscode", remob->name); snprintf(inte, sizeof(inte), "!%s.interruptmode", remob->name); */ RemTransact(remob->server, pCon, buf, ">", NULL); iRet=1; } else { pos=snprintf(buf, sizeof(buf), "%s ", remob->name); for (i=1; iserver, pCon, buf, ">", NULL); } return iRet; } /*--------------------------------------------------------------------------*/ static void RemobKill(void *self) { Remob *remob = self; Remob *p, **last; assert(remob); /* remove from object list */ if (remob->server) { last = &remob->server->objList; p = *last; while (p != remob && p !=NULL) { last = &p->next; p = p->next; } if (p != NULL) { *last = p->next; } remob->next = NULL; } if (remob->name) { free(remob->name); } if (remob->pDrivInt) { free(remob->pDrivInt); } if (remob->pCall) { DeleteCallBackInterface(remob->pCall); } /* kill Descriptor */ if (remob->desc) { DeleteDescriptor(remob->desc); } free(remob); } /*---------------------------------------------------------------------------*/ int RemServerAction(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) { RemServer *remserver = pData; char buf[512]; TokenList *pList = NULL; TokenList *pCurrent; TokenList *pName; int iRet; int i; int pos; float fValue; long lID; char *endp, *serverport, *thishostname; char acce[128], inte[128]; struct sockaddr_in adr; struct hostent *thishost; mkChannel *chan; Remob *p, *next; assert(pCon); assert(pSics); assert(remserver); if (SCGetRights(pCon) > usUser) { remserver->actChan = 0; } else { remserver->actChan = 1; } chan = remserver->chans[remserver->actChan]; if (argc == 1) { serverport = IFindOption(pSICSOptions,"ServerPort"); i = sizeof adr; thishostname = NULL; if (chan) { if (getsockname(chan->sockid, (void *)&adr, &i) >= 0) { thishost = gethostbyaddr((char *)&adr.sin_addr, sizeof adr.sin_addr, AF_INET); if (thishost) { thishostname = thishost->h_name; } } } if (thishostname == NULL) thishostname = "undef"; SCPrintf(pCon, eStatus, "%s = %s:%d %s:%s", argv[0], remserver->host, remserver->port, thishostname, serverport); } else if (argc>2 && strcasecmp(argv[1],"nowait") == 0) { RemConnect(remserver); for (i=2; i2) { RemWrite(remserver, " "); } RemWrite(remserver, argv[i]); } RemWrite(remserver, "\n"); } else if (argc==2 && strcasecmp(argv[1],"markForDel") == 0) { p = remserver->objList; while (p) { p->markForDel = 1; p = p->next; } } else if (argc==2 && strcasecmp(argv[1],"delMarked") == 0) { p = remserver->objList; while (p) { next = p->next; if (p->markForDel) { if (p->pDrivInt && pServ->pExecutor && isInRunMode(pServ->pExecutor)) { SCWrite(pCon,"ERROR: cannot delete while running",eError); return 0; } RemoveCommand(pSics, p->name); } p = next; } } else { pos=0; for (i=1; i", NULL); return iRet; } return 1; } /*--------------------------------------------------------------------------*/ static void RemServerKill(void *self) { RemServer *remserver = self; Remob *remob, *next; assert(remserver); if (remserver->taskActive) { remserver->taskActive=0; /* let the tasker kill me */ return; } remob = remserver->objList; while (remob) { next = remob->next; RemoveCommand(pServ->pSics, remob->name); remob = next; } RemDisconnect(remserver); DeleteDescriptor(remserver->desc); if (remserver->name) free(remserver->name); if (remserver->host) free(remserver->host); free(remserver); } /*-----------------------------------------------------------------------*/ static int RemobSetDriveable(Remob *remob, int driveable) { if (driveable) { /* initialise Drivable interface */ remob->pDrivInt = CreateDrivableInterface(); if (!remob->pDrivInt) return 0; remob->pDrivInt->SetValue = RemobRun; remob->pDrivInt->CheckLimits = RemobLimits; remob->pDrivInt->CheckStatus = RemobStatus; remob->pDrivInt->GetValue = RemobGetValue; remob->pDrivInt->Halt = RemobHalt; } else if (remob->pDrivInt) { free(remob->pDrivInt); remob->pDrivInt = NULL; } return 1; } /*-----------------------------------------------------------------------*/ static Remob *RemobInit(char *name, RemServer *remserver) { Remob *remob, *p; assert(name); /* get memory */ remob = (Remob *)calloc(1,sizeof(Remob)); if(!remob) { return NULL; } /* copy arguments */ remob->server = remserver; remob->name = strdup(name); /* initialise object descriptor */ remob->desc = CreateDescriptor("RemObject"); if (!remob->desc) goto killit; remob->desc->GetInterface = RemobGetInterface; remob->desc->SaveStatus = RemobSaveStatus; /* initialise callback interface */ remob->pCall = CreateCallBackInterface(); if(!remob->pCall) goto killit; /* check if not yet in object list */ for (p = remserver->objList; p != NULL; p=p->next) { if (p == remob) break; } if (p == NULL) { remob->next = remserver->objList; remserver->objList = remob; } remob->markForDel = 0; /* done */ return remob; killit: RemobKill(remob); return NULL; } /*-----------------------------------------------------------------------*/ static RemServer *RemServerInit(char *name, char *host, int port) { RemServer *remserver = NULL; assert(name); remserver = calloc(1, sizeof(RemServer)); if (!remserver) { return 0; } /* initialise object descriptor */ remserver->desc = CreateDescriptor("RemServer"); if (!remserver->desc) { free(remserver); return NULL; } remserver->desc->SaveStatus = RemServerSaveStatus; remserver->name = strdup(name); remserver->host = strdup(host); remserver->port = port; remserver->incomplete = 0; remserver->objList = NULL; remserver->chans[0] = NULL; remserver->chans[1] = NULL; if (!remserver->name || !remserver->host || !remserver->port) { /* no success, clean up */ RemServerKill(remserver); return NULL; } remserver->taskActive = 1; TaskRegister(pServ->pTasker, RemServerTask, NULL, RemServerKill, remserver, 1); return remserver; } /*-------------------------------------------------------------------------- The Factory function for creating a remote driveable object. Usage: Remob server serverName host:port Remob obj remobName serverName Remob drv remobName serverName Remob del remobName Remob del serverName */ int RemobCreate(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) { RemServer *remserver = NULL; Remob *remob = NULL; char host[128]; char *p; int iD, iRet; char *obj; assert(pCon); assert(pSics); argtolower(argc,argv); if (argc >= 4 && strcmp(argv[1], "server") == 0) { if (argc == 5) { remserver = RemServerInit(argv[2], argv[3], atoi(argv[4])); } else { p = strchr(argv[3], ':'); if (!p) { SCPrintf(pCon, eError, "ERROR: illegal host:port"); return 0; } snprintf(host, sizeof host, "%.*s", p-argv[3], argv[3]); remserver = RemServerInit(argv[2], host, atoi(p+1)); } if (!remserver) { SCPrintf(pCon, eError, "ERROR: Failure to create remote server connection %s", argv[2]); return 0; } /* create the interpreter command */ iRet = AddCommand(pSics,argv[2],RemServerAction,RemServerKill,remserver); if (!iRet) { SCPrintf(pCon, eError, "ERROR: duplicate command %s not created",argv[2]); RemServerKill(remserver); return 0; } return 1; } else if (argc == 4 && (strcmp(argv[1], "obj") == 0 || strcmp(argv[1], "drv") == 0)) { remserver = FindCommandData(pServ->pSics, argv[3], "RemServer"); if (!remserver) { SCPrintf(pCon, eError, "ERROR: remote server %s not found", argv[3]); return 0; } remob = FindCommandData(pServ->pSics, argv[2], "RemObject"); if (remob) { if (remob->server == remserver) { RemobSetDriveable(remob, strcmp(argv[1], "drv") == 0); remob->markForDel = 0; return 1; /* object exists already, silent return */ } } remob = RemobInit(argv[2], remserver); if (!remob) { SCPrintf(pCon, eError, "ERROR: Failure to create remote object %s", argv[1]); return 0; } RemobSetDriveable(remob, strcmp(argv[1], "drv") == 0); /* create the interpreter command */ iRet = AddCommand(pSics,argv[2],RemobAction,RemobKill,remob); SCparChange(pCon); if (!iRet) { SCPrintf(pCon, eError, "ERROR: duplicate command %s not created",argv[2]); RemobKill(remob); return 0; } return 1; } else if (argc == 3 && strcmp(argv[1], "del") == 0) { remob = FindCommandData(pSics, argv[2], "RemObject"); if (remob) { /* its a remob */ if (remob->pDrivInt && pServ->pExecutor && isInRunMode(pServ->pExecutor)) { SCPrintf(pCon,eError,"ERROR: cannot delete %s while running", argv[2]); return 0; } return RemoveCommand(pSics, argv[2]); } if (pServ->pExecutor && isInRunMode(pServ->pExecutor)) { SCPrintf(pCon,eError,"ERROR: cannot delete %s while running", argv[2]); return 0; } remserver = FindCommandData(pSics, argv[2], "RemServer"); if (remserver) { /* its a remserver */ return RemoveCommand(pSics, argv[2]); } SCWrite(pCon,"ERROR: remob object not found",eError); return 0; } SCPrintf(pCon, eError, "ERROR: illegal arguments for command remob"); return 0; }