/*----------------------------------------------------------------------- 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; char *user; char *pass; mkChannel *chan; int incomplete; char line[256]; Remob *objList; int matchMap; int timeout; int taskActive; SCStore conn; } RemServer; struct Remob { pObjectDescriptor desc; char *name; ObPar *ParArray; pIDrivable pDrivInt; pICallBack pCall; RemServer *server; int status; Remob *next; }; 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) { if (remserver->chan) { /* printf("> %s\n", line); */ return NETWrite(remserver->chan, line, strlen(line)); } else { return -1; } } /*-------------------------------------------------------------------------*/ static int RemRead(RemServer *remserver, long tmo) { int iRet; if (remserver->chan == NULL) return 0; /* no data */ iRet = NETReadTillTermNew(remserver->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 int RemCopy(RemServer *remserver, SConnection *pCon) { char buf[256]; if (pCon != NULL && remserver->line[0] != '\0') { snprintf(buf, sizeof(buf), " %s", remserver->line); /* snprintf(buf, sizeof(buf), "%s (%s)", remserver->line, remserver->name); */ SCWrite(pCon, buf, eStatus); } } /*-------------------------------------------------------------------------*/ 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 void RemDisconnect(RemServer *remserver) { if (remserver->chan != NULL) { NETClosePort(remserver->chan); free(remserver->chan); remserver->chan=NULL; /* printf("disconnected\n"); */ } } /*-------------------------------------------------------------------------*/ static void RemConnect(RemServer *remserver) { int iRet; char buf[256]; if (!remserver->chan) { remserver->timeout = 0; remserver->chan = NETConnect(remserver->host, remserver->port); if (!remserver->chan) { return; } snprintf(buf, sizeof(buf), "%s %s\ntransact listexe interest\n" , remserver->user, remserver->pass); iRet = RemWrite(remserver, buf); 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 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; } snprintf(buf, sizeof(buf), "ERROR: no connection to %s", remserver->name); SCWrite(pCon,buf,eError); RemDisconnect(remserver); try--; if (try>0) goto tryagain; 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 *fd) { Remob *self = pData; char buf[512]; assert(self); assert(fd); /* data is stored on remote server */ 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); /* check if I'am allowed to move this motor */ if (!SCMatchRights(pCon,(int)ObVal(remob->ParArray,ACCESSCODE))) { snprintf(buf,sizeof(buf), "ERROR: You are not authorised to run %s", remob->name); SCWrite(pCon,buf,eError); SCSetInterrupt(pCon,eAbortBatch); return 0; } 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, fNew); 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; ObPar *par; char acce[128], inte[128]; assert(pCon); assert(pSics); assert(remob); argtolower(argc,argv); SCSave(&remob->server->conn, pCon); if (argc == 1) { iRet = RemTransact(remob->server, pCon, argv[0], ">", NULL); } else if (strcmp(argv[1],"list") == 0) { snprintf(buf, sizeof(buf), "%s list", remob->name); snprintf(acce, sizeof(acce), "!%s.accesscode", remob->name); snprintf(inte, sizeof(inte), "!%s.interruptmode", remob->name); RemTransact(remob->server, pCon, buf, acce, inte, ">", NULL); snprintf(buf, sizeof(buf), "%s = %.0f", acce+1, ObVal(remob->ParArray,ACCESSCODE)); SCWrite(pCon, buf, eStatus); snprintf(buf, sizeof(buf), "%s = %.0f", inte+1, ObVal(remob->ParArray,INTERRUPTMODE)); SCWrite(pCon, buf, eStatus); iRet=1; } else { par=ObParFind(remob->ParArray, argv[1]); if (par != NULL) { if (argc == 3) { fValue = strtod(argv[2], &endp); if (endp == argv[2]) { snprintf(buf, sizeof(buf), "number expected instead of %s", argv[2]); SCWrite(pCon, buf, eError); return 0; } iRet = ObParSet(remob->ParArray,argv[0],argv[1],fValue,pCon); } if (iRet) { snprintf(buf, sizeof(buf), "%s.%s = %.0f", argv[0], argv[1], par->fVal); SCWrite(pCon, buf, eStatus); } } else { pos=snprintf(buf, sizeof(buf), "%s ", remob->name); for (i=1; iserver, pCon, buf, ">", NULL); } } return iRet; } /*---------------------------------------------------------------------------*/ 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; ObPar *par; char acce[128], inte[128]; assert(pCon); assert(pSics); assert(remserver); argtolower(argc,argv); if (argc == 1) { snprintf(buf, sizeof(buf), "%s = %s:%d", argv[0], remserver->host, remserver->port); SCWrite(pCon, buf, eStatus); } else { pos=0; for (i=1; i", NULL); return iRet; } return 1; } /*--------------------------------------------------------------------------*/ 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); } /* get rid of parameter space */ if (remob->ParArray) { ObParDelete(remob->ParArray); } /* kill Descriptor */ DeleteDescriptor(remob->desc); free(remob); } /*--------------------------------------------------------------------------*/ static void RemServerKill(void *self) { RemServer *remserver = self; assert(remserver); if (remserver->taskActive) { remserver->taskActive=0; /* let the tasker kill me */ return; } remserver = (RemServer *)self; if (remserver->chan) { RemDisconnect(remserver); } DeleteDescriptor(remserver->desc); if (remserver->name) free(remserver->name); if (remserver->host) free(remserver->host); if (remserver->user) free(remserver->user); if (remserver->pass) free(remserver->pass); free(remserver); } /*-----------------------------------------------------------------------*/ static Remob *RemobInit(char *name, RemServer *remserver) { Remob *remob, *p; assert(name); /* get memory */ remob = (Remob *)malloc(sizeof(Remob)); if(!remob) { return NULL; } /* create and initialize parameters */ remob->ParArray = ObParCreate(2); if (!remob->ParArray) { free(remob); return NULL; } ObParInit(remob->ParArray,INTERRUPTMODE,"interruptmode",0.0,usUser); ObParInit(remob->ParArray,ACCESSCODE,"accesscode",(float)usUser,usMugger); /* copy arguments */ remob->server = remserver; remob->name = strdup(name); /* initialise object descriptor */ remob->desc = CreateDescriptor("Remob"); if (!remob->desc) { ObParDelete(remob->ParArray); free(remob); return NULL; } remob->desc->GetInterface = RemobGetInterface; remob->desc->SaveStatus = RemobSaveStatus; /* initialise Drivable interface */ remob->pDrivInt = CreateDrivableInterface(); if (!remob->pDrivInt) { DeleteDescriptor(remob->desc); ObParDelete(remob->ParArray); free(remob); return NULL; } remob->pDrivInt->SetValue = RemobRun; remob->pDrivInt->CheckLimits = RemobLimits; remob->pDrivInt->CheckStatus = RemobStatus; remob->pDrivInt->GetValue = RemobGetValue; remob->pDrivInt->Halt = RemobHalt; /* initialise callback interface */ remob->pCall = CreateCallBackInterface(); if(!remob->pCall) { RemobKill(remob); return NULL; } /* 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; } if (!remserver->taskActive) { remserver->taskActive = 1; TaskRegister(pServ->pTasker, RemServerTask, NULL, RemServerKill, remserver, 1); } /* done */ return remob; } /*-----------------------------------------------------------------------*/ static RemServer *RemServerInit(char *name, char *host, int port, char *user, char *pass) { RemServer *remserver = NULL; assert(name); remserver = malloc(sizeof(RemServer)); if (!remserver) { return 0; } /* initialise object descriptor */ remserver->desc = CreateDescriptor("RemServer"); if (!remserver->desc) { free(remserver); return NULL; } remserver->taskActive = 0; remserver->name = strdup(name); remserver->host = strdup(host); remserver->port = port; remserver->user = strdup(user); remserver->pass = strdup(pass); remserver->incomplete = 0; if (!remserver->name || !remserver->host || !remserver->port || !remserver->pass) { /* no success, clean up */ RemServerKill(remserver); return NULL; } return remserver; } /*-------------------------------------------------------------------------- The Factory function for creating a remote driveable object. Usage: Remob server serverName host port user pass Remob new remobName serverName Remob del remobName (not yet implemented) Remob del serverName (not yet implemented) */ int RemobCreate(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) { RemServer *remserver = NULL; Remob *remob = NULL; char buf[512]; int iD, iRet; Tcl_Interp *pTcl = (Tcl_Interp *)pSics->pTcl; assert(pCon); assert(pSics); if (argc <= 5) { argtolower(argc,argv); } else { argtolower(5,argv); } if (argc == 7 && strcmp(argv[1], "server") == 0) { remserver = RemServerInit(argv[2], argv[3], atoi(argv[4]), argv[5], argv[6]); if (!remserver) { snprintf(buf, sizeof(buf), "Failure to create remote server connection %s", argv[2]); SCWrite(pCon, buf, eError); return 0; } /* create the interpreter command */ iRet = AddCommand(pSics,argv[2],RemServerAction,RemServerKill,remserver); if (!iRet) { snprintf(buf, sizeof(buf),"ERROR: duplicate command %s not created",argv[1]); SCWrite(pCon,buf,eError); return 0; } return 1; } else if (argc == 4 && strcmp(argv[1], "new") == 0) { remserver = FindCommandData(pServ->pSics, argv[3], "RemServer"); if (!remserver) { snprintf(buf, sizeof(buf), "remote server %s not found", argv[3]); SCWrite(pCon, buf, eError); return 0; } remob = RemobInit(argv[2], remserver); if (!remob) { snprintf(buf, sizeof(buf), "Failure to create remote driveable object %s", argv[1]); SCWrite(pCon, buf, eError); return 0; } /* create the interpreter command */ iRet = AddCommand(pSics,argv[2],RemobAction,RemobKill,remob); if (!iRet) { snprintf(buf, sizeof(buf),"ERROR: duplicate command %s not created",argv[1]); SCWrite(pCon,buf,eError); return 0; } return 1; } snprintf(buf, sizeof(buf),"ERROR: illegal arguments for command remob",argv[1]); SCWrite(pCon,buf,eError); }