/*--------------------------------------------------------------------------- E N V I R O N M E N T C O N T R O L L E R This is the implementation file for a base class for all environment control devices in SICS. Mark Koennecke, Juli 1997 Implemented calling site specific initialisation routines. Mark Koennecke, July 2003 Implemented scripted out of tolerance handling and retrieval of driver name. Mark Koennecke, December 2003 Copyright: Labor fuer Neutronenstreuung Paul Scherrer Institut CH-5423 Villigen-PSI The authors hereby grant permission to use, copy, modify, distribute, and license this software and its documentation for any purpose, provided that existing copyright notices are retained in all copies and that this notice is included verbatim in any distributions. No written agreement, license, or royalty fee is required for any of the authorized uses. Modifications to this software may be copyrighted by their authors and need not follow the licensing terms described here, provided that the new terms are clearly indicated on the first page of each file where they apply. IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. ----------------------------------------------------------------------------*/ #include #include #include #include #include #include "fortify.h" #include "splitter.h" #include "sics.h" #include "obpar.h" #include "devexec.h" #include "nserver.h" #include "interrupt.h" #include "emon.h" #include "varlog.h" #include "evcontroller.h" #include "evcontroller.i" #include "evdriver.i" #include "simev.h" #include "tclev.h" #include "chadapter.h" #include "status.h" #include "site.h" /*--------------------- Functions needed to implement interfaces -----------*/ static long EVIDrive(void *pData, SConnection * pCon, float fVal) { pEVControl self = NULL; int iRet, iCode, i, iFix, savedStatus; char pError[132], pBueffel[256]; Tcl_Interp *pTcl = NULL; self = (pEVControl) pData; assert(self); assert(pCon); if (self->runScript != NULL) { pTcl = InterpGetTcl(pServ->pSics); snprintf(pBueffel, sizeof(pBueffel), "%s %f", self->runScript, fVal); iRet = Tcl_Eval(pTcl, pBueffel); if (iRet != TCL_OK) { SCPrintf(pCon, eError, "ERROR: %s while processing runscript for %s", pTcl->result, self->pName); } } self->fTarget = fVal; self->eMode = EVDrive; self->iStop = 0; self->start = time(NULL); self->lastt = 0; self->iWarned = 0; if (self->conn != NULL) { SCDeleteConnection(self->conn); } self->conn = SCCopyConnection(pCon); if (self->conn == NULL) { SCWrite(pCon, "ERROR: out of memory in EVIDrive", eError); return 0; } /* try at least three times to do it */ for (i = 0; i < 3; i++) { iRet = self->pDriv->SetValue(self->pDriv, fVal); if (!iRet) { self->pDriv->GetError(self->pDriv, &iCode, pError, 131); iFix = self->pDriv->TryFixIt(self->pDriv, iCode); switch (iFix) { case DEVOK: return 1; break; case DEVFAULT: sprintf(pBueffel, "ERROR: %s", pError); SCWrite(pCon, pBueffel, eError); return 0; case DEVREDO: sprintf(pBueffel, "WARNING: Fixing problem %s", pError); SCWrite(pCon, pBueffel, eWarning); break; } } else { return 1; } } SCWrite(pCon, "ERROR: Failed to fix previous problems, Device Error", eError); return 0; } /*---------------------------------------------------------------------------*/ static float EVIGet(void *pData, SConnection * pCon) { pEVControl self = NULL; int iRet, iCode, i, iFix; char pError[132], pBueffel[256]; float fPos = -999.; int iPendingCount = 0; self = (pEVControl) pData; assert(self); assert(pCon); /* try at least three times to do it */ for (i = 0; i < 5; i++) { iRet = self->pDriv->GetValue(self->pDriv, &fPos); if (iRet == 0) { self->pDriv->GetError(self->pDriv, &iCode, pError, 131); iFix = self->pDriv->TryFixIt(self->pDriv, iCode); switch (iFix) { case DEVOK: return fPos; break; case DEVFAULT: sprintf(pBueffel, "ERROR: %s", pError); SCWrite(pCon, pBueffel, eError); return -999.; case DEVREDO: sprintf(pBueffel, "WARNING: Fixing problem %s", pError); SCWrite(pCon, pBueffel, eWarning); break; } } else if (iRet == -1) { /* pending */ i--; iPendingCount++; if (iPendingCount > 300) { SCWrite(pCon, "ERROR: cannot get data from socket", eError); return -999.; } } else { return fPos; } } SCWrite(pCon, "ERROR: Failed to fix previous problems, Device Error", eError); return -999.; } static void notifyStatus(pEVControl self, SConnection * pCon, int status) { if (self->pDrivInt->drivableStatus != status) { ((SConnection *) pCon)->conEventType = STATUS; ((SConnection *) pCon)->conStatus = status; SCWrite(pCon, "", eEvent); self->pDrivInt->drivableStatus = status; } } /*---------------------------------------------------------------------------*/ static int EVIStatus(void *pData, SConnection * pCon) { pEVControl self = NULL; float fPos, fDelta = .0; int iRet, iCode, iFix; char pBueffel[256], pError[132]; static int callCount; time_t now, tmo; float tol; self = (pEVControl) pData; assert(self); assert(pCon); /* go to idle when stopped */ if (self->iStop) { notifyStatus(self, pCon, HWIdle); return HWIdle; } /* get the current position */ iRet = self->pDriv->GetValues(self->pDriv, &self->fTarget, &fPos, &fDelta); if (iRet == 0) { self->pDriv->GetError(self->pDriv, &iCode, pError, 131); iFix = self->pDriv->TryFixIt(self->pDriv, iCode); switch (iFix) { case DEVOK: return HWBusy; break; case DEVFAULT: sprintf(pBueffel, "ERROR: %s", pError); SCWrite(pCon, pBueffel, eError); notifyStatus(self, pCon, HWFault); return HWFault; case DEVREDO: sprintf(pBueffel, "WARNING: Fixing problem %s", pError); SCWrite(pCon, pBueffel, eWarning); notifyStatus(self, pCon, HWBusy); return HWBusy; break; } } else if (iRet == -1) { /* pending */ notifyStatus(self, pCon, HWBusy); return HWBusy; } if (fPos < -990.) { snprintf(pBueffel, 255, "ERROR: %s cannot read its current value", self->pName); SCWrite(pCon, pBueffel, eError); self->eMode = EVIdle; notifyStatus(self, pCon, HWFault); return HWFault; } if (fDelta < 0.) { fDelta = -fDelta; } /* handle callback */ callCount++; if (callCount >= 10) { sprintf(pBueffel, "%s = %g", self->pName, fPos); InvokeCallBack(self->pCall, VALUECHANGE, pBueffel); callCount = 0; } now = time(NULL); tmo = (int) (ObVal(self->pParam, MAXWAIT)); /* based on this: logic ! */ if (tmo > 0 && now > self->start + tmo) { /* time out */ snprintf(pBueffel,255, "ERROR: wait time limit reached on %s", self->pName); SCWrite(pCon, pBueffel, eError); self->eMode = EVMonitor; notifyStatus(self, pCon, HWIdle); return HWIdle; } tol = ObVal(self->pParam, TOLERANCE); if (self->lastt > 0) { /* increase tol for hysteresis */ tol = tol * 1.1001; } tmo = (int) (ObVal(self->pParam, SETTLE)); if (fDelta <= tol) { /* inside tolerance */ if (self->lastt <= 0) { /* lastt negative: -seconds already waited */ self->lastt += now; if (tmo > 0) { snprintf(pBueffel,255, "%s inside tolerance, wait %.2f sec to settle", self->pName, (self->lastt + tmo - now) * 1.0); SCWrite(pCon, pBueffel, eLog); } notifyStatus(self, pCon, HWBusy); return HWBusy; } if (now > self->lastt + tmo) { self->eMode = EVMonitor; notifyStatus(self, pCon, HWIdle); return HWIdle; } notifyStatus(self, pCon, HWBusy); return HWBusy; } else { if (self->lastt > 0) { /* save time already waited */ if (tmo > 0) { snprintf(pBueffel,255, "%s outside tolerance, settling time suspended", self->pName); SCWrite(pCon, pBueffel, eLog); } self->lastt -= now; } notifyStatus(self, pCon, HWBusy); return HWBusy; } } /*--------------------------------------------------------------------------*/ static int EVILimits(void *pData, float fVal, char *pError, int iErrLen) { pEVControl self = NULL; char pBueffel[256]; self = (pEVControl) pData; assert(self); /* lower limit */ if (fVal < ObVal(self->pParam, LOWLIMIT)) { sprintf(pBueffel, "ERROR: %g violates lower limit of device", fVal); strlcpy(pError, pBueffel, iErrLen); return 0; } /* upper limit */ if (fVal > ObVal(self->pParam, UPLIMIT)) { sprintf(pBueffel, "ERROR: %g violates upper limit of device", fVal); strlcpy(pError, pBueffel, iErrLen); return 0; } /* OK */ return 1; } /*---------------------------------------------------------------------------*/ static int EVIHalt(void *pData) { pEVControl self = NULL; float fPos; int iRet, iRes; self = (pEVControl) pData; assert(self); iRes = 1; self->eMode = EVIdle; self->iStop = 1; return iRes; } /*---------------------------- Error Handlers --------------------------------*/ static void ErrWrite(char *txt, SConnection * conn) { pExeList pExe; SConnection *pCon = NULL; pExe = GetExecutor(); pCon = GetExeOwner(pExe); if (pCon) { SCWrite(pCon, txt, eLog); } else { SCWrite(conn, txt, eLog); } } /*-----------------------------------------------------------------------*/ static void ErrReport(pEVControl self) { float fPos = .0, fDelta = .0; char pBueffel[256]; self->pDriv->GetValues(self->pDriv, &self->fTarget, &fPos, &fDelta); if (self->iWarned == 0) { snprintf(pBueffel, 255,"WARNING: %s is out of range by %g", self->pName, fDelta); ErrWrite(pBueffel, self->conn); self->iWarned = 1; } } /*-------------------------------------------------------------------------*/ static int ErrLazy(void *pData) { pEVControl self = NULL; self = (pEVControl) pData; assert(self); ErrReport(self); return 1; } /*--------------------------------------------------------------------------*/ static int ErrPause(void *pData) { pEVControl self = NULL; pExeList pExe; int iRet; self = (pEVControl) pData; assert(self); ErrReport(self); pExe = GetExecutor(); if (IsCounting(pExe)) { SCWrite(GetExeOwner(pExe), "Pausing till OK", eLogError); PauseExecution(pExe); /* wait till OK */ iRet = 0; while (iRet == 0 && IsCounting(pExe)) { SicsWait(5); iRet = self->pEnvir->IsInTolerance(self); } /* OK now, continue */ self->iWarned = 0; ContinueExecution(pExe); return 1; } return 1; } /*--------------------------------------------------------------------------*/ static int ErrScript(void *pData) { pEVControl self = NULL; int iRet; Tcl_Interp *pTcl = NULL; pExeList pExe; char pBueffel[256]; self = (pEVControl) pData; assert(self); ErrReport(self); pExe = GetExecutor(); if (self->errorScript != NULL) { pTcl = InterpGetTcl(pServ->pSics); iRet = Tcl_Eval(pTcl, self->errorScript); if (iRet != TCL_OK) { snprintf(pBueffel, 255, "ERROR: %s while processing errorscript for %s", pTcl->result, self->pName); ErrWrite(pBueffel, self->conn); } /* assume that everything is fine again after the script returns */ self->eMode = EVMonitor; self->iWarned = 0; } else { snprintf(pBueffel, 255, "ERROR: script error handling requested for %s, but no script given", self->pName); ErrWrite(pBueffel, self->conn); } return 1; } /*---------------------------------------------------------------------------*/ static int ErrInterrupt(void *pData) { pEVControl self = NULL; self = (pEVControl) pData; assert(self); ErrReport(self); /* interrupt */ SetInterrupt((int) ObVal(self->pParam, INTERRUPT)); self->iWarned = 0; return 1; } /*---------------------------------------------------------------------------*/ static int ErrRun(void *pData) { pEVControl self = NULL; self = (pEVControl) pData; assert(self); ErrReport(self); ErrWrite("Running to safe value", self->conn); self->pDriv->SetValue(self->pDriv, ObVal(self->pParam, SAFEVALUE)); self->eMode = EVIdle; self->iWarned = 0; return 1; } /*---------------------------------------------------------------------------*/ static int EVIErrHandler(void *pData) { pEVControl self = NULL; int iRet, iHandler, iStatus; self = (pEVControl) pData; assert(self); /* select an error handler according to the variable set in Controller */ iHandler = (int) ObVal(self->pParam, ERRORHANDLER); self->eMode = EVError; switch (iHandler) { case 0: /* no op */ iStatus = ErrLazy(pData); break; case 1: /* Pause */ iStatus = ErrPause(pData); break; case 2: /* Interrupt */ iStatus = ErrInterrupt(pData); break; case 3: /* run to a safe place, put him into idle afterwards */ iStatus = ErrRun(pData); return iStatus; break; case 4: /* invoke a script */ iStatus = ErrScript(pData); return iStatus; break; default: return 0; } /* reset mode to monitor again! */ self->eMode = EVMonitor; return iStatus; } /*---------------------------------------------------------------------------*/ static EVMode EVIGetMode(void *pData) { pEVControl self = NULL; self = (pEVControl) pData; assert(self); return self->eMode; } /*--------------------------------------------------------------------------*/ static int EVIIsInTolerance(void *pData) { pEVControl self = NULL; float fPos, fDelta; int iRet, iCode, iStat; char pError[132], pBueffel[512]; pExeList pExe = NULL; SConnection *pCon = NULL; float tol; self = (pEVControl) pData; assert(self); iRet = self->pDriv->GetValues(self->pDriv, &self->fTarget, &fPos, &fDelta); if (iRet == 1) { if (self->iLog) { VarlogAdd(self->pLog, fPos); } if (fDelta < 0.) { fDelta = -fDelta; } tol = ObVal(self->pParam, TOLERANCE); if (self->iWarned == 0) tol = tol * 1.1001; if (fDelta <= tol) { self->eMode = EVMonitor; if (self->iWarned) { snprintf(pBueffel,255, "Environment device %s back in tolerances again", self->pName); ErrWrite(pBueffel, self->conn); self->iWarned = 0; } return 1; } else { return 0; } /* deal with callbacks */ self->callCount++; if (self->callCount >= 20) { snprintf(pBueffel,255, "%s = %g", self->pName, fPos); InvokeCallBack(self->pCall, VALUECHANGE, pBueffel); self->callCount = 0; } } else if (iRet == -1) { /* pending */ /* pending means OK to me */ return 1; } else { /* break down of connection to a environment device has to be considered a problem as well */ memset(pError, 0, 132 * sizeof(char)); self->pDriv->GetError(self->pDriv, &iCode, pError, 131); Log(ERROR,"dev", "emon:%s", pError); iStat = self->pDriv->TryFixIt(self->pDriv, iCode); if ((iStat == DEVOK) || (iStat == DEVREDO)) { return 1; /* effectively a redo in some time */ } else { return 0; } } return 0; } /*-------------------------------------------------------------------------*/ static void *EVIInterface(void *pData, int iCode) { pEVControl self = NULL; self = (pEVControl) pData; assert(self); if (iCode == DRIVEID) { return self->pDrivInt; } else if (iCode == ENVIRINTERFACE) { return self->pEnvir; } return NULL; } /*--------------------------------------------------------------------------*/ /* this routine is the standard method of pEVDriver for GetValues. * it may be replaced by a device specific routine, for the case that * the target value differs systematically from the target value * (i.e.control not on sample sensor) * Oct. 2002 M.Zolliker */ static int StdGetValues(pEVDriver self, float *fTarget, float *fPos, float *fDelta) { int iRet; assert(self); iRet = self->GetValue(self, fPos); if (iRet > 0) *fDelta = *fTarget - *fPos; return iRet; } /*-------- All this done, we can actually implement the controller ---------*/ pEVControl CreateEVController(pEVDriver pDriv, char *pName, int *iErr) { pEVControl pRes = NULL; int iRet; assert(pDriv); *iErr = 1; /* new memory first */ pRes = (pEVControl) malloc(sizeof(EVControl)); if (!pRes) { return NULL; } memset(pRes, 0, sizeof(EVControl)); /* new Object Descriptor */ pRes->pDes = CreateDescriptor("Environment Controller"); if (!pRes->pDes) { free(pRes); return NULL; } pRes->pDes->GetInterface = EVIInterface; /* new Drivable interface */ pRes->pDrivInt = CreateDrivableInterface(); if (!pRes->pDrivInt) { DeleteDescriptor(pRes->pDes); free(pRes); return NULL; } pRes->pDrivInt->Halt = EVIHalt; pRes->pDrivInt->CheckLimits = EVILimits; pRes->pDrivInt->SetValue = EVIDrive; pRes->pDrivInt->CheckStatus = EVIStatus; pRes->pDrivInt->GetValue = EVIGet; /* new environment interface */ pRes->pEnvir = CreateEVInterface(); if (!pRes->pEnvir) { free(pRes->pDrivInt); DeleteDescriptor(pRes->pDes); free(pRes); return NULL; } pRes->pEnvir->GetMode = EVIGetMode; pRes->pEnvir->IsInTolerance = EVIIsInTolerance; pRes->pEnvir->HandleError = EVIErrHandler; /* callback interface */ pRes->pCall = CreateCallBackInterface(); if (!pRes->pCall) { free(pRes->pDrivInt); DeleteDescriptor(pRes->pDes); free(pRes); return NULL; } /* new parameter space */ pRes->pParam = ObParCreate(9); if (!pRes->pParam) { free(pRes->pDrivInt); free(pRes->pEnvir); DeleteDescriptor(pRes->pDes); free(pRes); return NULL; } ObParInit(pRes->pParam, TOLERANCE, "tolerance", 2.0, usUser); ObParInit(pRes->pParam, ACCESS, "access", usUser, usMugger); ObParInit(pRes->pParam, ERRORHANDLER, "errorhandler", 0.0, usUser); ObParInit(pRes->pParam, INTERRUPT, "interrupt", eContinue, usUser); ObParInit(pRes->pParam, UPLIMIT, "upperlimit", 300.0, usUser); ObParInit(pRes->pParam, LOWLIMIT, "lowerlimit", 1.0, usUser); ObParInit(pRes->pParam, SAFEVALUE, "safevalue", 0., usUser); ObParInit(pRes->pParam, MAXWAIT, "maxwait", 0., usUser); ObParInit(pRes->pParam, SETTLE, "settle", 0., usUser); /* local initialisations */ if (pDriv->GetValues == NULL) { /* if GetValues is undefined, set to default */ pDriv->GetValues = StdGetValues; } iRet = pDriv->Init(pDriv); *iErr = iRet; /* new var log for logging values */ pRes->iLog = 1; if (!VarlogInit(&pRes->pLog)) { DeleteEVController(pRes); return NULL; } pRes->pName = strdup(pName); pRes->eMode = EVIdle; pRes->iWarned = 0; if (pRes->conn != NULL) { SCDeleteConnection(pRes->conn); pRes->conn = NULL; } /* a terminal error gives a -1 in iRet */ if (iRet < 0) { DeleteEVController(pRes); return NULL; } pRes->pDriv = pDriv; /* moved here to avoid double freeing on evfactory del */ return pRes; } /*---------------------------------------------------------------------------*/ void DeleteEVController(void *pData) { pEVControl self = NULL; self = (pEVControl) pData; assert(self); if (self->pDes) { DeleteDescriptor(self->pDes); self->pDes = NULL; } if (self->pDrivInt) { free(self->pDrivInt); } if (self->pEnvir) { free(self->pEnvir); } if (self->pCall) { DeleteCallBackInterface(self->pCall); } if (self->KillPrivate) { self->KillPrivate(self->pPrivate); } if (self->pDriv) { self->pDriv->Close(self->pDriv); DeleteEVDriver(self->pDriv); } if (self->pName) { free(self->pName); } if (self->pParam) { ObParDelete(self->pParam); } if (self->pLog) { VarlogDelete(self->pLog); } if (self->driverName != NULL) { free(self->driverName); } if (self->errorScript != NULL) { free(self->errorScript); } if (self->creationArgs != NULL) { free(self->creationArgs); } if (self->runScript != NULL) { free(self->runScript); } if (self->conn != NULL) { SCDeleteConnection(self->conn); } free(self); } /*--------------------------------------------------------------------------*/ int EVCDrive(pEVControl self, SConnection * pCon, float fVal) { char pBueffel[256], pError[132]; int iRet; assert(self); assert(pCon); /* Check Limits */ iRet = self->pDrivInt->CheckLimits(self, fVal, pError, 131); if (!iRet) { SCWrite(pCon, pError, eError); return 0; } /* start executing */ iRet = StartDevice(GetExecutor(), self->pName, self->pDes, self, pCon, SCGetRunLevel(pCon), fVal); if (!iRet) { snprintf(pBueffel,255, "ERROR: Failure to start %s", self->pName); SCWrite(pCon, pBueffel, eError); return 0; } /* Wait for success */ iRet = Wait4Success(GetExecutor()); if (iRet == DEVINT) { return 0; } return 1; } /*--------------------------------------------------------------------------*/ int EVCGetPos(pEVControl self, SConnection * pCon, float *fPos) { float fVal; assert(self); assert(pCon); fVal = self->pDrivInt->GetValue(self, pCon); if (fVal < -990) { return 0; } *fPos = fVal; return 1; } /*-------------------------------------------------------------------------*/ pVarLog EVCGetVarLog(pEVControl self) { assert(self); return self->pLog; } /*---------------------------------------------------------------------------*/ EVMode EVCGetMode(pEVControl self) { assert(self); return self->eMode; } /*--------------------------------------------------------------------------*/ int EVCSetMode(pEVControl self, EVMode eNew) { assert(self); self->eMode = eNew; return 1; } /*---------------------------------------------------------------------------*/ int EVCGetPar(pEVControl self, char *name, float *fVal) { ObPar *pPar = NULL; assert(self); if (strcmp(name, "target") == 0) { *fVal = self->fTarget; return 1; } pPar = ObParFind(self->pParam, name); if (pPar) { *fVal = pPar->fVal; return 1; } else { return 0; } } /*---------------------------------------------------------------------------*/ int EVCSetPar(pEVControl self, char *name, float fVal, SConnection * pCon) { ObPar *pPar = NULL; int iRet, savedStatus; assert(self); assert(pCon); /* find the parameter */ pPar = ObParFind(self->pParam, name); if (pPar == NULL) { SCPrintf(pCon, eError, "ERROR: %s.%s parameter not found", self->pName, name); return 0; } /* DO NOT CHECK RUNNING STATE, this is not needed */ /* check permission */ if (!SCMatchRights(pCon, pPar->iCode)) { SCPrintf(pCon, eError, "ERROR: Insufficient privilege to change %s.%s", self->pName, name); return 0; } /* passed all tests: do It! */ pPar->fVal = fVal; iRet = 1; if (self->iTcl) { iRet = UpdateTclVariable(self->pDriv, name, fVal); } return iRet; } /*--------------------------------------------------------------------------*/ int EVCList(pEVControl self, SConnection * pCon) { char pBueffel[256]; float fPos; assert(self); assert(pCon); snprintf(pBueffel, 255, "Parameter listing for %s", self->pName); SCWrite(pCon, pBueffel, eValue); snprintf(pBueffel, 255, "%s.%s = %g ", self->pName, "tolerance", ObVal(self->pParam, TOLERANCE)); SCWrite(pCon, pBueffel, eValue); snprintf(pBueffel, 255, "%s.%s = %g", self->pName, "access", ObVal(self->pParam, ACCESS)); SCWrite(pCon, pBueffel, eValue); snprintf(pBueffel, 255, "%s.%s = %g", self->pName, "ErrorHandler", ObVal(self->pParam, ERRORHANDLER)); SCWrite(pCon, pBueffel, eValue); snprintf(pBueffel, 255, "%s.%s = %g", self->pName, "interrupt", ObVal(self->pParam, INTERRUPT)); SCWrite(pCon, pBueffel, eValue); snprintf(pBueffel, 255, "%s.%s = %g", self->pName, "UpperLimit", ObVal(self->pParam, UPLIMIT)); SCWrite(pCon, pBueffel, eValue); snprintf(pBueffel, 255, "%s.%s = %g", self->pName, "LowerLimit", ObVal(self->pParam, LOWLIMIT)); SCWrite(pCon, pBueffel, eValue); snprintf(pBueffel, 255, "%s.%s = %g", self->pName, "SafeValue", ObVal(self->pParam, SAFEVALUE)); SCWrite(pCon, pBueffel, eValue); snprintf(pBueffel, 255, "%s.%s = %g (sec)", self->pName, "MaxWait", ObVal(self->pParam, MAXWAIT)); SCWrite(pCon, pBueffel, eValue); snprintf(pBueffel, 255, "%s.%s = %g (sec)", self->pName, "Settle", ObVal(self->pParam, SETTLE)); SCWrite(pCon, pBueffel, eValue); EVCGetPos(self, pCon, &fPos); snprintf(pBueffel, 255, "%s.%s = %g", self->pName, "CurrentValue", fPos); SCWrite(pCon, pBueffel, eValue); snprintf(pBueffel, 255, "%s.%s = %g", self->pName, "TargetValue", self->fTarget); SCWrite(pCon, pBueffel, eValue); snprintf(pBueffel, 255, "%s.driver = %s", self->pName, self->driverName); SCWrite(pCon, pBueffel, eValue); if (self->errorScript != NULL) { snprintf(pBueffel, 255, "%s.errorScript = %s", self->pName, self->errorScript); } else { snprintf(pBueffel, 255, "%s.errorScript = UNDEFINED", self->pName); } SCWrite(pCon, pBueffel, eValue); if (self->runScript != NULL) { SCPrintf(pCon, eValue, "%s.runScript = %s", self->pName, self->runScript); } else { SCPrintf(pCon, eValue, "%s.runScript = none", self->pName); } return 1; } /*-------------------------------------------------------------------------*/ static int EVCallBack(int iEvent, void *pEventData, void *pUserData) { char *pBuf = (char *) pEventData; SConnection *pCon = (SConnection *) pUserData; char pBueffel[132]; if (pCon == NULL || !SCisConnected(pCon)) { return -1; } if (iEvent == VALUECHANGE && pCon != NULL) { SCSetEventType(pCon,POSITION); SCWrite(pCon, pBuf, eEvent); return 1; } return 1; } /*--------------------------------------------------------------------------- The wrapper understands the following syntax: EVControl : print current value EVControl fVal : drive value EVControl par : print parameter EVControl par fVal : set parameter EVControl list : lists parameters EVControl log ... : log subcommands EVControl send bla bla ..... : sends everything after send to the device ---------------------------------------------------------------------------*/ int EVControlWrapper(SConnection * pCon, SicsInterp * pSics, void *pData, int argc, char *argv[]) { pEVControl self = NULL; char pBueffel[256], pReply[512]; double dVal; float fPos; int iRet; long lID; self = (pEVControl) pData; assert(self); assert(pSics); assert(pCon); if (argc < 2) { /* only value requested */ iRet = EVCGetPos(self, pCon, &fPos); if (iRet) { /* sprintf(pBueffel,"%s.%s = %g",self->pName,"CurrentValue", fPos); SCWrite(pCon,pBueffel,eValue); */ SCPrintf(pCon, eValue, "%s = %g", argv[0], fPos); return 1; } return 0; } else { strtolower(argv[1]); } /* analyse commands */ if (strcmp(argv[1], "send") == 0) { /* send to controller */ Arg2Text(argc - 2, &argv[2], pBueffel, 255); iRet = self->pDriv->Send(self->pDriv, pBueffel, pReply, 511); SCWrite(pCon, pReply, eValue); return iRet; } /* install automatic notification */ else if (strcmp(argv[1], "interest") == 0) { lID = RegisterCallback(self->pCall, VALUECHANGE, EVCallBack, SCCopyConnection(pCon), SCDeleteConnection); SCSendOK(pCon); return 1; } else if (strcmp(argv[1], "uninterest") == 0) { RemoveCallbackCon(self->pCall, pCon); SCSendOK(pCon); return 1; } else if (strcmp(argv[1], "log") == 0) { /* log commands */ if (argc < 3) { SCWrite(pCon, "ERROR: insufficient number of commands for varlog", eError); return 0; } iRet = VarlogWrapper(self->pLog, pCon, argv[2], argv[3], argv[0]); return iRet; } else if (argc < 3) { /* either parameter or drive */ iRet = Tcl_GetDouble(pSics->pTcl, argv[1], &dVal); if (iRet == TCL_OK) { /* float Value: drive */ if (!SCMatchRights(pCon, usUser)) { return 0; } iRet = EVCDrive(self, pCon, (float) dVal); if (iRet) { SCSendOK(pCon); } return iRet; } else if (strcmp(argv[1], "list") == 0) { EVCList(self, pCon); return 1; } else if (strcmp(argv[1], "send") == 0) { /* send to controller */ Arg2Text(argc - 2, &argv[2], pBueffel, 255); iRet = self->pDriv->Send(self->pDriv, pBueffel, pReply, 511); SCWrite(pCon, pReply, eValue); return iRet; } else if (strcmp(argv[1], "log") == 0) { /* log commands */ if (argc < 3) { SCWrite(pCon, "ERROR: insufficient number of commands for varlog", eError); return 0; } return VarlogWrapper(self->pLog, pCon, argv[2], argv[3], argv[0]); } else { /* parameter request */ strtolower(argv[1]); /* catch case of errorScript */ if (strcmp(argv[1], "errorscript") == 0) { if (self->errorScript != NULL) { snprintf(pBueffel, 255, "%s.errorScript = %s", self->pName, self->errorScript); } else { snprintf(pBueffel, 255, "%s.errorScript = UNDEFINED", self->pName); } SCWrite(pCon, pBueffel, eValue); return 1; } /* catch case of runScript */ if (strcmp(argv[1], "runscript") == 0) { if (self->runScript != NULL) { SCPrintf(pCon, eValue, "%s.runScript = %s", self->pName, self->runScript); } else { SCPrintf(pCon, eValue, "%s.runScript = none", self->pName); } return 1; } /* catch case for drivername */ if (strcmp(argv[1], "driver") == 0) { snprintf(pBueffel, 255, "%s.driver = %s", self->pName, self->driverName); SCWrite(pCon, pBueffel, eValue); return 1; } iRet = EVCGetPar(self, argv[1], &fPos); if (!iRet) { snprintf(pBueffel,255, "ERROR: parameter %s NOT found", argv[1]); SCWrite(pCon, pBueffel, eError); return 0; } else { snprintf(pBueffel,255, "%s.%s = %g", self->pName, argv[1], fPos); SCWrite(pCon, pBueffel, eValue); return 1; } } } else { /* try to set parameter */ strtolower(argv[1]); /* first catch case of errorScript */ if (strcmp(argv[1], "errorscript") == 0) { if (self->errorScript != NULL) { free(self->errorScript); } self->errorScript = Arg2Tcl(argc - 2, &argv[2], NULL, 0); SCSendOK(pCon); SCparChange(pCon); return 1; } /* catch case of runScript */ if (strcmp(argv[1], "runscript") == 0) { if (self->runScript != NULL) { free(self->runScript); } if (strcasecmp(argv[2], "none") == 0) { self->runScript = NULL; } else { self->runScript = Arg2Tcl(argc - 2, &argv[2], NULL, 0); } SCSendOK(pCon); SCparChange(pCon); return 1; } iRet = Tcl_GetDouble(pSics->pTcl, argv[2], &dVal); if (iRet != TCL_OK) { snprintf(pBueffel, 255,"ERROR: %s no valid number", argv[2]); SCWrite(pCon, pBueffel, eError); return 0; } iRet = EVCSetPar(self, argv[1], (float) dVal, pCon); if (iRet) { SCSendOK(pCon); SCparChange(pCon); } return iRet; } return 0; /* not reached */ } /*-----------------------------------------------------------------------*/ static int EVSaveStatus(void *pData, char *name, FILE * fil) { pEVControl evc = pData; pEVDriver pD = evc->pDriv; assert(evc); assert(pD); if (evc->creationArgs && pD->SavePars) { fprintf(fil, "if {[catch { evfactory replace %s %s }] == 0} {\n", name, evc->creationArgs); if (pD->SavePars(pD, fil) == 1) { fprintf(fil, " %s %s %g\n", evc->pName, "tolerance", ObVal(evc->pParam, TOLERANCE)); fprintf(fil, " %s %s %g\n", evc->pName, "access", ObVal(evc->pParam, ACCESS)); fprintf(fil, " %s %s %g\n", evc->pName, "ErrorHandler", ObVal(evc->pParam, ERRORHANDLER)); fprintf(fil, " %s %s %g\n", evc->pName, "interrupt", ObVal(evc->pParam, INTERRUPT)); fprintf(fil, " %s %s %g\n", evc->pName, "UpperLimit", ObVal(evc->pParam, UPLIMIT)); fprintf(fil, " %s %s %g\n", evc->pName, "LowerLimit", ObVal(evc->pParam, LOWLIMIT)); fprintf(fil, " %s %s %g\n", evc->pName, "SafeValue", ObVal(evc->pParam, SAFEVALUE)); fprintf(fil, " %s %s %g\n", evc->pName, "MaxWait", ObVal(evc->pParam, MAXWAIT)); fprintf(fil, " %s %s %g\n", evc->pName, "Settle", ObVal(evc->pParam, SETTLE)); if (evc->errorScript != NULL) { fprintf(fil, " %s errorScript %s\n", evc->pName, evc->errorScript); } if (evc->runScript != NULL) { fprintf(fil, " %s runScript %s\n", evc->pName, evc->runScript); } } fprintf(fil, "}\n"); } return 1; } /*--------------------------------------------------------------------------*/ /* standard method for saving parameters */ static int EVCSaveStd(void *pData, char *name, FILE * fil) { return 1; } /*-----------------------------------------------------------------------*/ pEVControl MakeEVController(pEVDriver pDriv, SConnection * pCon, ObjectFunc wrapper, int argc, char *argv[]) { pEVControl pNew; int status = 1; char pBueffel[512]; char pError[132]; if (pDriv == NULL) { SCWrite(pCon, "ERROR: failed to create driver", eError); return NULL; } pNew = CreateEVController(pDriv, argv[0], &status); if (status != 1) { printf("CEVC error\n"); SCWrite(pCon, "ERROR: failed to initialize device", eError); pDriv->GetError(pDriv, &status, pError, sizeof pError - 1); printf("HW %s\n", pError); sprintf(pBueffel, "HW reported: %s", pError); SCWrite(pCon, pBueffel, eError); } if (pNew == NULL) { printf("CEVC failed\n"); SCWrite(pCon, "ERROR: failed to create environment device object", eError); DeleteEVDriver(pDriv); return NULL; } if (wrapper == NULL) { AddCommand(pServ->pSics, argv[0], EVControlWrapper, DeleteEVController, pNew); } else { AddCommand(pServ->pSics, argv[0], wrapper, DeleteEVController, pNew); } return pNew; } /*-----------------------------------------------------------------------*/ static pEVControl InstallCommonControllers(SicsInterp * pSics, SConnection * pCon, int argc, char *argv[], int *found) { pEVControl pNew = NULL; pEVDriver pDriv = NULL; char pBueffel[512], pError[132]; int iRet; int (*Wrapper) (SConnection * pCon, SicsInterp * pSics, void *pData, int argc, char *argv[]) = EVControlWrapper; *found = 1; if (strcmp(argv[3], "sim") == 0) { /* SIM driver */ /* Create a Sim Driver */ pDriv = CreateSIMEVDriver(argc - 4, &argv[4]); if (!pDriv) { SCWrite(pCon, "ERROR: failed to create Environment Device driver", eError); return NULL; } } else if (strcmp(argv[3], "tcl") == 0) { /* Tcl driver */ /* Create a Tcl driver */ pDriv = CreateTclDriver(argc - 4, &argv[4], argv[2], pCon); if (!pDriv) { SCWrite(pCon, "ERROR: failed to create TCL device driver", eError); return NULL; } /* got a driver, initialise everything */ pNew = CreateEVController(pDriv, argv[2], &iRet); if (!pNew) { SCWrite(pCon, "ERROR creating Environment Controller", eError); DeleteEVDriver(pDriv); return NULL; } if (!iRet) { SCWrite(pCon, "ERROR: problem initialising Environment controller", eError); pDriv->GetError(pDriv, &iRet, pError, 131); sprintf(pBueffel, "HW reported: %s", pError); SCWrite(pCon, pBueffel, eError); } Wrapper = TclEnvironmentWrapper; pNew->iTcl = 1; /* make ev parameters available to Tcl */ UpdateTclVariable(pNew->pDriv, "tolerance", ObVal(pNew->pParam, TOLERANCE)); UpdateTclVariable(pNew->pDriv, "upperlimit", ObVal(pNew->pParam, UPLIMIT)); UpdateTclVariable(pNew->pDriv, "lowerlimit", ObVal(pNew->pParam, LOWLIMIT)); } else if (strcmp(argv[3], "gencon") == 0) { /* general controller */ /* Create a driver */ pDriv = MakeControllerEnvironmentDriver(argc - 4, &argv[4]); if (!pDriv) { SCWrite(pCon, "ERROR: failed to create Controller Environment driver", eError); return NULL; } } else { /* not recognized */ *found = 0; return NULL; } if (pNew == NULL) { /* not yet initialized */ pNew = CreateEVController(pDriv, argv[2], &iRet); if (!pNew) { SCWrite(pCon, "ERROR creating Environment Controller", eError); DeleteEVDriver(pDriv); return NULL; } if (!iRet) { SCWrite(pCon, "ERROR: problem initialising Environment controller", eError); pDriv->GetError(pDriv, &iRet, pError, 131); sprintf(pBueffel, "HW reported: %s", pError); SCWrite(pCon, pBueffel, eError); } } /* install command */ iRet = AddCommand(pSics, argv[2], Wrapper, DeleteEVController, pNew); if (!iRet) { snprintf(pBueffel,511, "ERROR: duplicate command %s not created", argv[2]); DeleteEVController((void *) pNew); SCWrite(pCon, pBueffel, eError); return NULL; } return pNew; } int RemoveEVController(SConnection * pCon, char *name) { char pBueffel[512]; int iRet; if (!pServ->pExecutor) { return 1; } if (isInRunMode(pServ->pExecutor)) { SCWrite(pCon, "ERROR: cannot delete while running", eError); return 0; } EVUnregister(FindEMON(pServ->pSics), name); iRet = RemoveCommand(pServ->pSics, name); if (!iRet) { snprintf(pBueffel,511, "ERROR: %s not found, NOT deleted", name); SCWrite(pCon, pBueffel, eError); return 0; } return 1; } /*------------------------------------------------------------------------- EVControlFactory implements a SICS command which creates and deletes Controllers at run-time. Syntax: ControlFactory new name SIM -- creates a new controller name with a simulation driver ControlFactory del name -- deletes controller name -------------------------------------------------------------------------*/ int EVControlFactory(SConnection * pCon, SicsInterp * pSics, void *pData, int argc, char *argv[]) { pEVControl pNew = NULL; char pBueffel[512], pError[132]; int iRet, found; CommandList *pCom = NULL; pSite site = NULL; assert(pSics); assert(pCon); if (argc < 3) { SCWrite(pCon, "ERROR: Insufficient number of arguments to EVFactory", eError); return 0; } /* check rights */ if (!SCMatchRights(pCon, usUser)) { return 0; } strtolower(argv[1]); if (strcmp(argv[1], "del") == 0) { /* delete */ if (RemoveEVController(pCon, argv[2])) { SCSendOK(pCon); return 1; } else { return 0; } } else if (strcmp(argv[1], "new") == 0 || strcmp(argv[1], "replace") == 0) /* make a new one */ { /* argv[2] = name */ /* argv[3] must be type */ if (argc < 4) { SCWrite(pCon, "ERROR: no type specified for Environment Controller", eError); return 0; } strtolower(argv[2]); strtolower(argv[3]); if (FindCommandData(pSics, argv[2], "Environment Controller")) { if (strcmp(argv[1], "replace") == 0) { if (!RemoveEVController(pCon, argv[2])) { return 0; } } else { snprintf(pBueffel, 511, "ERROR: environment device %s already installed, delete first", argv[2]); SCWrite(pCon, pBueffel, eError); return 0; } } pCom = FindCommand(pSics, argv[2]); if (pCom) { snprintf(pBueffel,511, "ERROR: command %s already exists", argv[2]); SCWrite(pCon, pBueffel, eError); return 0; } pNew = InstallCommonControllers(pSics, pCon, argc, argv, &found); if (!found) { site = getSite(); if (site != NULL) { pNew = site->InstallEnvironmentController(pSics, pCon, argc, argv); } else { snprintf(pBueffel,511, "ERROR: %s not recognized as a valid driver type", argv[3]); SCWrite(pCon, pBueffel, eError); pNew = NULL; } } if (pNew == NULL) { /* error message is already written */ return 0; } if (pNew->pDriv->SavePars) { pNew->creationArgs = Arg2Tcl(argc - 3, argv + 3, NULL, 0); pNew->pDes->SaveStatus = EVSaveStatus; } else { pNew->creationArgs = NULL; } EVRegisterController(FindEMON(pSics), argv[2], pNew, pCon); pNew->driverName = strdup(argv[3]); SCSendOK(pCon); return 1; } SCWrite(pCon, "ERROR: command not understood", eError); return 0; }