/*---------------------------------------------------------------------------
	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 environement 
  control devices in SICS.
  
  Mark Koennecke, Juli 1997
  
       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 <string.h>
#include <stdlib.h>
#include <assert.h>
#include <tcl.h>
#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 "itc4.h"
#include "dilludriv.h"
#include "tclev.h"
#include "bruker.h"
#include "ltc11.h"
#include "eurodriv.h"
#include "el755driv.h"
#include "tecsdriv.h"
#include "chadapter.h"
/*--------------------- Functions needed to implement interfaces -----------*/
  static long EVIDrive(void *pData, SConnection *pCon, float fVal)
  {
      pEVControl self = NULL;
      int iRet, iCode, i, iFix;
      char pError[132], pBueffel[256];
      
      self = (pEVControl)pData;
      assert(self);
      assert(pCon);
      
      self->fTarget = fVal;
      self->eMode = EVDrive;
      self->iStop = 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 int EVIStatus(void *pData, SConnection *pCon)
  {
      pEVControl self = NULL;
      float fPos, fDelta;
      int iRet, iCode, iFix;
      char pBueffel[256], pError[132];
      static int iCount;
      
      self = (pEVControl)pData;
      assert(self);
      assert(pCon);

      /* go to idle when stopped */
      if(self->iStop)
      {
            return HWIdle;
      }

      /* get the current position */

        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 HWBusy;
                        break;
              case DEVFAULT:
                   sprintf(pBueffel,"ERROR: %s",pError);
                   SCWrite(pCon,pBueffel,eError);
                   return HWFault;
              case DEVREDO:
                   sprintf(pBueffel,"WARNING: Fixing problem %s",pError);
                   SCWrite(pCon,pBueffel,eWarning);
                   return HWBusy;
                   break;                            
           }
        }
        else if(iRet == -1 ) /* pending */
        {
          return HWBusy;
        }
      
      if(fPos < -990.)
      {
        sprintf(pBueffel,"ERROR: %s cannot read its current value",
                self->pName);
        SCWrite(pCon,pBueffel,eError);
        self->eMode = EVIdle;
        return HWFault;
      }
  
      /* calculate difference to target */
      fDelta = self->fTarget - fPos;
      if(fDelta < 0.)
      {
        fDelta = - fDelta;
      }
   
      /* based on this: logic ! */
      if(fDelta <= ObVal(self->pParam, TOLERANCE)) /* done */
      {
        iCount++;
        if(iCount > 3)
        {
          self->eMode = EVMonitor;
          return HWIdle;
        }
        else
        {
          return HWBusy;
        }  
      }
      else
      {
        iCount = 0;
        return HWBusy;
      }
      self->eMode = EVIdle;
      return HWFault;
  }
/*--------------------------------------------------------------------------*/
  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: %f violates lower limit of device",fVal);
        strncpy(pError,pBueffel,iErrLen);
        return 0;
      }
  
      /* upper limit */
      if( fVal > ObVal(self->pParam, UPLIMIT))
      {
        sprintf(pBueffel,"ERROR: %f violates upper limit of device",fVal);
        strncpy(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 int ErrLazy(void *pData)
  {
      pEVControl self = NULL;
      pExeList   pExe;
      SConnection *pCon = NULL;
      float fPos;
      char pBueffel[256];
      
      self = (pEVControl)pData;
      assert(self);


      self->pDriv->GetValue(self->pDriv,&fPos);
      sprintf(pBueffel,"WARNING: %s is out of range by %f",self->pName,
              self->fTarget - fPos);
      pExe = GetExecutor();
      pCon = GetExeOwner(pExe);
      if(!self->iWarned)
      {
        if(pCon)
        {
           SCWrite(pCon,pBueffel,eWarning);
        }          
        else
        {
          ServerWriteGlobal(pBueffel,eWarning);
        }
      }
      self->iWarned = 1;  
      return 1;
  }  
/*--------------------------------------------------------------------------*/
  static int ErrPause(void *pData)
  {
      pEVControl self = NULL;
      pExeList   pExe;
      SConnection *pCon = NULL;
      float fPos;
      char pBueffel[256];
      int iRet;
      
      self = (pEVControl)pData;
      assert(self);


      self->pDriv->GetValue(self->pDriv,&fPos);
      sprintf(pBueffel,"WARNING: %s is out of range by %f",self->pName,
              self->fTarget - fPos);
      pExe = GetExecutor();
      pCon = GetExeOwner(pExe);

      if(!self->iWarned)
      {
        if(pCon)
        {
           SCWrite(pCon,pBueffel,eWarning);
        }          
        else
        {
          ServerWriteGlobal(pBueffel,eWarning);
        }
      }
      self->iWarned = 1;  

      
      if(IsCounting(pExe))
      {
           SCWrite(GetExeOwner(pExe),"Pausing till OK",eError);
           PauseExecution(pExe);
      
           /* wait till OK */
           iRet = 0;
           while(iRet == 0)
           {
              SicsWait(5);
              iRet = self->pEnvir->IsInTolerance(self);

           }
           
           /* OK now, continue */
           ContinueExecution(pExe);
           return 1;           
      }
      return 1;
  }    	
/*---------------------------------------------------------------------------*/
  static int ErrInterrupt(void *pData)
  {
      pEVControl self = NULL;
      pExeList   pExe;
      SConnection *pCon = NULL;
      float fPos;
      char pBueffel[256];
      int iRet;
      
      self = (pEVControl)pData;
      assert(self);

      /* report problem */
      self->pDriv->GetValue(self->pDriv,&fPos);
      sprintf(pBueffel,"WARNING: %s is out of range by %f",self->pName,
              self->fTarget - fPos);
      pExe = GetExecutor();
      pCon = GetExeOwner(pExe);
      if(!self->iWarned)
      {
        if(pCon)
        {
           SCWrite(pCon,pBueffel,eWarning);
        }          
        else
        {
          ServerWriteGlobal(pBueffel,eWarning);
        }
      }
      self->iWarned = 1;  

      /* interrupt */
      SetInterrupt((int)ObVal(self->pParam,INTERRUPT));
      return 1;    
  }  
/*---------------------------------------------------------------------------*/
 static int ErrRun(void *pData)
 {
      pEVControl self = NULL;
      pExeList   pExe;
      SConnection *pCon = NULL;
      float fPos;
      char pBueffel[256];
      int iRet;
      
      self = (pEVControl)pData;
      assert(self);

      /* report problem */
      self->pDriv->GetValue(self->pDriv,&fPos);
      sprintf(pBueffel,"WARNING: %s is out of range by %f",self->pName,
              self->fTarget - fPos);
      pExe = GetExecutor();
      pCon = GetExeOwner(pExe);
      if(pCon)
      {
         SCWrite(pCon,pBueffel,eWarning);
         SCWrite(pCon,"Driving to a safe place",eWarning);
      }          
      else
      {
         ServerWriteGlobal(pBueffel,eWarning);
         ServerWriteGlobal("Driving to a safe place",eWarning);
      }

      self->pDriv->SetValue(self->pDriv, ObVal(self->pParam,SAFEVALUE));
      self->eMode = EVIdle;
      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;
        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[10], pBueffel[512];
      pExeList pExe = NULL;
      SConnection *pCon = NULL;
      
      self = (pEVControl)pData;
      assert(self);
      
      
      iRet = self->pDriv->GetValue(self->pDriv, &fPos);
      if( iRet == 1 )
      {
         if(self->iLog)
         {
           VarlogAdd(self->pLog, fPos); 
         }
         fDelta = self->fTarget - fPos;
         if(fDelta < 0.)
         {
           fDelta = -fDelta;
         }
         if(fDelta < ObVal(self->pParam,TOLERANCE))
         {
           self->eMode = EVMonitor;
           if(self->iWarned)
           {
             pExe = GetExecutor();
             pCon = GetExeOwner(pExe);
             sprintf(pBueffel,"Environment device %s back in tolerances again",
                       self->pName);
             if(pCon)
             {
                SCWrite(pCon,pBueffel,eWarning);
             }          
             else
             {
                ServerWriteGlobal(pBueffel,eWarning);
             }
             self->iWarned = 0;
           }
           return 1;
         }
         else
         {
           return 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
      */
         self->pDriv->GetError(self->pDriv, &iCode,pError,9);
         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;
  } 
/*-------- 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;
   
   /* new parameter space */
   pRes->pParam = ObParCreate(7);
   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", 2., usUser);
   ObParInit(pRes->pParam, SAFEVALUE, "safevalue", 0., usUser);
   
   /* local initialisations */
   pRes->pDriv = pDriv;
   iRet = pDriv->Init(pDriv);
   if(iRet >= 0)
   {
     *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;

   /* a terminal error gives a -1 in iRet */
   if(iRet < 0)
   {
      DeleteEVController(pRes);
      return NULL;
   }

   return pRes;
 } 
/*---------------------------------------------------------------------------*/
  void DeleteEVController(void *pData)
  {
      pEVControl self = NULL;
      
      self = (pEVControl)pData;
      assert(self);

      if(self->pDes)
      {
        DeleteDescriptor(self->pDes);
      }  
      if(self->pDrivInt)
      {
        free(self->pDrivInt);
      }
      if(self->pEnvir)
      {
        free(self->pEnvir);
      }
      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);
      }
      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, fVal);
     if(!iRet)
     {
       sprintf(pBueffel,"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);
    
    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;
     char pBueffel[512];
     int iRet;
     
     assert(self);
     assert(pCon);
     
     iRet = ObParSet(self->pParam,self->pName,name,fVal,pCon);
     if(!iRet)
     {
       return iRet;
     }
     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);
     
     sprintf(pBueffel,"Parameter listing for %s",self->pName);
     SCWrite(pCon,pBueffel,eValue);
     
     sprintf(pBueffel,"%s.%s = %f ",self->pName, "tolerance",
             ObVal(self->pParam,TOLERANCE));
     SCWrite(pCon,pBueffel, eValue);        
     sprintf(pBueffel,"%s.%s = %f",self->pName, "access",
             ObVal(self->pParam,ACCESS));
     SCWrite(pCon,pBueffel, eValue);        
     sprintf(pBueffel,"%s.%s = %f",self->pName, "ErrorHandler",
             ObVal(self->pParam,ERRORHANDLER));
     SCWrite(pCon,pBueffel, eValue);        
     sprintf(pBueffel,"%s.%s = %f",self->pName, "interrupt",
             ObVal(self->pParam,INTERRUPT));
     SCWrite(pCon,pBueffel, eValue);        
     sprintf(pBueffel,"%s.%s = %f",self->pName, "UpperLimit",
             ObVal(self->pParam,UPLIMIT));
     SCWrite(pCon,pBueffel, eValue);        
     sprintf(pBueffel,"%s.%s = %f",self->pName, "LowerLimit",
             ObVal(self->pParam,LOWLIMIT));
     SCWrite(pCon,pBueffel, eValue);        
     sprintf(pBueffel,"%s.%s = %f",self->pName, "SafeValue",
             ObVal(self->pParam,SAFEVALUE));
     SCWrite(pCon,pBueffel, eValue);        
     EVCGetPos(self,pCon,&fPos);
     sprintf(pBueffel,"%s.%s = %f",self->pName, "CurrentValue",
             fPos);
     SCWrite(pCon,pBueffel, eValue);        
     sprintf(pBueffel,"%s.%s = %f",self->pName, "TargetValue",
             self->fTarget);
     SCWrite(pCon,pBueffel, eValue);        
             
     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;
      
      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 = %f",self->pName,"CurrentValue", fPos);
           SCWrite(pCon,pBueffel,eValue);
           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;
      }
      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]);
            if(iRet)
            {
               SCSendOK(pCon);
            }                
            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 */
         {
           iRet = EVCGetPar(self,argv[1],&fPos);
           if(!iRet)
           {
              sprintf(pBueffel,"ERROR: parameter %s NOT found",
                      argv[1]);
              SCWrite(pCon,pBueffel,eError);
              return 0;        
           }
           else
           {
             sprintf(pBueffel,"%s.%s = %f",self->pName,argv[1],fPos);
             SCWrite(pCon,pBueffel,eValue);
             return 1;
           }
         }
      }
      else /* try to set parameter */
      {
         iRet = Tcl_GetDouble(pSics->pTcl,argv[2],&dVal);
         if(iRet != TCL_OK)
         {
           sprintf(pBueffel,"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);
         }
         return iRet;
      }
      return 0; /* not reached */
   }                            
/*-------------------------------------------------------------------------
  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;
      pEVDriver  pDriv = NULL;
      char pBueffel[512],pError[132];
      int iRet;
      CommandList *pCom = 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(!pServ->pExecutor)
         {
           SCSendOK(pCon);
           return 1;
         }
         if(isInRunMode(pServ->pExecutor))
         {
            SCWrite(pCon,"ERROR: cannot delete while running",eError);
            return 0;
         }
         EVUnregister(FindEMON(pSics),argv[2]);
         iRet = RemoveCommand(pSics,argv[2]);
         if(!iRet)
         {
           sprintf(pBueffel,"ERROR: %s not found, NOT deleted",argv[2]);
           SCWrite(pCon,pBueffel,eError);
           return 0;
         }
         SCSendOK(pCon);
         return 1;
      }
      else if(strcmp(argv[1],"new") == 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]);
        pCom = FindCommand(pSics,argv[2]);
        if(pCom)
        {
          sprintf(pBueffel,"ERROR: environment device %s already installed, delete first",
                  argv[2]);
          SCWrite(pCon,pBueffel,eError);
          return 0;        
        }
        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 0;
            }
            
        }
        else if(strcmp(argv[3],"tecs") == 0) /* TECS temperature server */
        {
            /* Create a driver */
            pDriv = CreateTecsDriver(argc-4,&argv[4]);
            if(!pDriv)
            {
               SCWrite(pCon,"ERROR: failed to create TECS device driver",eError);
               return 0;
            }
            /* got a driver, initialise everything */
            pNew = CreateEVController(pDriv,argv[2],&iRet);
            if(!pNew)
            {
               SCWrite(pCon,"ERROR: failed to initialize Tecs",eError);
               SCWrite(pCon,"ERROR creating Environment Controller",eError);
               return 0;
            }
            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);
            }
            /* set a few parameters */
            EVCSetPar(pNew,"upperlimit",300.0,pCon);
            EVCSetPar(pNew,"lowerlimit",1.0,pCon);
            /* install command */
            iRet = AddCommand(pSics,argv[2],TecsWrapper,DeleteEVController,
                   pNew);
            if(!iRet)
            {
              sprintf(pBueffel,"ERROR: duplicate command %s not created",argv[2]);
              DeleteEVController((void *)pNew);
              SCWrite(pCon,pBueffel,eError);
              return 0;
            }
            EVRegisterController(FindEMON(pSics),argv[2],pNew, pCon);           
            SCSendOK(pCon);           
            return 1;           
        }
        else if(strcmp(argv[3],"itc4") == 0) /* ITC4 driver */
        {
            /* Create a driver */
            pDriv = CreateITC4Driver(argc-4,&argv[4]);
            if(!pDriv)
            {
               SCWrite(pCon,"ERROR: failed to create ITC4 device driver",eError);
               return 0;
            }
            /* got a driver, initialise everything */
            pNew = CreateEVController(pDriv,argv[2],&iRet);
            if(!pNew)
            {
               SCWrite(pCon,"ERROR creating Environment Controller",eError);
               return 0;
            }
            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);
            }
            /* set a few parameters */
            EVCSetPar(pNew,"upperlimit",300.0,pCon);
            EVCSetPar(pNew,"lowerlimit",1.0,pCon);
            /* install command */
            iRet = AddCommand(pSics,argv[2],ITC4Wrapper,DeleteEVController,
                   pNew);
            if(!iRet)
            {
              sprintf(pBueffel,"ERROR: duplicate command %s not created",argv[2]);
              DeleteEVController((void *)pNew);
              SCWrite(pCon,pBueffel,eError);
              return 0;
            }
            EVRegisterController(FindEMON(pSics),argv[2],pNew, pCon);           
            SCSendOK(pCon);           
            return 1;           
        }
        else if(strcmp(argv[3],"bruker") == 0) /* Bruker Magnet Controller driver */
        {
            /* Create a driver */
            pDriv = CreateBrukerDriver(argc-4,&argv[4]);
            if(!pDriv)
            {
               SCWrite(pCon,"ERROR: failed to create Bruker Controller device driver",eError);
               return 0;
            }
            /* got a driver, initialise everything */
            pNew = CreateEVController(pDriv,argv[2],&iRet);
            if(!pNew)
            {
               SCWrite(pCon,"ERROR creating Environment Controller",eError);
               return 0;
            }
            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);
            }

            /* set a few parameters */
            EVCSetPar(pNew,"upperlimit",45.0,pCon);
            EVCSetPar(pNew,"lowerlimit",0.0,pCon);
            /* install command */
            iRet = AddCommand(pSics,argv[2],BrukerAction,DeleteEVController,
                   pNew);
            if(!iRet)
            {
              sprintf(pBueffel,"ERROR: duplicate command %s not created",argv[2]);
              DeleteEVController((void *)pNew);
              SCWrite(pCon,pBueffel,eError);
              return 0;
            }
            EVRegisterController(FindEMON(pSics),argv[2],pNew, pCon);           
            SCSendOK(pCon);           
            return 1;           
        }
        else if(strcmp(argv[3],"ltc11") == 0) 
                    /* Neocera LTC-11 temperature controller*/
        {
            /* Create a driver */
            pDriv = CreateLTC11Driver(argc-4,&argv[4]);
            if(!pDriv)
            {
               SCWrite(pCon,"ERROR: failed to create LTC-11 device driver",eError);
               return 0;
            }
            /* got a driver, initialise everything */
            pNew = CreateEVController(pDriv,argv[2],&iRet);
            if(!pNew)
            {
               SCWrite(pCon,"ERROR creating Environment Controller",eError);
               return 0;
            }
            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);
            }
            /* set a few parameters */
            EVCSetPar(pNew,"upperlimit",500.,pCon);
            EVCSetPar(pNew,"lowerlimit",1.5,pCon);
            /* install command */
            iRet = AddCommand(pSics,argv[2],LTC11Action,DeleteEVController,
                   pNew);
            if(!iRet)
            {
              sprintf(pBueffel,"ERROR: duplicate command %s not created",argv[2]);
              DeleteEVController((void *)pNew);
              SCWrite(pCon,pBueffel,eError);
              return 0;
            }
            EVRegisterController(FindEMON(pSics),argv[2],pNew, pCon);           
            SCSendOK(pCon);           
            return 1;           
        }
        else if(strcmp(argv[3],"tcl") == 0) /* Tcl driver */
        {
            /* Create a driver */
            pDriv = CreateTclDriver(argc-4,&argv[4],argv[2], pCon);
            if(!pDriv)
            {
               SCWrite(pCon,"ERROR: failed to create TCL device driver",eError);
               return 0;
            }
            /* got a driver, initialise everything */
            pNew = CreateEVController(pDriv,argv[2],&iRet);
            if(!pNew)
            {
               SCWrite(pCon,"ERROR creating Environment Controller",eError);
               DeleteEVDriver(pDriv);
               return 0;
            }
            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],TclEnvironmentWrapper,DeleteEVController,
                   pNew);
            if(!iRet)
            {
              sprintf(pBueffel,"ERROR: duplicate command %s not created",argv[2]);
              DeleteEVController((void *)pNew);
              SCWrite(pCon,pBueffel,eError);
              return 0;
            }
            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));
                             
            /* register controller for monitoring */
            EVRegisterController(FindEMON(pSics),argv[2],pNew, pCon);           
            SCSendOK(pCon);           
            return 1;           
        }
        else if(strcmp(argv[3],"dillu") == 0) /* dillution driver */
        {
            /* Create a driver */
            pDriv = CreateDILLUDriv(argc-4,&argv[4]);
            if(!pDriv)
            {
               SCWrite(pCon,
                 "ERROR: failed to create Dillution device driver",eError);
               return 0;
            }
        }
        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 0;
            }
        }
        else if(strcmp(argv[3],"euro") == 0) /* dillution driver */
        {
            /* Create a driver */
            pDriv = CreateEURODriv(argc-4,&argv[4]);
            if(!pDriv)
            {
               SCWrite(pCon,
                "ERROR: failed to create Eurotherm device driver",eError);
               return 0;
            }
        }
        else if(strcmp(argv[3],"el755") == 0) /* EL755 magnet  driver */
        {
            /* Create a driver */
            pDriv = CreateEL755Driv(argc-4,&argv[4]);
            if(!pDriv)
            {
               SCWrite(pCon,
                "ERROR: failed to create EL755 device driver",eError);
               return 0;
            }
        }
        else
        {
          sprintf(pBueffel,"ERROR: %s not recognized as a valid driver type",
                  argv[3]);
          SCWrite(pCon,pBueffel,eError);
          return 0;        
        }
        /* got a driver, initialise everything */
        pNew = CreateEVController(pDriv,argv[2],&iRet);
        if(!pNew)
        {
          SCWrite(pCon,"ERROR creating Environment Controller",eError);
          return 0;
        }
        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);
        }
        
        /* set a few parameters */
        if(strcmp(argv[3],"euro") == 0)
	{
           EVCSetPar(pNew,"upperlimit",750.0,pCon);
           EVCSetPar(pNew,"lowerlimit",15.0,pCon);
        }
        else if(strcmp(argv[3],"el755") == 0)
	{
           EVCSetPar(pNew,"upperlimit",10.,pCon);
           EVCSetPar(pNew,"lowerlimit",-10.,pCon);
        }
        else
        {
           EVCSetPar(pNew,"upperlimit",4.0,pCon);
           EVCSetPar(pNew,"lowerlimit",0.05,pCon);
        }
        /* install command */
        iRet = AddCommand(pSics,argv[2],EVControlWrapper,DeleteEVController,
                   pNew);
        if(!iRet)
        {
            sprintf(pBueffel,"ERROR: duplicate command %s not created",argv[2]);
            DeleteEVController((void *)pNew);
            SCWrite(pCon,pBueffel,eError);
            return 0;
        }
        EVRegisterController(FindEMON(pSics),argv[2],pNew, pCon);           
        SCSendOK(pCon);           
        return 1;           
      }
      SCWrite(pCon,"ERROR: command not understood",eError);
      return 0;
   }    



