/*
**   motorInit.c
**   ===========
**
**  Program to initialise a motor record from an encoder at ioc boot time.
**
**  Note that, under EPICS 3.14, there are problems with ca_context_destroy
**  if any of the channels could not be connected, e.g. if the encoder is
**  on a remote IOC which is "down". In such a case, motorInit will not exit
**  but go into an idle loop. For this reason, the program has been written
**  so that it is reentrant, i.e. it has no global variables.
**
** Usage:
**   On host:
**              cc -c ... motorInit.c
**   On ioc:
**              ld < motorInit.o
**              motorInit, "motor", "encoder"
**
** Repository:
**  $Source: /cvs/G/DRV/motor/EPICS-R3-14/motorR6-2-2/motorApp/snl/motorInit.c,v $
**
**  $Revision: 1.7 $   $Date: 2008/05/06 11:44:48 $
**
** +--------------------------------------------------------------+
** |                  Paul Scherrer Institute                     |
** |                SLS Controls Software Group                   |
** |                                                              |
** | This software may be used freely by non-profit organizations.|
** | It may be copied provided that the name of PSI and of the    |
** | author is included. Neither PSI nor the author assume any    |
** | responsibility for the use of this software outside of PSI.  |
** +--------------------------------------------------------------+
*/

#ifdef vxWorks
  #include <vxWorks.h>
#else
  #define   OK    0
  #define   ERROR 1
#endif
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef vxWorks
  #include <taskLib.h>
  #include <sysLib.h>
  static void epicsThreadSleep (double secs) {
    taskDelay ((int)(sysClkRateGet () * secs));
  }
#else
  #include <epicsThread.h>
#endif

#include <cadef.h>
#include <errlog.h>

#define DESTROY  {if (pVar->connectingCounter > 0) { \
                    printf ("motorInit: pending connection -- cannot exit!\n"); \
                    motorInitSuspend (pVar); \
                  } \
                  if (pVar->debug) printf ("Calling ca_context_destroy ...\n"); \
                  ca_context_destroy (); \
                  return ERROR;}
/*                                 Function Prototypes
*/
  struct UserVar {
    int  connectingCounter;
    int  debug;
  };

  static void caConnectCallback (
                       struct connection_handler_args args);
  static void motorInitSuspend (
                       struct UserVar *pVar);
  static int  myGetw (
                       char  *chanName,
                       chid   chId,
                       chtype type,
                       void  *pVal,
                       double secs,
                       int    debug);
  static int  myPutw (
                       char  *chanName,
                       chid   chId,
                       chtype type,
                       void  *pVal,
                       double secs,
                       int    debug);
  static int  mySearchw (
                       char           *chanName,
                       chid           *pChId,
                       double          secs,
                       struct UserVar *pVar);
  static int  startCaSearch (
                       char           *chanName,
                       chid           *pChId,
                       struct UserVar *pVar);
  static int  waitAllConnected (
                       double          secs,
                       struct UserVar *pVar);
/*--------------------------------------------------------------------------------
**    motorInitSuspend --  Simply do nothing forever, unless connectingCounter
**                         should happen to go to zero, in which case we can
**                         return.
*/
  static void motorInitSuspend (
/*            ================
*/                struct UserVar   *pVar) {

    while (pVar->connectingCounter > 0) {
      epicsThreadSleep (1);
    }
  }
/*--------------------------------------------------------------------------------
*/
  static void caConnectCallback (
/*            =================
*/                  struct connection_handler_args myArgs) {  /* The arguments */

    struct UserVar   *pVar;

    if (myArgs.op != CA_OP_CONN_UP) {
      errlogPrintf ("caConnectCallback: Connection failed, probably!\n");
    } else {
      pVar = (struct UserVar *) ca_puser (myArgs.chid);
      if (pVar->debug) errlogPrintf ("caConnectCallback: Connection up.\n");
      pVar->connectingCounter--;
    }
  }
/*--------------------------------------------------------------------------------
*/
  static int myGetw (
/*           ======
*/                  char  *chanName,    /* Name of channel to be read */
                    chid   chId,        /* Channel ID of channel */
                    chtype type,        /* Type of variable to be returned */
                    void  *pVal,        /* Pointer to value to be returned */
                    double secs,        /* Time-out in secs */
                    int    debug) {     /* If non-zero, be verbose */

/*  Read a value from an EPICS channel and wait till we've got it.
*/
    int  status;

    if (debug) printf ("Reading %s, type %s ...\n", chanName, dbr_type_to_text (type));

    status = ca_get (type, chId, pVal);
    if (status != ECA_NORMAL) {
      errlogPrintf ("Error return from ca_get of %s: %d\n", chanName, status);
      return ERROR;
    }
    status = ca_pend_io (secs);
    switch (status) {
      case ECA_NORMAL:
        if (debug) printf ("ca_get OK.\n");
        return OK;
      case ECA_TIMEOUT:
        errlogPrintf ("Time-out from ca_pend_io for ca_get from channel %s\n", chanName);
        return ERROR;
      default:
        errlogPrintf ("Error return from ca_pend_io for ca_get from channel %s: %d\n", chanName, status);
        return ERROR;
    }
    return OK;
  }
/*--------------------------------------------------------------------------------
*/
  static int myPutw (
/*           ======
*/                  char  *chanName,    /* Name of channel to be read */
                    chid   chId,        /* Channel ID of channel */
                    chtype type,        /* Type of variable to be written */
                    void  *pVal,        /* Pointer to value to be written */
                    double secs,        /* Time-out in secs */
                    int    debug) {     /* If non-zero, be verbose */

/*  Read a value from an EPICS channel and wait till we've got it.
*/
    int   status, i;
    char *pChar;

    if (debug) {
      printf ("Writing %s, type %s\n", chanName, dbr_type_to_text (type));
      pChar = (char *) pVal;
      for (i = 0; i < 8; i++) printf (" %d", pChar[i]);
      printf ("\n");
    }

    status = ca_put (type, chId, pVal);
    if (status != ECA_NORMAL) {
      errlogPrintf ("Error return from ca_put of %s: %d\n", chanName, status);
      return ERROR;
    }
    status = ca_pend_io (secs);
    switch (status) {
      case ECA_NORMAL:
        if (debug) printf ("ca_put OK.\n");
        return OK;
      case ECA_TIMEOUT:
        errlogPrintf ("Time-out from ca_pend_io for ca_put to channel %s\n", chanName);
        return ERROR;
      default:
        errlogPrintf ("Error return from ca_pend_io for ca_put to channel %s: %d\n", chanName, status);
        return ERROR;
    }
    return OK;
  }
/*--------------------------------------------------------------------------------
*/
  static int mySearchw (
/*           =========
*/            char           *chanName,    /* Name of channel to be connected */
              chid           *pChId,       /* Channel ID to be returned */
              double          secs,        /* Time-out in secs */
              struct UserVar *pVar) {      /* User data structure */

/*  Search for an EPICS channel and wait until we have
**  connected to it.
*/
    int  status;

    if (pVar->debug) printf ("mySearchw: %s ...\n", chanName);
    status = startCaSearch (chanName, pChId, pVar);
    if (status != OK) {
      errlogPrintf ("Error return from startCaSearch for %s: %d\n", chanName, status);
      return ERROR;
    }
    if (pVar->debug) printf ("mySearchw: waiting for connection to complete ...\n");
    if (waitAllConnected (secs, pVar) != OK) return ERROR;
    return OK;
  }
/*--------------------------------------------------------------------------------
*/
  static int startCaSearch (
/*           =============
*/                  char           *chanName,
                    chid           *pChId,
                    struct UserVar *pVar) {
    int  status;

    if (pVar->debug) printf ("Connecting to %s\n", chanName);
    status = ca_create_channel (chanName, caConnectCallback, pVar, 0, pChId);
    if (status != ECA_NORMAL) {
      errlogPrintf ("Error return from ca_search of %s: %d\n", chanName, status);
      return ERROR;
    }
    pVar->connectingCounter++;
    return OK;
  }
/*--------------------------------------------------------------------------------
*/
  static int waitAllConnected (
/*           ================
*/                double          secs,
                  struct UserVar *pVar) {

    if (secs <= 0.0) secs = 5.0;
    while (pVar->connectingCounter & (secs >= 0)) {
      epicsThreadSleep (0.1);
      secs = secs - 0.1;
    }
    if (pVar->connectingCounter) {
      errlogPrintf ("Time-out waiting for connection(s) to be established.\n");
      return ERROR;
    }
    return OK;
  }
/*--------------------------------------------------------------------------------
*/
  int motorInit (
/*    =========
*/                char *motor,
                  char *encoder,
                  int   debug) {

    char            *usage = "Usage: motorInit \"motor\", \"encoder\" [, debug]";
    char             myMotor[64], myEncoder[64], *pDot;
    int              status, cntr;
    size_t           rdblLen;
    double           tmo = 5.0;
    epicsInt32       foff_save = 0, able_save = 0;
    double           setVal;
    struct UserVar  *pVar;
                          /*-------------------------------------------------------
                          ** Here are the channel id's of all the channels we need.
                          */
    chid     ca_ENC_UDF;          /* != 0 if encoder value undefined */
    epicsInt32  ENC_UDF = 0;
    char        ENC_UDF_name[64];

    chid     ca_ENC_VAL;          /* The encoder value */
    double      ENC_VAL;
    char        ENC_VAL_name[64];

    chid     ca_MOT_RRES;         /* Motor "Readback resolution" */
    double      MOT_RRES;
    char        MOT_RRES_name[64];

    chid     ca_MOT_DMOV;         /* DMOV == 1 if motor finished moving */
    epicsInt32  MOT_DMOV = 0;
    char        MOT_DMOV_name[64];

    chid     ca_MOT_URIP;         /* Motor "Use Readout If Present" */
    epicsInt32  MOT_URIP = 0;
    char        MOT_URIP_name[64];

    chid     ca_MOT_RDBL;         /* Motor Readback Link */
    char        MOT_RDBL[64];
    char        MOT_RDBL_name[64];

    chid     ca_MOT_SSET;         /* Motor "Set SET Mode" */
    epicsInt32  MOT_SSET = 0;
    char        MOT_SSET_name[64];

    chid     ca_MOT_DVAL;         /* Motor "Dial Value" */
    double      MOT_DVAL;
    char        MOT_DVAL_name[64];

    chid     ca_MOT_DLLM;         /* Motor Dial Low Limit */
    double      MOT_DLLM;
    char        MOT_DLLM_name[64];

    chid     ca_MOT_DHLM;         /* Motor Dial High Limit */
    double      MOT_DHLM;
    char        MOT_DHLM_name[64];

    chid     ca_MOT_SUSE;         /* Motor "Set USE Mode" */
    epicsInt32  MOT_SUSE = 0;
    char        MOT_SUSE_name[64];

    chid     ca_MOT_FOFF;         /* Motor "Offset-Freeze" if == 1 */
    epicsInt32  MOT_FOFF = 0;
    char        MOT_FOFF_name[64];

    chid     ca_MOT_ABLE;         /* Motor "Enable/Disable" */
    epicsInt32  MOT_ABLE = 0;
    char        MOT_ABLE_name[64];
                          /*-------------------------------------------------------
                          */
    pVar = calloc (1, sizeof (struct UserVar));
    if (debug) printf ("Address of connectingCounter = %p\n", &pVar->connectingCounter);

    pVar->debug             = debug;
    pVar->connectingCounter = 0;

    if ((motor   == NULL) ||
        (encoder == NULL)) {
      printf ("%s\n", usage);
      return ERROR;
    }
    if (((strlen (motor)   < 2) || (strlen (motor)   > 40)) ||
        ((strlen (encoder) < 2) || (strlen (encoder) > 40))) {
      printf ("%s\n", usage);
      return ERROR;
    }

    strcpy (myMotor, motor);
    pDot = strchr (myMotor, '.');
    if (pDot) {
      *pDot = '\000';
      printf ("Motor name has been truncated to \"%s\".\n", myMotor);
    }

    strcpy (myEncoder, encoder);
    pDot = strchr (myEncoder, '.');
    if (pDot) {
      *pDot = '\000';
      printf ("Encoder name has been truncated to \"%s\".\n", myEncoder);
    }

    status = ca_context_create (ca_enable_preemptive_callback);
    if (status != ECA_NORMAL) {
      errlogPrintf ("Error return from ca_context_create: %d", status);
      return ERROR;
    }
    printf ("Synchronising %s and %s ...\n", myMotor, encoder);

    sprintf (ENC_UDF_name,  "%.58s.UDF",  encoder);
    sprintf (ENC_VAL_name,  "%.58s",      encoder);
    sprintf (MOT_RRES_name, "%.58s.RRES", myMotor);
    sprintf (MOT_DMOV_name, "%.58s.DMOV", myMotor);
    sprintf (MOT_URIP_name, "%.58s.URIP", myMotor);
    sprintf (MOT_RDBL_name, "%.58s.RDBL", myMotor);
    sprintf (MOT_SSET_name, "%.58s.SSET", myMotor);
    sprintf (MOT_DVAL_name, "%.58s.DVAL", myMotor);
    sprintf (MOT_DLLM_name, "%.58s.DLLM", myMotor);
    sprintf (MOT_DHLM_name, "%.58s.DHLM", myMotor);
    sprintf (MOT_SUSE_name, "%.58s.SUSE", myMotor);
    sprintf (MOT_FOFF_name, "%.58s.FOFF", myMotor);
    sprintf (MOT_ABLE_name, "%.58s_able", myMotor);
    if (debug) {
      printf ("Channels to be used are:\n");
      printf ("  %s\n", ENC_UDF_name);
      printf ("  %s\n", ENC_VAL_name);
      printf ("  %s\n", MOT_RRES_name);
      printf ("  %s\n", MOT_DMOV_name);
      printf ("  %s\n", MOT_URIP_name);
      printf ("  %s\n", MOT_RDBL_name);
      printf ("  %s\n", MOT_SSET_name);
      printf ("  %s\n", MOT_DVAL_name);
      printf ("  %s\n", MOT_DLLM_name);
      printf ("  %s\n", MOT_DHLM_name);
      printf ("  %s\n", MOT_SUSE_name);
      printf ("  %s\n", MOT_FOFF_name);
      printf ("  %s\n", MOT_ABLE_name);
    }
                 /*---------------------------------------------------
                 ** Start by getting the RDBL field of the motor and
                 ** checking that it matches the given encoder name.
                 */
    status = mySearchw (MOT_RDBL_name, &ca_MOT_RDBL, tmo, pVar);
    if (status != OK) {
      ca_clear_channel (ca_MOT_RDBL); DESTROY;
    }
    status = myGetw (
               MOT_RDBL_name, ca_MOT_RDBL, DBR_STRING, &MOT_RDBL, tmo, debug);
    if (status != OK) {
      ca_context_destroy ();
      return ERROR;
    }

    rdblLen = strcspn (MOT_RDBL, " "); MOT_RDBL[rdblLen] = '\0';
    if (strcmp (MOT_RDBL, encoder) != 0) {
      errlogPrintf ("\007RDBL field of %s is not %s.\n", myMotor, encoder);
      errlogPrintf ("Synchronisation abandoned!\n");
      ca_context_destroy ();
      return ERROR;
    }
                 /*---------------------------------------------------
                 ** Next, check that the encoder channel exists.
                 */
    if (mySearchw (ENC_VAL_name, &ca_ENC_VAL, tmo, pVar) != OK) {
      errlogPrintf ("Synchronisation abandoned!\n");
      DESTROY;
    }

    status = myGetw (
               ENC_VAL_name, ca_ENC_VAL, DBR_DOUBLE, &ENC_VAL, tmo, debug);
    if (status != OK) {
      errlogPrintf ("Synchronisation abandoned!\n");
      ca_context_destroy ();
      return ERROR;
    }
                 /*---------------------------------------------------
                 ** Preliminary checks OK, so connect to all channels.
                 */
    if (startCaSearch (ENC_UDF_name,  &ca_ENC_UDF,  pVar) != OK) {ca_context_destroy (); return ERROR;}
    if (startCaSearch (MOT_RRES_name, &ca_MOT_RRES, pVar) != OK) {ca_context_destroy (); return ERROR;}
    if (startCaSearch (MOT_DMOV_name, &ca_MOT_DMOV, pVar) != OK) {ca_context_destroy (); return ERROR;}
    if (startCaSearch (MOT_URIP_name, &ca_MOT_URIP, pVar) != OK) {ca_context_destroy (); return ERROR;}
    if (startCaSearch (MOT_SSET_name, &ca_MOT_SSET, pVar) != OK) {ca_context_destroy (); return ERROR;}
    if (startCaSearch (MOT_DVAL_name, &ca_MOT_DVAL, pVar) != OK) {ca_context_destroy (); return ERROR;}
    if (startCaSearch (MOT_DLLM_name, &ca_MOT_DLLM, pVar) != OK) {ca_context_destroy (); return ERROR;}
    if (startCaSearch (MOT_DHLM_name, &ca_MOT_DHLM, pVar) != OK) {ca_context_destroy (); return ERROR;}
    if (startCaSearch (MOT_SUSE_name, &ca_MOT_SUSE, pVar) != OK) {ca_context_destroy (); return ERROR;}
    if (startCaSearch (MOT_FOFF_name, &ca_MOT_FOFF, pVar) != OK) {ca_context_destroy (); return ERROR;}
    if (startCaSearch (MOT_ABLE_name, &ca_MOT_ABLE, pVar) != OK) {ca_context_destroy (); return ERROR;}

    if (waitAllConnected (tmo, pVar) != OK) {
      DESTROY;
    }
    if (pVar->debug) printf ("All channels connected.\n");
                 /*---------------------------------------------------
                 ** Wait for encoder value to become valid. Immediately
                 ** after iocInit, this could, in principle, take a
                 ** bit of time.
                 */
    if (debug) printf ("Ensuring that %s is valid ...\n", myEncoder);
    cntr = 50;
    if (myGetw (
          ENC_UDF_name, ca_ENC_UDF, DBR_INT, &ENC_UDF, tmo, debug) != OK) {
      DESTROY;
    }
    while (ENC_UDF != 0) {
      cntr--;
      if (cntr <= 0) {
        errlogPrintf ("\007Time-out waiting for encoder value to become valid.\n");
        errlogPrintf ("Synchronisation abandoned!\n");
        DESTROY;
      }
      epicsThreadSleep (0.1);
      if (myGetw (
            ENC_UDF_name, ca_ENC_UDF, DBR_INT, &ENC_UDF, tmo, debug) != OK) {
        DESTROY;
      }
    }
    if (debug) printf ("%s is OK\n", myEncoder);
                 /*---------------------------------------------------
                 ** Get the enable/disable state of the motor and then
                 ** ensure that it is enabled.
                 */
    if (myGetw (
          MOT_ABLE_name, ca_MOT_ABLE, DBR_LONG, &able_save, tmo, debug) != OK) {
      DESTROY;
    }
    if (debug) printf ("%-27s = %10d\n", MOT_ABLE_name, able_save);
    if (able_save != 0) {
      MOT_ABLE = 0;
      if (myPutw (
            MOT_ABLE_name, ca_MOT_ABLE, DBR_LONG, &MOT_ABLE, tmo, debug) != OK) {
        DESTROY;
      }
      if (debug) {
        printf ("Motor enabled\n");
        epicsThreadSleep (1.0);
      }
    }
                 /*---------------------------------------------------
                 ** Get the values we need or which need saving.
                 */
    if (myGetw (
          MOT_FOFF_name, ca_MOT_FOFF, DBR_LONG,   &foff_save, tmo, debug) != OK) {
      DESTROY;
    }
    if (myGetw (
          MOT_DLLM_name, ca_MOT_DLLM, DBR_DOUBLE, &MOT_DLLM,  tmo, debug) != OK) {
      DESTROY;
    }
    if (myGetw (
          MOT_DHLM_name, ca_MOT_DHLM, DBR_DOUBLE, &MOT_DHLM,  tmo, debug) != OK) {
      DESTROY;
    }
    if (myGetw (
          MOT_RRES_name, ca_MOT_RRES, DBR_DOUBLE, &MOT_RRES,  tmo, debug) != OK) {
      DESTROY;
    }
    if (myGetw (
          ENC_VAL_name,  ca_ENC_VAL,  DBR_DOUBLE, &ENC_VAL,   tmo, debug) != OK) {
      DESTROY;
    }
    if (debug) printf ("%-27s = %10d\n",   MOT_FOFF_name, foff_save);
    if (debug) printf ("%-27s = %10.5f\n", MOT_DLLM_name, MOT_DLLM);
    if (debug) printf ("%-27s = %10.5f\n", MOT_DHLM_name, MOT_DHLM);
    if (debug) printf ("%-27s = %10.5f\n", MOT_RRES_name, MOT_RRES);
    if (debug) printf ("%-27s = %10.5f\n", ENC_VAL_name,  ENC_VAL);

    setVal = MOT_RRES * ENC_VAL;
    printf ("Value of encoder = %10.5f\n", setVal);
                 /*---------------------------------------------------
                 ** Freeze the offsets
                 */
    MOT_FOFF = 1;
    if (myPutw (
          MOT_FOFF_name, ca_MOT_FOFF, DBR_LONG, &MOT_FOFF, tmo, debug) != OK) {
      DESTROY;
    }
    if (debug) {
      printf ("Fixed offset.\n");
      epicsThreadSleep (1.0);
    }
                 /*---------------------------------------------------
                 ** Tell motor to use encoder
                 */
    MOT_URIP = 1;
    if (myPutw (
          MOT_URIP_name, ca_MOT_URIP, DBR_LONG, &MOT_URIP, tmo, debug) != OK) {
      DESTROY;
    }
    if (debug) {
      printf ("URIP set to 1.\n");
      epicsThreadSleep (1.0);
    }
                 /*---------------------------------------------------
                 ** Go to "Set" mode
                 */
    MOT_SSET = 1;
    if (myPutw (
          MOT_SSET_name, ca_MOT_SSET, DBR_LONG, &MOT_SSET, tmo, debug) != OK) {
      DESTROY;
    }
    if (debug) {
      printf ("\"Set\" mode has been set.\n");
      epicsThreadSleep (1.0);
    }
                 /*---------------------------------------------------
                 ** If the motor soft limits are set (i.e. not both zero),
                 ** See if encoder has a valid value.
                 ** If so, use it, otherwise set a middle point.
                 */
    if ((MOT_DLLM != 0.0) || (MOT_DHLM != 0.0)) {
      if ((setVal < MOT_DLLM) || (setVal > MOT_DHLM)) {
        printf ("\007Warning -- motor seems to be outside its soft limits!\n");
        setVal = 0.5 * (MOT_DLLM + MOT_DHLM);
      }
    }
                                 /* Force motor record update */
    MOT_DVAL = setVal;
    if (myPutw (
          MOT_DVAL_name, ca_MOT_DVAL, DBR_DOUBLE, &MOT_DVAL, tmo, debug) != OK) {
      DESTROY;
    }
    if (debug) {
      printf ("Dial value has been set to %f\n", MOT_DVAL);
    }
                 /*---------------------------------------------------
                 ** The update can take some time. Wait for the
                 ** motor's DMOV field to get set.
                 */
    if (debug) printf ("Waiting for update to complete ...\n");
    cntr = 50;
    if (myGetw (
          MOT_DMOV_name, ca_MOT_DMOV, DBR_INT, &MOT_DMOV, tmo, debug) != OK) {
      DESTROY;
    }
    while (MOT_DMOV == 0) {
      cntr--;
      if (cntr <= 0) {
        errlogPrintf ("\007Time-out waiting for update to complete.\n");
        errlogPrintf ("Synchronisation abandoned!\n");
        DESTROY;
      }
      epicsThreadSleep (0.1);
      if (myGetw (
            MOT_DMOV_name, ca_MOT_DMOV, DBR_INT, &MOT_DMOV, tmo, debug) != OK) {
        DESTROY;
      }
    }
    if (debug) printf ("Update completed.\n");
                 /*---------------------------------------------------
                 ** Go back to "Use" mode.
                 */
    MOT_SUSE = 1;
    if (myPutw (
          MOT_SUSE_name, ca_MOT_SUSE, DBR_LONG, &MOT_SUSE, tmo, debug) != OK) {
      DESTROY;
    }
    if (debug) {
      printf ("\"Use\" mode has been set.\n");
      epicsThreadSleep (1.0);
    }
                 /*---------------------------------------------------
                 ** Restore FOFF state.
                 */
    MOT_FOFF = foff_save;
    if (myPutw (
          MOT_FOFF_name, ca_MOT_FOFF, DBR_LONG, &MOT_FOFF, tmo, debug) != OK) {
      DESTROY;
    }
    if (debug) {
      printf ("Offset state (fixed/variable) restored.\n");
      epicsThreadSleep (1.0);
    }
                 /*---------------------------------------------------
                 ** Restore "Enable" state.
                 */
    MOT_ABLE = able_save;
    if (myPutw (
          MOT_ABLE_name, ca_MOT_ABLE, DBR_LONG, &MOT_ABLE, tmo, debug) != OK) {
      DESTROY;
    }
    if (debug) {
      printf ("Motor enable/disable state restored.\n");
      epicsThreadSleep (1.0);
    }
                 /*---------------------------------------------------
                 */
    ca_context_destroy ();      /* Close down channel access */
    free (pVar);
    printf ("%s and %s have been synchronised\n", myMotor, myEncoder);
    return OK;
  }
/*--------------------------------------------------*/
/* emacs setup - force text mode to prevent emacs   */
/*               from helping with the indentation  */
/*               and tell emacs to use spaces when  */
/*               tabbing.                           */
/* Local Variables:                                 */
/* mode:text                                        */
/* indent-tabs-mode:nil                             */
/* End:                                             */
/*--------------------------------------------------*/
/*--------------------------------- End of $RCSfile: motorInit.c,v $ ----*/
