/* ** 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 #else #define OK 0 #define ERROR 1 #endif #include #include #include #include #ifdef vxWorks #include #include static void epicsThreadSleep (double secs) { taskDelay ((int)(sysClkRateGet () * secs)); } #else #include #endif #include #include #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 $ ----*/