/*---------------------------------------------------------------------------
                         H K L
                         
   Implementation of the SICS object for doing four circle diffractometer
   angle calculations. The actual transformation routines were recoded
   in C from F77 routines provided by J. Allibon, ILL with the MAD
   system.
   
   copyright: see copyright.h
   
   Mark Koennecke, February 1998
   
-----------------------------------------------------------------------------*/
#include <math.h>
#include <ctype.h>
#include "sics.h"
#include "fortify.h"
#include "motor.h"
#include "selector.h"
#include "selvar.h"
#include "hkl.h"
#include "hkl.i"
/*-------------------------------------------------------------------------*/
   static int HKLSave(void *pData, char *name, FILE *fd)
   {
      pHKL self = NULL;

      self = (pHKL)pData;
      if( (self == NULL) || (fd == NULL) )
      {
        return 1;
      }
      fprintf(fd,"#Crystallographic Settings\n");
      fprintf(fd,"%s lambda %f\n",name, self->fLambda);
      fprintf(fd,
      "%s setub %8.6f %8.6f %8.6f %8.6f %8.6f %8.6f %8.6f %8.6f %8.6f\n",
         name, 
         self->fUB[0], self->fUB[1], self->fUB[2], self->fUB[3], self->fUB[4],
         self->fUB[5], self->fUB[6], self->fUB[7], self->fUB[8]);
      return 1;
   }

/*---------------------------------------------------------------------------*/
  pHKL CreateHKL(pMotor pTheta, pMotor pOmega, pMotor pChi, 
                 pMotor pPhi, pMotor pNu)
  {
     pHKL pNew = NULL;
   
     assert(pTheta);
     assert(pOmega);
     assert(pChi);
     assert(pPhi);
     
     /* allocate memory */
     pNew = (pHKL)malloc(sizeof(HKL));
     if(!pNew)
     {
       return NULL;
     }
     memset(pNew,0,sizeof(HKL));
     
     /* create object descriptor */
     pNew->pDes = CreateDescriptor("4-Circle-Calculus");
     if(!pNew->pDes)
     {
       free(pNew);
       return NULL;
     }
     pNew->pDes->SaveStatus =  HKLSave; 

     pNew->pTheta  = pTheta;
     pNew->pOmega  = pOmega;
     pNew->pChi    = pChi;
     pNew->pPhi    = pPhi;
     pNew->pNu     = pNu;
     pNew->fLambda = 1.38;
     pNew->iManual = 1;
     pNew->iQuad   = 1;
     pNew->fUB[0] = 1.;
     pNew->fUB[4] = 1.;
     pNew->fUB[8] = 1.;
     
     return pNew;
  }
/*--------------------------------------------------------------------------*/
  void DeleteHKL(void *pData)
  {
    pHKL self = NULL;
    
    self = (pHKL)pData;
    if(!self)
    {
      return;
    }
    
    if(self->pDes)
    {
      DeleteDescriptor(self->pDes);
    }
    
    free(self);
  }           
/*---------------------------------------------------------------------------*/
  int HKLFactory(SConnection *pCon, SicsInterp *pSics, void *pData,
                 int argc, char *argv[])
  {
     pMotor pTheta = NULL, pOmega = NULL, pChi = NULL, 
            pPhi = NULL, pNu = NULL;
     pHKL self = NULL;
     int iRet;
     
     /* check no of arguments */
     if(argc < 5)
     {
       SCWrite(pCon,"ERROR: Insufficient number of arguments to HKLFactory",
               eError);
       return 0;        
     } 
     
     /* check motors */
     pTheta = FindMotor(pSics,argv[1]);
     if(!pTheta)
     {
       SCWrite(pCon,"ERROR: cannot find two theta motor",eError);
       return 0;
     }
     pOmega = FindMotor(pSics,argv[2]);
     if(!pOmega)
     {
       SCWrite(pCon,"ERROR: cannot find omega motor",eError);
       return 0;
     }
     pPhi = FindMotor(pSics,argv[3]);
     if(!pPhi)
     {
       SCWrite(pCon,"ERROR: cannot find phi motor",eError);
       return 0;
     }
     pChi = FindMotor(pSics,argv[4]);
     if(!pChi)
     {
       SCWrite(pCon,"ERROR: cannot find chi motor",eError);
       return 0;
     }

     if(argc >= 6)
     {
        pNu = FindMotor(pSics,argv[5]);
        if(!pNu)
	{
           SCWrite(pCon,"WARNING: cannot find nu motor",eWarning);
        }
     }
     
     /* make a new structure */
     self = CreateHKL(pTheta, pOmega, pPhi, pChi, pNu);
     if(!self)
     {
       SCWrite(pCon,"ERROR: cannot allocate HKL data structure",eError);
       return 0;
     }
     
     /* install a command */
     iRet = AddCommand(pSics,
                       "HKL",
                       HKLAction,
                       DeleteHKL,
                       self);
      if(!iRet)
      {
        SCWrite(pCon,"ERROR: duplicate command HKL not created",eError);
        DeleteHKL(self);
        return 0;
      }         
      return 1;        
  }           
#include "selvar.i"
/*---------------------------------------------------------------------------*/

  static int HKLCallback(int iEvent, void *pEvent, void *pUser)
  {
    pHKL self = NULL;
    pSelVar pVar = NULL;
    float fVal;
    
    if(iEvent == WLCHANGE)
    {
      self = (pHKL)pUser;
      pVar = (pSelVar)pEvent;
      assert(self);
      assert(pVar);
      
      if(pVar->pCon != NULL)
      {
         fVal = GetSelValue(pVar, pVar->pCon);
         if(fVal > -900.)
         {
           self->fLambda = fVal;
           return 1;
         }
      }          
    }
    return 1;
  }         
/*--------------------------------------------------------------------------*/
  int SetWavelengthVariable(SConnection *pCon, pHKL self, pSelVar pVar)
  {
    pICallBack pCall = NULL, pCall2 = NULL;
    pDummy pDum = NULL;
    float fVal;
    
    assert(pCon);
    assert(self);
    assert(pVar);

    /* try to get callback interface of the new mono variable */
    pDum = (pDummy)pVar;
    pCall2 = pDum->pDescriptor->GetInterface(pDum,CALLBACKINTERFACE);
    if(!pCall2)
    {
      return 0;
    }
    
    /* clear old stuff, if apropriate */
    if(self->pMono)
    {
      pDum = (pDummy)self->pMono;
      pCall = pDum->pDescriptor->GetInterface(pDum,CALLBACKINTERFACE);
      if(pCall)
      {
        RemoveCallback(pCall,self->lID);
        self->lID = 0;
        self->pMono = NULL;
        self->iManual = 1;
      }
    }
    
    /* install new callback */
    self->lID = RegisterCallback(pCall2, 
                                 WLCHANGE,
                                 HKLCallback,
                                 self,
                                 NULL);
    self->pMono =  pVar;
    self->iManual = 0;
    
    /* update the current value */
    fVal = GetSelValue(pVar,pCon);
    if(fVal > -900.)
    {
      self->fLambda = fVal;
    } 
    else
    {
      return 0;
    }    
    return 1;                         
  }  
/*-------------------------------------------------------------------------*/
  int SetWavelengthManual(pHKL self, float fVal)
  {
    pICallBack pCall = NULL;
    pDummy pDum = NULL;
    
    assert(self);

    /* clear old stuff, if apropriate */
    if(self->pMono)
    {
      pDum = (pDummy)self->pMono;
      pCall = pDum->pDescriptor->GetInterface(pDum,CALLBACKINTERFACE);
      if(pCall)
      {
        RemoveCallback(pCall,self->lID);
        self->lID = 0;
        self->pMono = NULL;
        self->iManual = 1;
      }
    }
    
    self->fLambda = fVal;
    return 1;
  }
/*------------------------------------------------------------------------*/
  int GetLambda(pHKL self, float *fVal)
  {
     assert(self);
     
     *fVal = self->fLambda;
     return 1;
  }  
/*-------------------------------------------------------------------------*/
  int GetCurrentHKL(pHKL self, float fHKL[3])
  {
     int i;
     
     assert(self);
     
     for(i = 0; i < 3; i++)
     {
       fHKL[i] = self->fLastHKL[i];
     }
     return 1;
  }  
/*-------------------------------------------------------------------------*/
  int SetUB(pHKL self, float fUB[9])
  {
    int i;
    
    assert(self);
    
    for(i = 0; i < 9; i++)
    {
      self->fUB[i] = fUB[i];
    }
    return 1;
  }    
/*-------------------------------------------------------------------------*/
  int GetUB(pHKL self, float fUB[9])
  {
    int i;
    
    assert(self);
    
    for(i = 0; i < 9; i++)
    {
      fUB[i] = self->fUB[i];
    }
    return 1;
  }    
  
/*--------------------------------------------------------------------------*/
  int SetNOR(pHKL self, int iNOB)
  {
    assert(self);
    
    /* cannot set normal beam geometry if no nu motor */
    if( (iNOB == 1) && (self->pNu == NULL))
       return 0;

    self->iNOR = iNOB;
    return 1;
  }   
/*-------------------------------------------------------------------------*/
     static int hamth[8] = {0,0,1,1,0,0,1,1};
     static int hamom[8] = {0,0,1,1,0,0,1,1};
     static int hamch[8] = {0,1,1,1,1,1,1,0};
     static int hamph[8] = {0,1,0,1,1,0,1,0};
     static int hamhkl[8] = {0,1,0,1,0,1,0,1};
     static const float RD = 57.2957795, pi = 3.1415926;
       
#define ABS(x) (x < 0 ? -(x) : (x))    

/*-------------------------------------------------------------------------*/
  static void rtan(double y, double x, double *rt)
  {
    if( (x == 0.) && (y == 0.) )
    {
      *rt = 0.;
      return;
    }       
    if( x == 0.)
    {
      *rt = pi/2.;
      if(y < 0.)
      {
        *rt = -*rt;
      }
      return;
    }
    if(ABS(y) < ABS(x))
    {
      *rt = atan(ABS(y/x));
      if(x < 0.)
      {
        *rt = pi - *rt;
      }
      if(y < 0.)
      {
          *rt = - *rt;
      }
      return;
    }
    else
    {
      *rt = pi/2 - atan(ABS(x/y));
    }
    if(x < 0.)
    {
      *rt = pi - *rt;
    }
    if( y < 0.)
    {
      *rt = - *rt;
    }
    return;
  }
/*------------------------------------------------------------------------*/
  static double fsign(double a, double a2)
  {
    if(a2 >= 0.)
    {
      return ABS(a);
    }
    else
    {
      return -ABS(a);
    }
  }  
/*------------------------------------------------------------------------*/
  static void sincos(double *sn, double *cs, double *loc)
  {
    if(ABS(*sn) >= 1.)
    {
      *sn = fsign(1.0,*sn);
      *cs = 0.;
      return;
    }
    else
    {
      *cs = sqrt(1. - *sn * *sn);
      return;
    }
  }
/*-------------------------------------------------------------------------*/
  static int ICAL(pHKL self, float fHKL[3], float fPsi, int iHamil, 
                  int iQuad, float fSet[4], float fDelom)
  {
     double z[3],tang[2],r,r1,phi,delomr,t,dstar,v,theta,omega,chi,cosp;
     double sinp,d,q,zer,d2,all,den1,den2,cosps,a,sinc,cosc,b,c,chibi;
     double sinps,sinchi,extrap, psi, loc;
     int i,ip,ivariz,ipara,nh,nhamil,isign,icry,ii;
     
     delomr = fDelom/(RD * -1);
     iHamil -= 1; /* to get iHamil from 1 -- 8 & negative if not requested*/
     psi = fPsi;
     
     /* HKl --> Lab Axis through UB */
     for(i = 0; i < 3; i++)
     {
       ii = 3*i;
       z[i] = self->fUB[ii]*fHKL[0] + self->fUB[ii+1]*fHKL[1] +
                self->fUB[ii+2]*fHKL[2]; 
     }
     
     if(!self->iNOR)
     {
       /* four circle calculation */
       t = z[0]*z[0] + z[1]*z[1] + z[2]*z[2];
       dstar = sqrt(t);
       for(i = 0; i < 3; i++)
       {
         z[i] /= dstar;
       }
       v = self->fLambda *.5 * dstar;
       if(v > 1.)
       {
         return -1;
       }
       theta = 2 * asin(v);
       omega = .5 * theta - delomr;
       
       sinchi = -z[2]/cos(delomr);
       if(ABS(sinchi) > 1.)
       {
         return -1;
       } 
       chi = asin(sinchi);
       
       if(iQuad == 1)
       {
         chi = pi - chi;
         if(chi > pi)
         {
           chi = chi -(2.*pi);
         }
       }
       d = delomr;
       
       q = sin(d);
       r = cos(d) * cos(chi);
       cosp = (q*z[0] + r*z[1])/(q*q + r*r);
       sinp = (z[0] - q*cosp)/r;
       rtan(cosp,sinp,&phi);
       
       chi = -chi;
       
       if(fDelom != 0.)
       {
         /* recalculate psi */
         r1 = -z[2];
         if(r1 == 1.)
         {
           psi = phi;
         } 
         else 
         {
           chibi = asin(r1);
           if(iQuad == 1)
           {
             chibi = pi - chibi;
             if(chibi > pi)
             {
               chibi = chibi -(2*pi);
             }
           }
           sinps = tan(chibi)*tan(delomr);
           cosps = (cos(chibi) -sinps*sin(delomr)*sinchi)/cos(chi);
           rtan(sinps,cosps,&psi);
           psi = -psi;
         }
         psi = RD*psi;
         goto hamil;   
       }
       if(psi != 0.)
       { 
          /* non zero psi */
          a = psi/RD;
          sinps = sin(a);
          cosps = cos(a);
          sinc = sin(chi);
          cosc = cos(chi);
          sinp = sin(phi);
          cosp = cos(phi);
       
          a = sinps*sinp - sinc*cosp*cosps;
          b = -sinps*cosp - sinc*sinp*cosps;
          /* do chi */
          c = sqrt(a*a + b*b);
          d = cosps*cosc;
          rtan(c,d,&chi);
          /* do phi */
          rtan(-b,-a,&phi);
          /* do omega */
          a = -sinps *cosc;
          rtan(a,sinc,&omega);
          omega = omega + theta *.5;
       }   
 hamil:
          theta = theta *RD;
          omega = omega *RD;
          chi   = chi*RD;
          phi   = phi*RD;
          
          if(iHamil >= 0)
          {
            /* hamilton logic */
            if(hamth[iHamil]) theta = theta * -1;
            if(hamom[iHamil]) omega = omega -ABS(theta);
            if(hamph[iHamil])
            {
              phi += 180.;
              if(phi > 180.)
              {
                phi -= 360;
              }
            } 
            if(hamch[iHamil])
            {
              if( (iHamil == 1) || (iHamil == 6) )
              {
                chi *= -1;
              
              }else if((iHamil == 1) || (iHamil == 5))
              {
                chi -= 180.;
                if(chi < -180.)
                {
                  chi += 360.;
                }
              }
              else
              {
                chi = chi* -1 - 180;
                if(chi > 180.)
                { 
                  chi -= 360;
                }
              }
            }
          }              
          
          /* hamhkl left out */
          fSet[0] = theta;
          fSet[1] = omega;
          fSet[2] = chi;
          fSet[3] = phi;
          return 1;

     } /* end four circle */
     else
     {
        /* start normal beam calculation */
        /* ignore psi values */
        d2 = 0.;
        for(i = 0; i < 3; i++)
        {
          d2 += z[i]*z[i];
        }
        d = sqrt(d2);
        d2 *= self->fLambda*self->fLambda;
        
        tang[0] = z[2]*self->fLambda;
        all = tang[0]*tang[0];
        den1 = sqrt(d2-all);
        if(all > 1.)
        {
          return -1;
        }
        den2 = sqrt(1. - all);
        if(!((den1+den2 > 1.) && (ABS(den2-den1) < 1.)))
        {
          return -1;
        }
        
        isign = 1;
        fSet[3] = 0.;
        sincos(&tang[0],&tang[1],&loc);
        fSet[2] = RD*atan2(tang[0],tang[1]);
        tang[1] = (2-d2)/(2*den2);
        sincos(&tang[1],&tang[0],&loc);
        fSet[0] = RD*atan2(tang[0],tang[1])*isign;
        tang[1] = d2/(2*den1);
        sincos(&tang[1],&tang[0],&loc);
        all = atan2(tang[0],tang[1]);
        all -= isign*atan2(z[1],z[0]);
        fSet[1] = 180. - RD*all -90.;
        if(fSet[1] < 0.)
	{
          fSet[1] = 360. + fSet[1];
        }

        return 1;
     }
  }        
/*-------------------------------------------------------------------------*/
  int CalculateSettings(pHKL self, float fHKL[3], float fPsi, int iHamil,
                       float fSet[4], SConnection *pCon)
  {
     int iRet, iSuccess = 0, iRes = 1, iRetry = 1;
     int iQuad = 0;
     int iTest;
     float fDelom = 0.;
     char pError[132];
     char pBueffel[512];
     float fHard;
     float fVal;
     
     /* catch shitty input */
     if( (fHKL[0] == 0.) && (fHKL[1] == 0.) && (fHKL[2] == 0.))
     {
       SCWrite(pCon,"ERROR: I will not calculate angles for HKL = (0,0,0) ",
               eError);
       return 0;         
     }
     
     /* no retries if normal beam calculation or specific Hamilton requested */
     if( (self->iNOR) || (iHamil != 0) )
     {
       iRetry = 0;
     }
     
     /* loop till success */
     while(!iSuccess)
     {
       /* just try it*/
       iRet = ICAL(self,fHKL, fPsi, iHamil, self->iQuad,fSet,fDelom);
       if(iRet < 0 ) /* could not do it */
       {
          sprintf(pBueffel,"ERROR: cannot calculate %4.1f %4.1f %4.1f",
                  fHKL[0], fHKL[1], fHKL[2]);
          SCWrite(pCon,pBueffel,eError);
          return 0;        
       }
       
       /* check two theta  */
       iTest = MotorCheckBoundary(self->pTheta,fSet[0], &fHard,pError,131);
       if(!iTest)
       {
         /* this cannot be fixed */
         sprintf(pBueffel,"ERROR: %4.1f, %4.1f, %4.1f, %5.2f violates two theta limits",
                 fSet[0], fHKL[0], fHKL[1],fHKL[2]);
         SCWrite(pCon,pBueffel,eError);
         return 0;        
       } 
       
       /* check omega */
       iTest = MotorCheckBoundary(self->pOmega,fSet[1], &fHard,pError,131);
       if(!iTest)
       {
         if((iRetry) && (ABS(fPsi) <= 0.) )
         {
            /* try tweaking omega a bit */
            MotorGetPar(self->pOmega,"softupperlim",&fVal);
            if(fSet[1] > fVal) /* upper limit violated */
            {
              fDelom = -(fSet[1] - fVal - .5);
              iRetry = 0; /* do not try a second time */
            }
            MotorGetPar(self->pOmega,"softlowerlim",&fVal);
            if(fSet[1] < fVal)
            {
              fDelom = (fVal +.5) -fSet[1];
              iRetry = 0;
            }
            continue;
         }  
         else
         {
           /* this cannot be fixed */
           sprintf(pBueffel,"ERROR: %4.1f, %4.1f, %4.1f, %5.2f violates omega limits",
                   fSet[1], fHKL[0], fHKL[1],fHKL[2]);
           SCWrite(pCon,pBueffel,eError);
           return 0;        
         }
       }
       
       /* check nu if normal beam */
       if(self->iNOR)
       {
         iTest = MotorCheckBoundary(self->pNu,fSet[2], &fHard,pError,131);
         if(!iTest)
	 {
           sprintf(pBueffel,
       "ERROR: %4.1f, %4.1f, %4.1f, %5.2f %5.2f %5.2f violates nu limits",
                     fHKL[0], fHKL[1],fHKL[2],fSet[0], fSet[1],fSet[2]);
             SCWrite(pCon,pBueffel,eError);
           return 0;
         }
         else
	 {
            return 1;
         }
       }

       /* check chi and phi, but first put into 0-360 degrees */
       if(fSet[2] < 0.0)
       {
          fSet[2] = 360.0 + fSet[2];
       }
       if(fSet[3] < 0.0)
       {
          fSet[3] = 360.0 + fSet[3];
       }
       iTest = MotorCheckBoundary(self->pChi,fSet[2], &fHard,pError,131);
       iTest += MotorCheckBoundary(self->pPhi,fSet[4], &fHard,pError,131);
       if(iTest < 2) /* one of them burns */
       {
           if(iRetry)
           {
             /* try other quadrant */
             if(self->iQuad == 1)
             {
               iQuad = 0;
             }
             else
             {
               iQuad = 1;
             }  
             iRetry = 0;
             continue;
           }
           else
           {
             sprintf(pBueffel,
       "ERROR: %4.1f, %4.1f, %4.1f, %5.2f %5.2f %5.2f %5.2f violates chi, phi limits",
                     fHKL[0], fHKL[1],fHKL[2],fSet[0], fSet[1],fSet[2],fSet[3]);
             SCWrite(pCon,pBueffel,eError);
             return 0;        
             
           }
       }
       iSuccess = 1; /* end loop, we got valid data */
     }
     return 1;
  }          
/*-------------------------------------------------------------------------*/
  int RunHKL(pHKL self, float fHKL[3], 
             float fPsi, int iHamil, SConnection  *pCon)
  {
     float fSet[4];
     int iRet,i;
     char pBueffel[512];
     pDummy pDum;

     assert(self);
     iRet = CalculateSettings(self,fHKL,fPsi,iHamil,fSet,pCon);
     if(!iRet)
     {
       SCWrite(pCon,"ERROR: NOT started",eError);
       return 0;
     }

     /* start all the motors */
     pDum = (pDummy)self->pTheta;
     iRet = StartDevice(pServ->pExecutor, "HKL",
                        pDum->pDescriptor, pDum, pCon,fSet[0]);
     if(!iRet)
     {
        SCWrite(pCon,"ERROR: cannot start two theta motor",eError);
        StopExe(pServ->pExecutor,"all");
        return 0;
     }           
     
     pDum = (pDummy)self->pOmega;
     iRet = StartDevice(pServ->pExecutor, "HKL",
                        pDum->pDescriptor, pDum, pCon,fSet[1]);
     if(!iRet)
     {
        SCWrite(pCon,"ERROR: cannot start omega motor",eError);
        StopExe(pServ->pExecutor,"all");
        return 0;
     }           

     /* special case: normal beam */
     if(self->iNOR)
     {
        pDum = (pDummy)self->pNu;
        iRet = StartDevice(pServ->pExecutor, "HKL",
                        pDum->pDescriptor, pDum, pCon,fSet[2]);
        if(!iRet)
        {
            StopExe(pServ->pExecutor,"all");
            SCWrite(pCon,"ERROR: cannot start nu motor",eError);
            return 0;
        }
        else
	{
           for(i = 0; i < 3; i++)
           {
               self->fLastHKL[i] = fHKL[i];
           }               
           return 1;
        }           
     }

     pDum = (pDummy)self->pChi;
     iRet = StartDevice(pServ->pExecutor, "HKL",
                        pDum->pDescriptor, pDum, pCon,fSet[2]);
     if(!iRet)
     {
        SCWrite(pCon,"ERROR: cannot start chi motor",eError);
        StopExe(pServ->pExecutor,"all");
        return 0;
     }           
     pDum = (pDummy)self->pPhi;
     iRet = StartDevice(pServ->pExecutor, "HKL",
                        pDum->pDescriptor, pDum, pCon,fSet[3]);
     if(!iRet)
     {
        SCWrite(pCon,"ERROR: cannot start phi",eError);
        StopExe(pServ->pExecutor,"all");
        return 0;
     }        
     for(i = 0; i < 3; i++)
     {
       self->fLastHKL[i] = fHKL[i];
     }    
     return 1;
             
  }        
/*-------------------------------------------------------------------------*/
  int DriveSettings(pHKL self, float fSet[4], SConnection *pCon)
  {
     int iRet,i, iReturn;
     char pBueffel[512];
     pDummy pDum;

     iReturn = 1;

     /* start all the motors */
     pDum = (pDummy)self->pTheta;
     iRet = StartDevice(pServ->pExecutor, "HKL",
                        pDum->pDescriptor, pDum, pCon,fSet[0]);
     if(!iRet)
     {
        SCWrite(pCon,"ERROR: cannot start two theta motor",eError);
        StopExe(pServ->pExecutor,"all");
        iReturn = 0;
        goto ente;
     }           
     
     pDum = (pDummy)self->pOmega;
     iRet = StartDevice(pServ->pExecutor, "HKL",
                        pDum->pDescriptor, pDum, pCon,fSet[1]);
     if(!iRet)
     {
        SCWrite(pCon,"ERROR: cannot start omega motor",eError);
        StopExe(pServ->pExecutor,"all");
        iReturn = 0;
        goto ente;
     }           

     /* special case: normal beam */
     if(self->iNOR)
     {
        pDum = (pDummy)self->pNu;
        iRet = StartDevice(pServ->pExecutor, "HKL",
                        pDum->pDescriptor, pDum, pCon,fSet[2]);
        if(!iRet)
        {
            SCWrite(pCon,"ERROR: cannot start nu motor",eError);
            StopExe(pServ->pExecutor,"all");
            iReturn = 0;
        }
        goto ente;
     }

     pDum = (pDummy)self->pChi;
     iRet = StartDevice(pServ->pExecutor, "HKL",
                        pDum->pDescriptor, pDum, pCon,fSet[2]);
     if(!iRet)
     {
        SCWrite(pCon,"ERROR: cannot start chi motor",eError);
        StopExe(pServ->pExecutor,"all");
        iReturn = 0;
        goto ente;
     }           
     pDum = (pDummy)self->pPhi;
     iRet = StartDevice(pServ->pExecutor, "HKL",
                        pDum->pDescriptor, pDum, pCon,fSet[3]);
     if(!iRet)
     {
        StopExe(pServ->pExecutor,"all");
        iReturn = 0;
        SCWrite(pCon,"ERROR: cannot start phi",eError);
     }        

ente:
      /* wait for end */
      iRet = Wait4Success(pServ->pExecutor);
      switch(iRet)
      {
         case DEVINT:
                 if(SCGetInterrupt(pCon) == eAbortOperation)
                 {
                       SCSetInterrupt(pCon,eContinue);
                       SCSetError(pCon,OKOK);
                       SCWrite(pCon,"Driving to HKL Aborted",eStatus);
                 }
                 return 0;
                 break;
          case DEVDONE:
                 SCWrite(pCon,"Driving to Reflection done",eStatus);
                 break;
          default:
                 SCWrite(pCon,"WARNING: driving to HKL finished with problems",
                         eWarning);
                 if(SCGetInterrupt(pCon) != eContinue)
		 {
                    return 0;
                 }
                 break;                          
     }   
     return iReturn;
  }    
/*--------------------------------------------------------------------------*/
  int DriveHKL(pHKL self, float fHKL[3], 
             float fPsi, int iHamil, SConnection  *pCon)
  {
      int iRet, iReturn;
      long lID;
      char pBueffel[132];
      
      assert(self);
      
      /* start running */
      iReturn = 1;
      iRet = RunHKL(self,fHKL,fPsi,iHamil,pCon);
      if(!iRet)
      {
        StopExe(pServ->pExecutor,"all");
        iReturn = 0;
      }    

      /* wait for end */
      iRet = Wait4Success(pServ->pExecutor);
      switch(iRet)
      {
         case DEVINT:
                 if(SCGetInterrupt(pCon) == eAbortOperation)
                 {
                       SCSetInterrupt(pCon,eContinue);
                       SCSetError(pCon,OKOK);
                       SCWrite(pCon,"Driving to HKL Aborted",eStatus);
                 }
                 return 0;
                 break;
          case DEVDONE:
                 sprintf(pBueffel,"Driving to %3.1f %3.1f %3.1f done",
                         fHKL[0], fHKL[1], fHKL[2]);
                 SCWrite(pCon,pBueffel,eStatus);
                 break;
          default:
                 SCWrite(pCon,"WARNING: driving to HKL finished with problems",
                         eWarning);
                 if(SCGetInterrupt(pCon) != eContinue)
		 {
                    return 0;
                 }
                 break;                          
      }   
      return iReturn;
  }    
/*-----------------------------------------------------------------------*/
  int GetCurrentPosition(pHKL self, SConnection *pCon, float fPos[4])
  {
     float fVal;
     int iRet, iResult;

     assert(self);
     assert(pCon);
     
     iResult = 1;
     iRet = MotorGetSoftPosition(self->pTheta,pCon, &fVal);
     if(iRet == 1)
     {
        fPos[0] = fVal;
     }
     else
     {
       iResult = 0;
     }
     iRet = MotorGetSoftPosition(self->pOmega,pCon, &fVal);
     if(iRet == 1)
     {
        fPos[1] = fVal;
     }
     else
     {
       iResult = 0;
     }

     /* normal beam geometry */
     if(self->iNOR == 1)
     {
        iRet = MotorGetSoftPosition(self->pNu,pCon, &fVal);
        if(iRet == 1)
        {
            fPos[2] = fVal;
        }
        else
        {
          iResult = 0;
        }
        return iResult;
     }

     /* bissecting geometry */
     iRet = MotorGetSoftPosition(self->pChi,pCon, &fVal);
     if(iRet == 1)
     {
        fPos[2] = fVal;
     }
     else
     {
       iResult = 0;
     }
     iRet = MotorGetSoftPosition(self->pPhi,pCon, &fVal);
     if(iRet == 1)
     {
        fPos[3] = fVal;
     }
     else
     {
       iResult = 0;
     }

     return iResult;
  }
/*--------------------------------------------------------------------------*/
  int isNumeric(char *pText)
  {
    int i, ii, iGood;
    static char pNum[13] = {"1234567890.+-"};
    
    for(i = 0; i < strlen(pText); i++)
    {
      for(ii = 0; ii < 13; ii++)
      {
        iGood = 0;
        if(pText[i] == pNum[ii])
        {
          iGood = 1;
          break;
        }
      }
      if(!iGood)
      {
          return 0;
      }
    }    
    return 1;
  }  
                                  
/*--------------------------------------------------------------------------*/
  static int GetCommandData(int argc, char *argv[], float fHKL[3], 
                            float *fPsi, int *iHamil, SConnection *pCon)
  {
     int iRet, i;
     double d;
     char pBueffel[512];
     
     if(argc < 3)
     {
        SCWrite(pCon,
              "ERROR: Insufficient reflection data specified for calculation",
              eError);
        return 0;      
     }

     /* get the reflection */
     for(i = 0; i < 3; i++)
     {
        if(!isNumeric(argv[i]))
        {
           sprintf(pBueffel,"ERROR: %s is NOT recognized as a number",argv[i]);
           SCWrite(pCon,pBueffel,eError);
           return 0;
        }
        fHKL[i] = atof(argv[i]);
     }
     
     *fPsi = 0.;
     *iHamil = 0;
     
     /* has psi been specicifed ? */
     if(argc > 3)
     {
        if(!isNumeric(argv[3]))
        {
           sprintf(pBueffel,"ERROR: %s is NOT recognized as a number",argv[3]);
           SCWrite(pCon,pBueffel,eError);
        }
        *fPsi = atof(argv[3]);
     }
     
     /* has iHamil been specified ? */
     if(argc > 4)
     {
        if(!isNumeric(argv[4]))
        {
           sprintf(pBueffel,"ERROR: %s is NOT recognized as a number",argv[4]);
           SCWrite(pCon,pBueffel,eError);
        }
        *iHamil = atof(argv[4]);
     }
     return 1;
  }
/*--------------------------------------------------------------------------*/
  int HKLAction(SConnection *pCon, SicsInterp *pSics, void *pData,
                int argc, char *argv[])
  {
     int iRet,i, iHamil;
     char pBueffel[512];
     float fUB[9], fPsi, fVal;
     float fHKL[3], fSet[4];
     pHKL self = NULL;
     CommandList *pCom = NULL;
     pDummy pDum = NULL;
     
     assert(pCon);
     assert(pSics);
     
     self = (pHKL)pData;
     assert(self);
     
     /* enough arguments ? */
     if(argc < 2)
     {
       SCWrite(pCon,"Insufficient number of arguments to HKL",eError);
       return 0;       
     }
     
/*-------- handle list */
     strtolower(argv[1]);
     if(strcmp(argv[1],"list") == 0 )
     {
        sprintf(pBueffel,"lambda = %f   Normal Beam = %d  Quadrant = %d", 
                self->fLambda, self->iNOR, self->iQuad);
        SCWrite(pCon,pBueffel,eValue);
        sprintf(pBueffel,"UB = { %f %f %f",
                self->fUB[0], self->fUB[1],self->fUB[2]);
        SCWrite(pCon,pBueffel,eValue);
        sprintf(pBueffel,"       %f %f %f",
                self->fUB[3], self->fUB[4],self->fUB[5]);
        SCWrite(pCon,pBueffel,eValue);
        sprintf(pBueffel,"       %f %f %f }",
                self->fUB[6], self->fUB[7],self->fUB[8]);
        SCWrite(pCon,pBueffel,eValue);
        return 1;                        
        sprintf(pBueffel,"Last HKL:  %f %f %f ",
                self->fLastHKL[0], self->fLastHKL[1],self->fLastHKL[2]);
        SCWrite(pCon,pBueffel,eValue);
        return 1;                        
     } 
/*----------- current */     
     else if(strcmp(argv[1],"current") == 0)
     {
        sprintf(pBueffel,"Last HKL:  %f %f %f ",
                self->fLastHKL[0], self->fLastHKL[1],self->fLastHKL[2]);
        SCWrite(pCon,pBueffel,eValue);
        return 1;                        
     }
/*------------- lambda */
     else if(strcmp(argv[1],"lambda") == 0)
     {
        if(argc < 3)
        {
          SCWrite(pCon,"ERROR: Insufficient number of arguments to HKL lambda",eError);
          return 0;       
        }
        if(!SCMatchRights(pCon,usUser))
        {
          return 0;
        }
        if(!isNumeric(argv[2]))
        {
          sprintf(pBueffel,"ERROR: %s was not recognized as a number", argv[2]);
          SCWrite(pCon,pBueffel,eError);
          return 0;
        }
        fVal = atof(argv[2]);
        iRet = SetWavelengthManual(self,fVal);
        if(!iRet)
        {
          SCWrite(pCon,"ERROR: cannot set wavelength",eError);
          return 0; 
        }
        SCSendOK(pCon);
        return 1;
     }
/*------- lambdavar*/
     else if(strcmp(argv[1],"lambdavar") == 0)
     {
        if(argc < 3)
        {
          SCWrite(pCon,"ERROR: Insufficient number of arguments to HKL lambdavar",eError);
          return 0;       
        }
        if(!SCMatchRights(pCon,usUser))
        {
          return 0;
        }
        pCom = FindCommand(pSics,argv[2]);
        if(!pCom)
        {
          sprintf(pBueffel,"ERROR: cannot find variable --> %s <--",argv[2]);
          SCWrite(pCon,pBueffel,eError);
          return 0;
        }
        pDum = (pDummy)pCom->pData;
        if(!pDum)
        {
          sprintf(pBueffel,"ERROR: cannot find variable --> %s <--",argv[2]);
          SCWrite(pCon,pBueffel,eError);
          return 0;
        }
        if(strcmp(pDum->pDescriptor->name,"SicsSelVar") != 0)
        {
          sprintf(pBueffel,"ERROR: variable --> %s <-- has nothing to do with wavelength",argv[2]);
          SCWrite(pCon,pBueffel,eError);
          return 0;
        }
        iRet = SetWavelengthVariable(pCon,self,(pSelVar)pDum);
        if(!iRet)
        {
          SCWrite(pCon,"ERROR: cannot set wavelength variable",eError);
          return 0; 
        }
        SCSendOK(pCon);
        return 1;
     }
/*------------ UB */
     else if(strcmp(argv[1],"setub") == 0)
     {
       if(argc < 11)
       {
          SCWrite(pCon,"ERROR: Insufficient number of arguments to HKL setUB",eError);
          return 0;       
       }
       if(!SCMatchRights(pCon,usUser))
       {
          return 0;
       }
       for(i = 2; i < 11; i++)
       {
          if(!isNumeric(argv[i]))
          {
            sprintf(pBueffel,"ERROR: %s was not recognized as a number", argv[i]);
            SCWrite(pCon,pBueffel,eError);
            return 0;
          }
          fUB[i-2] = atof(argv[i]);
       }
       iRet = SetUB(self,fUB);
       if(!iRet)
       {
          SCWrite(pCon,"ERROR: cannot set UB Matrix",eError);
          return 0; 
       }
        SCSendOK(pCon);
        return 1;
     }
/*------------- normal beam */
     else if(strcmp(argv[1],"nb") == 0)
     {
        if(argc < 3)
        {
          SCWrite(pCon,"ERROR: Insufficient number of arguments to HKL nb",eError);
          return 0;       
        }
        if(!SCMatchRights(pCon,usUser))
        {
          return 0;
        }
        if(!isNumeric(argv[2]))
        {
          sprintf(pBueffel,"ERROR: %s was not recognized as a number", argv[2]);
          SCWrite(pCon,pBueffel,eError);
          return 0;
        }
        iRet = SetNOR(self,atoi(argv[2])); 
        if(!iRet)
        {
           SCWrite(pCon,
        "ERROR: cannot set Normal Beam geometry, probably nu motor undefined",
             eError);
           return 0; 
        }
        SCSendOK(pCon);
        return 1;
     }
/*------------- quadrant  */
     else if(strcmp(argv[1],"quadrant") == 0)
     {
        if(argc < 3)
        {
          SCWrite(pCon,"ERROR: Insufficient number of arguments to HKL quadrant",eError);
          return 0;       
        }
        if(!SCMatchRights(pCon,usUser))
        {
          return 0;
        }
        if(!isNumeric(argv[2]))
        {
          sprintf(pBueffel,"ERROR: %s was not recognized as a number", argv[2]);
          SCWrite(pCon,pBueffel,eError);
          return 0;
        }
        iRet = atoi(argv[2]);
        if(!( (iRet == 1) || (iRet == 0)) )
        {
          sprintf(pBueffel,"ERROR: Invalid parameter %d for quadrant, posiible: 0,1",
                  iRet);
          SCWrite(pCon,pBueffel,eError);
          return 0;        
        }
        self->iQuad = iRet;
        SCSendOK(pCon);
        return 1;
     }
/*------------- calculate */     
     else if(strcmp(argv[1],"calc") == 0)
     {
       iRet = GetCommandData(argc-2,&argv[2],fHKL, &fPsi, &iHamil,pCon);
       if(!iRet)
       { 
         return 0;
       }
       iRet = CalculateSettings(self,fHKL, fPsi, iHamil, fSet,pCon);
       if(!iRet)
       {
         return 0;
       }
       sprintf(pBueffel,"{ theta = %f, omega = %f, chi = %f, phi = %f",
               fSet[0], fSet[1], fSet[2],fSet[3]);
       SCWrite(pCon,pBueffel,eValue);
       return 1;        
     }
/*------------------ run */
     else if(strcmp(argv[1],"run") == 0)
     {
       if(!SCMatchRights(pCon,usUser))
       {
          return 0;
       }
       iRet = GetCommandData(argc-2,&argv[2],fHKL, &fPsi, &iHamil,pCon);
       if(!iRet)
       { 
         return 0;
       }
       iRet = RunHKL(self,fHKL,fPsi, iHamil, pCon);
       if(!iRet)
       {
         return 0;
       }
       else
       {
         SCSendOK(pCon);
         return 1;
       }
     }
/*------------------ drive */
     else if(strcmp(argv[1],"drive") == 0)
     {
       if(!SCMatchRights(pCon,usUser))
       {
          return 0;
       }
       iRet = GetCommandData(argc-2,&argv[2],fHKL, &fPsi, &iHamil,pCon);
       if(!iRet)
       { 
         return 0;
       }
       iRet = DriveHKL(self,fHKL,fPsi, iHamil, pCon);
       if(!iRet)
       {
         return 0;
       }
       else
       {
         SCSendOK(pCon);
         return 1;
       }
     }
     else
     {
       sprintf(pBueffel,"ERROR: subcommand --> %s <-- not recognized",
               argv[1]);
       SCWrite(pCon,pBueffel,eError);
       return 0;        
     }         
     return 0; /* not reached */ 
  }               





