/* FILENAME... motorSimController.cpp USAGE... Simulated Motor Support. Based on drvMotorSim.c Mark Rivers December 13, 2009 */ #include #include #include #include #include #include #include #include #include #include #include "motorSimDriver.h" #include #define DEFAULT_LOW_LIMIT -10000 #define DEFAULT_HI_LIMIT 10000 #define DEFAULT_HOME 0 #define DEFAULT_START 0 static const char *driverName = "motorSimDriver"; static void motorSimTaskC(void *drvPvt); typedef struct motorSimControllerNode { ELLNODE node; const char *portName; motorSimController *pController; } motorSimControllerNode; static ELLLIST motorSimControllerList; static int motorSimControllerListInitialized = 0; motorSimAxis::motorSimAxis(motorSimController *pController, int axis, double lowHardLimit, double hiHardLimit, double home, double start ) : asynMotorAxis(pController, axis), pC_(pController), lowHardLimit_(lowHardLimit), hiHardLimit_(hiHardLimit), home_(home) { route_pars_t pars; pars.numRoutedAxes = 1; pars.routedAxisList[0] = 1; pars.Tsync = 0.0; pars.Tcoast = 0.0; pars.axis[0].Amax = 1.0; pars.axis[0].Vmax = 1.0; endpoint_.T = 0; endpoint_.axis[0].p = start; endpoint_.axis[0].v = 0; nextpoint_.T = 0; nextpoint_.axis[0].p = start; route_ = routeNew( &(this->endpoint_), &pars ); deferred_move_ = 0; } motorSimController::motorSimController(const char *portName, int numAxes, int priority, int stackSize) : asynMotorController(portName, numAxes, NUM_SIM_CONTROLLER_PARAMS, asynInt32Mask | asynFloat64Mask, asynInt32Mask | asynFloat64Mask, ASYN_CANBLOCK | ASYN_MULTIDEVICE, 1, // autoconnect priority, stackSize) { int axis; motorSimAxis *pAxis; motorSimControllerNode *pNode; if (!motorSimControllerListInitialized) { motorSimControllerListInitialized = 1; ellInit(&motorSimControllerList); } // We should make sure this portName is not already in the list */ pNode = (motorSimControllerNode*) calloc(1, sizeof(motorSimControllerNode)); pNode->portName = epicsStrDup(portName); pNode->pController = this; ellAdd(&motorSimControllerList, (ELLNODE *)pNode); if (numAxes < 1 ) numAxes = 1; numAxes_ = numAxes; this->movesDeferred_ = 0; for (axis=0; axismotorPosition_, DEFAULT_START); } this->motorThread_ = epicsThreadCreate("motorSimThread", epicsThreadPriorityLow, epicsThreadGetStackSize(epicsThreadStackMedium), (EPICSTHREADFUNC) motorSimTaskC, (void *) this); } void motorSimController::report(FILE *fp, int level) { int axis; motorSimAxis *pAxis; fprintf(fp, "Simulation motor driver %s, numAxes=%d\n", this->portName, numAxes_); for (axis=0; axisaxisNo_); if (level > 0) { double lowSoftLimit=0.0; double hiSoftLimit=0.0; fprintf(fp, " Current position = %f, velocity = %f at current time: %f\n", pAxis->nextpoint_.axis[0].p, pAxis->nextpoint_.axis[0].v, pAxis->nextpoint_.T); fprintf(fp, " Destination posn = %f, velocity = %f at desination time: %f\n", pAxis->endpoint_.axis[0].p, pAxis->endpoint_.axis[0].v, pAxis->endpoint_.T); fprintf(fp, " Hard limits: %f, %f\n", pAxis->lowHardLimit_, pAxis->hiHardLimit_); fprintf(fp, " Home: %f\n", pAxis->home_); fprintf(fp, " Enc. offset: %f\n", pAxis->enc_offset_); getDoubleParam(pAxis->axisNo_, motorHighLimit_, &hiSoftLimit); getDoubleParam(pAxis->axisNo_, motorLowLimit_, &lowSoftLimit); fprintf(fp, " Soft limits: %f, %f\n", lowSoftLimit, hiSoftLimit ); if (pAxis->homing_) fprintf(fp, " Currently homing axis\n" ); } } // Call the base class method asynMotorController::report(fp, level); } asynStatus motorSimController::processDeferredMoves() { asynStatus status = asynError; double position = 0.0; int axis; motorSimAxis *pAxis; for (axis=0; axisdeferred_move_) { position = pAxis->deferred_position_; /* Check to see if in hard limits */ if ((pAxis->nextpoint_.axis[0].p >= pAxis->hiHardLimit_ && position > pAxis->nextpoint_.axis[0].p) || (pAxis->nextpoint_.axis[0].p <= pAxis->lowHardLimit_ && position < pAxis->nextpoint_.axis[0].p)) return asynError; pAxis->endpoint_.axis[0].p = position - pAxis->enc_offset_; pAxis->endpoint_.axis[0].v = 0.0; setIntegerParam(axis, motorStatusDone_, 0); pAxis->deferred_move_ = 0; } } return status; } asynStatus motorSimController::writeInt32(asynUser *pasynUser, epicsInt32 value) { int function = pasynUser->reason; asynStatus status = asynSuccess; motorSimAxis *pAxis = this->getAxis(pasynUser); static const char *functionName = "writeInt32"; /* Set the parameter and readback in the parameter library. This may be overwritten when we read back the * status at the end, but that's OK */ pAxis->setIntegerParam(function, value); if (function == motorDeferMoves_) { asynPrint(pasynUser, ASYN_TRACE_FLOW, "%s:%s: %sing Deferred Move flag on driver %s\n", value != 0.0?"Sett":"Clear", driverName, functionName, this->portName); if (value == 0.0 && movesDeferred_ != 0) { processDeferredMoves(); } movesDeferred_ = value; } else { /* Call base class call its method (if we have our parameters check this here) */ status = asynMotorController::writeInt32(pasynUser, value); } /* Do callbacks so higher layers see any changes */ pAxis->callParamCallbacks(); if (status) asynPrint(pasynUser, ASYN_TRACE_ERROR, "%s:%s: error, status=%d function=%d, value=%d\n", driverName, functionName, status, function, value); else asynPrint(pasynUser, ASYN_TRACEIO_DRIVER, "%s:%s: function=%d, value=%d\n", driverName, functionName, function, value); return status; } motorSimAxis* motorSimController::getAxis(asynUser *pasynUser) { return static_cast(asynMotorController::getAxis(pasynUser)); } motorSimAxis* motorSimController::getAxis(int axisNo) { return static_cast(asynMotorController::getAxis(axisNo)); } asynStatus motorSimController::profileMove(asynUser *pasynUser, int npoints, double positions[], double times[], int relative, int trigger ) { return asynError; } asynStatus motorSimController::triggerProfile(asynUser *pasynUser) { return asynError; } static void motorSimTaskC(void *drvPvt) { motorSimController *pController = (motorSimController*)drvPvt; pController->motorSimTask(); } #define DELTA 0.1 void motorSimController::motorSimTask() { epicsTimeStamp now; double delta; int axis; motorSimAxis *pAxis; while ( 1 ) { /* Get a new timestamp */ epicsTimeGetCurrent( &now ); delta = epicsTimeDiffInSeconds( &now, &(prevTime_) ); prevTime_ = now; if ( delta > (DELTA/4.0) && delta <= (4.0*DELTA) ) { /* A reasonable time has elapsed, it's not a time step in the clock */ for (axis=0; axislock(); pAxis = getAxis(axis); pAxis->process(delta ); this->unlock(); } } epicsThreadSleep( DELTA ); } } asynStatus motorSimAxis::move(double position, int relative, double minVelocity, double maxVelocity, double acceleration) { route_pars_t pars; static const char *functionName = "move"; if (relative) position += endpoint_.axis[0].p + enc_offset_; /* Check to see if in hard limits */ if ((nextpoint_.axis[0].p >= hiHardLimit_ && position > nextpoint_.axis[0].p) || (nextpoint_.axis[0].p <= lowHardLimit_ && position < nextpoint_.axis[0].p) ) return asynError; if (pC_->movesDeferred_ == 0) { /*Normal move.*/ endpoint_.axis[0].p = position - enc_offset_; endpoint_.axis[0].v = 0.0; } else { /*Deferred moves.*/ deferred_position_ = position; deferred_move_ = 1; deferred_relative_ = relative; } routeGetParams(route_, &pars); if (maxVelocity != 0) pars.axis[0].Vmax = fabs(maxVelocity); if (acceleration != 0) pars.axis[0].Amax = fabs(acceleration); routeSetParams( route_, &pars ); setIntegerParam(pC_->motorStatusDone_, 0); callParamCallbacks(); asynPrint(pasynUser_, ASYN_TRACE_FLOW, "%s:%s: Set driver %s, axis %d move to %f, min vel=%f, maxVel=%f, accel=%f\n", driverName, functionName, pC_->portName, axisNo_, position, minVelocity, maxVelocity, acceleration ); return asynSuccess; } asynStatus motorSimAxis::setVelocity(double velocity, double acceleration ) { route_pars_t pars; double deltaV = velocity - this->nextpoint_.axis[0].v; double time; /* Check to see if in hard limits */ if ((this->nextpoint_.axis[0].p > hiHardLimit_ && velocity > 0) || (this->nextpoint_.axis[0].p < lowHardLimit_ && velocity < 0) ) return asynError; routeGetParams( this->route_, &pars ); if (acceleration != 0) pars.axis[0].Amax = fabs(acceleration); routeSetParams( this->route_, &pars ); time = fabs( deltaV / pars.axis[0].Amax ); this->endpoint_.axis[0].v = velocity; this->endpoint_.axis[0].p = (this->nextpoint_.axis[0].p + time * ( this->nextpoint_.axis[0].v + 0.5 * deltaV)); this->reroute_ = ROUTE_NEW_ROUTE; return asynSuccess; } asynStatus motorSimAxis::home(double minVelocity, double maxVelocity, double acceleration, int forwards ) { asynStatus status = asynError; // static const char *functionName = "home"; status = setVelocity((forwards? maxVelocity: -maxVelocity), acceleration ); homing_ = 1; return status; } asynStatus motorSimAxis::moveVelocity(double minVelocity, double velocity, double acceleration ) { asynStatus status = asynError; // static const char *functionName = "moveVelocity"; status = setVelocity(velocity, acceleration ); return status; } asynStatus motorSimAxis::stop(double acceleration ) { // static const char *functionName = "moveVelocityAxis"; setVelocity(0.0, acceleration ); deferred_move_ = 0; return asynSuccess; } asynStatus motorSimAxis::setPosition(double position) { enc_offset_ = position - nextpoint_.axis[0].p; return asynSuccess; } asynStatus motorSimAxis::config(int hiHardLimit, int lowHardLimit, int home, int start) { hiHardLimit_ = hiHardLimit; lowHardLimit_ = lowHardLimit; home_ = home; enc_offset_ = start; return asynSuccess; } asynStatus motorSimAxis::poll(bool *moving) { return asynSuccess; } /** Process one iteration of an axis This routine takes a single axis and propogates its motion forward a given amount of time. \param delta [in] Time in seconds to propogate motion forwards. \return Integer indicating 0 (asynSuccess) for success or non-zero for failure. */ void motorSimAxis::process(double delta ) { double lastpos; int done = 0; lastpos = nextpoint_.axis[0].p; nextpoint_.T += delta; routeFind( route_, reroute_, &endpoint_, &nextpoint_ ); /* if (reroute_ == ROUTE_NEW_ROUTE) routePrint( route_, reroute_, &endpoint_, &nextpoint_, stdout ); */ reroute_ = ROUTE_CALC_ROUTE; /* No, do a limits check */ if (homing_ && ((lastpos - home_) * (nextpoint_.axis[0].p - home_)) <= 0) { /* Homing and have crossed the home sensor - return to home */ homing_ = 0; reroute_ = ROUTE_NEW_ROUTE; endpoint_.axis[0].p = home_; endpoint_.axis[0].v = 0.0; } if ( nextpoint_.axis[0].p > hiHardLimit_ && nextpoint_.axis[0].v > 0 ) { if (homing_) setVelocity(-endpoint_.axis[0].v, 0.0 ); else { reroute_ = ROUTE_NEW_ROUTE; endpoint_.axis[0].p = hiHardLimit_; endpoint_.axis[0].v = 0.0; } } else if (nextpoint_.axis[0].p < lowHardLimit_ && nextpoint_.axis[0].v < 0) { if (homing_) setVelocity(-endpoint_.axis[0].v, 0.0 ); else { reroute_ = ROUTE_NEW_ROUTE; endpoint_.axis[0].p = lowHardLimit_; endpoint_.axis[0].v = 0.0; } } if (nextpoint_.axis[0].v == 0) { if (!deferred_move_) { done = 1; } } else { done = 0; } setDoubleParam (pC_->motorPosition_, (nextpoint_.axis[0].p+enc_offset_)); setDoubleParam (pC_->motorEncoderPosition_, (nextpoint_.axis[0].p+enc_offset_)); setIntegerParam(pC_->motorStatusDirection_, (nextpoint_.axis[0].v > 0)); setIntegerParam(pC_->motorStatusDone_, done); setIntegerParam(pC_->motorStatusHighLimit_, (nextpoint_.axis[0].p >= hiHardLimit_)); setIntegerParam(pC_->motorStatusHome_, (nextpoint_.axis[0].p == home_)); setIntegerParam(pC_->motorStatusMoving_, !done); setIntegerParam(pC_->motorStatusLowLimit_, (nextpoint_.axis[0].p <= lowHardLimit_)); callParamCallbacks(); } /** Configuration command, called directly or from iocsh */ extern "C" int motorSimCreateController(const char *portName, int numAxes, int priority, int stackSize) { motorSimController *pSimController = new motorSimController(portName,numAxes, priority, stackSize); pSimController = NULL; return(asynSuccess); } extern "C" int motorSimConfigAxis(const char *portName, int axis, int hiHardLimit, int lowHardLimit, int home, int start) { motorSimControllerNode *pNode; static const char *functionName = "motorSimConfigAxis"; // Find this controller if (!motorSimControllerListInitialized) { printf("%s:%s: ERROR, controller list not initialized\n", driverName, functionName); return(-1); } pNode = (motorSimControllerNode*)ellFirst(&motorSimControllerList); while(pNode) { if (strcmp(pNode->portName, portName) == 0) { printf("%s:%s: configuring controller %s axis %d\n", driverName, functionName, pNode->portName, axis); pNode->pController->getAxis(axis)->config(hiHardLimit, lowHardLimit, home, start); return(0); } pNode = (motorSimControllerNode*)ellNext((ELLNODE*)pNode); } printf("Controller not found\n"); return(-1); } /** Code for iocsh registration */ static const iocshArg motorSimCreateControllerArg0 = {"Port name", iocshArgString}; static const iocshArg motorSimCreateControllerArg1 = {"Number of axes", iocshArgInt}; static const iocshArg motorSimCreateControllerArg2 = {"priority", iocshArgInt}; static const iocshArg motorSimCreateControllerArg3 = {"stackSize", iocshArgInt}; static const iocshArg * const motorSimCreateControllerArgs[] = {&motorSimCreateControllerArg0, &motorSimCreateControllerArg1, &motorSimCreateControllerArg2, &motorSimCreateControllerArg3}; static const iocshFuncDef motorSimCreateControllerDef = {"motorSimCreateController", 4, motorSimCreateControllerArgs}; static void motorSimCreateContollerCallFunc(const iocshArgBuf *args) { motorSimCreateController(args[0].sval, args[1].ival, args[2].ival, args[3].ival); } static const iocshArg motorSimConfigAxisArg0 = { "Post name", iocshArgString}; static const iocshArg motorSimConfigAxisArg1 = { "Axis #", iocshArgInt}; static const iocshArg motorSimConfigAxisArg2 = { "High limit", iocshArgInt}; static const iocshArg motorSimConfigAxisArg3 = { "Low limit", iocshArgInt}; static const iocshArg motorSimConfigAxisArg4 = { "Home position", iocshArgInt}; static const iocshArg motorSimConfigAxisArg5 = { "Start posn", iocshArgInt}; static const iocshArg *const motorSimConfigAxisArgs[] = { &motorSimConfigAxisArg0, &motorSimConfigAxisArg1, &motorSimConfigAxisArg2, &motorSimConfigAxisArg3, &motorSimConfigAxisArg4, &motorSimConfigAxisArg5 }; static const iocshFuncDef motorSimConfigAxisDef ={"motorSimConfigAxis",6,motorSimConfigAxisArgs}; static void motorSimConfigAxisCallFunc(const iocshArgBuf *args) { motorSimConfigAxis(args[0].sval, args[1].ival, args[2].ival, args[3].ival, args[4].ival, args[5].ival); } static void motorSimDriverRegister(void) { iocshRegister(&motorSimCreateControllerDef, motorSimCreateContollerCallFunc); iocshRegister(&motorSimConfigAxisDef, motorSimConfigAxisCallFunc); } extern "C" { epicsExportRegistrar(motorSimDriverRegister); }