asynMotorController控制器类

电机控制器的基类,实际的电机控制器从这个类派生

asynMotorController.h头文件

/* asynMotorController.h
 * 这个文件为asynMotorController定义了基类。
 * 真实电机控制器从这个类派生。它派生字PortDriver.
 */
#ifndef asynMotorController_H
#define asynMotorController_H

#include <epicsEvent.h>
#include <epicsTypes.h>

#define MAX_CONTROLLER_STRING_SIZE 256
#define DEFAULT_CONTROLLER_TIMEOUT 2.0

/*
 * 此驱动程序的字符串定义参数。这些时传递给drvUserCreate的值。
 * 当标准的asyn接口方法被调用时,驱动将在pasynUser->reason中放入一个被被使用的整数。
*/
#define motorMoveRelString              "MOTOR_MOVE_REL" //相对移动
#define motorMoveAbsString              "MOTOR_MOVE_ABS" //绝对移动
#define motorMoveVelString              "MOTOR_MOVE_VEL" //指定速度移动
#define motorHomeString                 "MOTOR_HOME"     //归零
#define motorStopString                 "MOTOR_STOP_AXIS"//停止轴
#define motorVelocityString             "MOTOR_VELOCITY" //速度
#define motorVelBaseString              "MOTOR_VEL_BASE" //基速度
#define motorAccelString                "MOTOR_ACCEL"    //加速度
#define motorPositionString             "MOTOR_POSITION" //位置
#define motorEncoderPositionString      "MOTOR_ENCODER_POSITION" //编码器位置
#define motorDeferMovesString           "MOTOR_DEFER_MOVES"
#define motorMoveToHomeString           "MOTOR_MOVE_HOME"
#define motorResolutionString           "MOTOR_RESOLUTION"       //分辨率
#define motorEncoderRatioString         "MOTOR_ENCODER_RATIO"    //编码器比率
#define motorPGainString                "MOTOR_PGAIN"
#define motorIGainString                "MOTOR_IGAIN"
#define motorDGainString                "MOTOR_DGAIN"
#define motorHighLimitString            "MOTOR_HIGH_LIMIT"       //上限位
#define motorLowLimitString             "MOTOR_LOW_LIMIT"        //下限位
#define motorClosedLoopString           "MOTOR_CLOSED_LOOP"      //闭环
#define motorPowerAutoOnOffString       "MOTOR_POWER_AUTO_ONOFF"
#define motorPowerOnDelayString         "MOTOR_POWER_ON_DELAY"
#define motorPowerOffDelayString        "MOTOR_POWER_OFF_DELAY"
#define motorPowerOffFractionString     "MOTOR_POWER_OFF_FRACTION"
#define motorPostMoveDelayString        "MOTOR_POST_MOVE_DELAY"
#define motorStatusString               "MOTOR_STATUS"               //状态
#define motorUpdateStatusString         "MOTOR_UPDATE_STATUS"        //
#define motorStatusDirectionString      "MOTOR_STATUS_DIRECTION"     //方向
#define motorStatusDoneString           "MOTOR_STATUS_DONE"          //结束
#define motorStatusHighLimitString      "MOTOR_STATUS_HIGH_LIMIT"    //高限位
#define motorStatusAtHomeString         "MOTOR_STATUS_AT_HOME"       //归零
#define motorStatusSlipString           "MOTOR_STATUS_SLIP"          //滑动
#define motorStatusPowerOnString        "MOTOR_STATUS_POWERED"      
#define motorStatusFollowingErrorString "MOTOR_STATUS_FOLLOWING_ERROR"
#define motorStatusHomeString           "MOTOR_STATUS_HOME"          //
#define motorStatusHasEncoderString     "MOTOR_STATUS_HAS_ENCODER"   //有编码器
#define motorStatusProblemString        "MOTOR_STATUS_PROBLEM"
#define motorStatusMovingString         "MOTOR_STATUS_MOVING"        //正在移动
#define motorStatusGainSupportString    "MOTOR_STATUS_GAIN_SUPPORT"
#define motorStatusCommsErrorString     "MOTOR_STATUS_COMMS_ERROR"   //下限位
#define motorStatusLowLimitString       "MOTOR_STATUS_LOW_LIMIT"
#define motorStatusHomedString          "MOTOR_STATUS_HOMED"

/*  这些是每个轴的参数,用于传递更多电机基类信息给驱动程序 */
#define motorRecResolutionString        "MOTOR_REC_RESOLUTION"    //分辨率
#define motorRecDirectionString         "MOTOR_REC_DIRECTION"     //方向
#define motorRecOffsetString            "MOTOR_REC_OFFSET"        //偏移

/*  这些是用于轮廓移动的每个控制器参数(联动运动) */
#define profileNumAxesString            "PROFILE_NUM_AXES"
#define profileNumPointsString          "PROFILE_NUM_POINTS"
#define profileCurrentPointString       "PROFILE_CURRENT_POINT"
#define profileNumPulsesString          "PROFILE_NUM_PULSES"
#define profileStartPulsesString        "PROFILE_START_PULSES"
#define profileEndPulsesString          "PROFILE_END_PULSES"
#define profileActualPulsesString       "PROFILE_ACTUAL_PULSES"
#define profileNumReadbacksString       "PROFILE_NUM_READBACKS"
#define profileTimeModeString           "PROFILE_TIME_MODE"
#define profileFixedTimeString          "PROFILE_FIXED_TIME"
#define profileTimeArrayString          "PROFILE_TIME_ARRAY"
#define profileAccelerationString       "PROFILE_ACCELERATION"
#define profileMoveModeString           "PROFILE_MOVE_MODE"
#define profileBuildString              "PROFILE_BUILD"
#define profileBuildStateString         "PROFILE_BUILD_STATE"
#define profileBuildStatusString        "PROFILE_BUILD_STATUS"
#define profileBuildMessageString       "PROFILE_BUILD_MESSAGE"
#define profileExecuteString            "PROFILE_EXECUTE"
#define profileExecuteStateString       "PROFILE_EXECUTE_STATE"
#define profileExecuteStatusString      "PROFILE_EXECUTE_STATUS"
#define profileExecuteMessageString     "PROFILE_EXECUTE_MESSAGE"
#define profileAbortString              "PROFILE_ABORT"
#define profileReadbackString           "PROFILE_READBACK"
#define profileReadbackStateString      "PROFILE_READBACK_STATE"
#define profileReadbackStatusString     "PROFILE_READBACK_STATUS"
#define profileReadbackMessageString    "PROFILE_READBACK_MESSAGE"

/* 这些是用于轮廓运动的每轴参数 */
#define profileUseAxisString            "PROFILE_USE_AXIS"
#define profilePositionsString          "PROFILE_POSITIONS"
#define profileReadbacksString          "PROFILE_READBACKS"
#define profileFollowingErrorsString    "PROFILE_FOLLOWING_ERRORS"

/**  
* 当状态改变时,被传递给devMotorAsyn的结构体
*/
typedef struct MotorStatus {
  double position;           /**< 命令的电机位置 */
  double encoderPosition;    /**< 实际编码器位置 */
  double velocity;           /**< 实际速度 */
  epicsUInt32 status;        /**< 包含状态位(运动结束,限位等)的字*/
} MotorStatus;

enum ProfileTimeMode{
  PROFILE_TIME_MODE_FIXED,
  PROFILE_TIME_MODE_ARRAY
};

enum ProfileMoveMode{
  PROFILE_MOVE_MODE_ABSOLUTE,
  PROFILE_MOVE_MODE_RELATIVE
};

/* 对应构建,读取和执行的状态代码.
 * 小心,这些必须匹配相应的MBBI基类,但没有检查此的方法
*/
enum ProfileBuildState{
  PROFILE_BUILD_DONE,
  PROFILE_BUILD_BUSY
};

enum ProfileExecuteState{
  PROFILE_EXECUTE_DONE,
  PROFILE_EXECUTE_MOVE_START,
  PROFILE_EXECUTE_EXECUTING,
  PROFILE_EXECUTE_FLYBACK
};

enum ProfileReadbackState{
  PROFILE_READBACK_DONE,
  PROFILE_READBACK_BUSY
};


/* 对应构建,执行和读取的状态代码 */
enum ProfileStatus {
  PROFILE_STATUS_UNDEFINED,
  PROFILE_STATUS_SUCCESS,
  PROFILE_STATUS_FAILURE,
  PROFILE_STATUS_ABORT,
  PROFILE_STATUS_TIMEOUT
};

#ifdef __cplusplus
#include <asynPortDriver.h>

class asynMotorAxis;

class epicsShareClass asynMotorController : public asynPortDriver {

  public:
  /* 这是这个类的构造器 */
  asynMotorController(const char *portName, int numAxes, int numParams,
                      int interfaceMask, int interruptMask,
                      int asynFlags, int autoConnect, int priority, int stackSize);

  virtual ~asynMotorController();

  /* 我们重写了asynPortDriver 的方法 */
  virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
  virtual asynStatus writeFloat64(asynUser *pasynUser, epicsFloat64 value);
  virtual asynStatus writeFloat64Array(asynUser *pasynUser, epicsFloat64 *value, size_t nElements);
  virtual asynStatus readFloat64Array(asynUser *pasynUser, epicsFloat64 *value, size_t nElements, size_t *nRead);
  virtual asynStatus readGenericPointer(asynUser *pasynUser, void *pointer);
  virtual void report(FILE *fp, int details);

  /* 这些是这个类的新方法 */
  virtual asynMotorAxis* getAxis(asynUser *pasynUser);
  virtual asynMotorAxis* getAxis(int axisNo);
  virtual asynStatus startPoller(double movingPollPeriod, double idlePollPeriod, int forcedFastPolls);
  virtual asynStatus wakeupPoller();
  virtual asynStatus poll();
  virtual asynStatus setDeferredMoves(bool defer);
  void asynMotorPoller();  // 这应该私有,但被从C函数调用

  /* 处理moveToHome的函数 */
  virtual asynStatus startMoveToHomeThread();
  void asynMotorMoveToHome();

  /* 这些是用于轮廓移动的函数 */
  virtual asynStatus initializeProfile(size_t maxPoints);
  virtual asynStatus buildProfile();
  virtual asynStatus executeProfile();
  virtual asynStatus abortProfile();
  virtual asynStatus readbackProfile();

  virtual asynStatus setMovingPollPeriod(double movingPollPeriod);
  virtual asynStatus setIdlePollPeriod(double idlePollPeriod);

  /* 表示IOC正在关闭的标记。停止poller */
  int shuttingDown_;  

  protected:
  /* 这些是参数库中参数的索引。 它们是在从设备支持调用中pasynUser->reason的值 */
  //这些是电机命令
  #define FIRST_MOTOR_PARAM motorMoveRel_
  int motorMoveRel_;
  int motorMoveAbs_;
  int motorMoveVel_;
  int motorHome_;
  int motorStop_;
  int motorVelocity_;
  int motorVelBase_;
  int motorAccel_;
  int motorPosition_;
  int motorEncoderPosition_;
  int motorDeferMoves_;
  int motorMoveToHome_;
  int motorResolution_;
  int motorEncoderRatio_;
  int motorPGain_;
  int motorIGain_;
  int motorDGain_;
  int motorHighLimit_;
  int motorLowLimit_;
  int motorClosedLoop_;
  int motorPowerAutoOnOff_;
  int motorPowerOnDelay_;
  int motorPowerOffDelay_;
  int motorPowerOffFraction_;
  int motorPostMoveDelay_;
  int motorStatus_;
  int motorUpdateStatus_;

  // 这些是状态位
  int motorStatusDirection_;
  int motorStatusDone_;
  int motorStatusHighLimit_;
  int motorStatusAtHome_;
  int motorStatusSlip_;
  int motorStatusPowerOn_;
  int motorStatusFollowingError_;
  int motorStatusHome_;
  int motorStatusHasEncoder_;
  int motorStatusProblem_;
  int motorStatusMoving_;
  int motorStatusGainSupport_;
  int motorStatusCommsError_;
  int motorStatusLowLimit_;
  int motorStatusHomed_;

  //这些是每个轴参数,用于传递更多电机记录信息到驱动
  int motorRecResolution_;
  int motorRecDirection_;
  int motorRecOffset_;

  // 这些是每个控制器参数,用于轮廓移动
  int profileNumAxes_;
  int profileNumPoints_;
  int profileCurrentPoint_;
  int profileNumPulses_;
  int profileStartPulses_;
  int profileEndPulses_;
  int profileActualPulses_;
  int profileNumReadbacks_;
  int profileTimeMode_;
  int profileFixedTime_;
  int profileTimeArray_;
  int profileAcceleration_;
  int profileMoveMode_;
  int profileBuild_;
  int profileBuildState_;
  int profileBuildStatus_;
  int profileBuildMessage_;
  int profileExecute_;
  int profileExecuteState_;
  int profileExecuteStatus_;
  int profileExecuteMessage_;
  int profileAbort_;
  int profileReadback_;
  int profileReadbackState_;
  int profileReadbackStatus_;
  int profileReadbackMessage_;

  // 这些是每轴参数,用于轮廓移动
  int profileUseAxis_;
  int profilePositions_;
  int profileReadbacks_;
  int profileFollowingErrors_;
  #define LAST_MOTOR_PARAM profileFollowingErrors_

  int numAxes_;                 /**< 控制器支持的轴数*/
  asynMotorAxis **pAxes_;       /**< 轴对象指针的数组 */
  epicsEventId pollEventId_;    /**< 唤醒poller的事件ID */
  epicsEventId moveToHomeId_;    /**< 唤醒移动到归零线程的事件ID */
  double idlePollPeriod_;       /**<  在没有轴在移动时,轮询之间的时间 */
  double movingPollPeriod_;     /**< 当任何轴正在移动时,轮询之间的时间。 */
  int    forcedFastPolls_;      /**< 当poller唤醒时,强制快速轮询的次数 */

  size_t maxProfilePoints_;     /**< 轮廓点的最大数目*/
  double *profileTimes_;        /**< 每个轮廓点次数的数组 */

  int moveToHomeAxis_;

  /* 这些是控制器的便捷函数,它们对硬件使用asynOcter接口 */
  asynStatus writeController();
  asynStatus writeController(const char *output, double timeout);
  asynStatus writeReadController();
  asynStatus writeReadController(const char *output, char *response, size_t maxResponseLen, size_t *responseLen, double timeout);
  asynUser *pasynUserController_;
  char outString_[MAX_CONTROLLER_STRING_SIZE];  //存储发送给控制器的字符串
  char inString_[MAX_CONTROLLER_STRING_SIZE];   //存储从控制器接收的字符串

  friend class asynMotorAxis;
};
//计算电机驱动参数数目
#define NUM_MOTOR_DRIVER_PARAMS (&LAST_MOTOR_PARAM - &FIRST_MOTOR_PARAM + 1)

#endif /* _cplusplus */

asynMotorController.cpp基类实现: 

/* asynMotorController.cpp
 * 这个文件为一个asynMotorContorller定义基类。真实电机控制器从这个类派生。
 * 它拍摄于asynPortDriver.
 */
#include <stdlib.h>
#include <string.h>

#include <epicsThread.h>
#include <iocsh.h>

#include <asynPortDriver.h>
#include <asynOctetSyncIO.h>
#include <epicsExport.h>
#define epicsExportSharedSymbols
#include <shareLib.h>
#include "asynMotorController.h"
#include "asynMotorAxis.h"

#ifndef VERSION_INT
#  define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
#endif

#define MOTOR_ASYN_VERSION_INT VERSION_INT(ASYN_VERSION,ASYN_REVISION,ASYN_MODIFICATION,0)

#define VERSION_INT_4_32 VERSION_INT(4,32,0,0)

static const char *driverName = "asynMotorController";
static void asynMotorPollerC(void *drvPvt);
static void asynMotorMoveToHomeC(void *drvPvt);



/** 创建一个asynMotorController对象
 * 所有参数只是被传递给了asynPortDriver基类。
 * 在调用这个基类构造器后,这个方法创建了在asynMotorDirver.h中定义的电机参数。
 */
asynMotorController::asynMotorController(const char *portName, int numAxes, int numParams,
                                         int interfaceMask, int interruptMask,
                                         int asynFlags, int autoConnect, int priority, int stackSize)

  : asynPortDriver(portName, numAxes,
#if MOTOR_ASYN_VERSION_INT < VERSION_INT_4_32
                   NUM_MOTOR_DRIVER_PARAMS+numParams,
#endif
      interfaceMask | asynOctetMask | asynInt32Mask | asynFloat64Mask | asynFloat64ArrayMask | asynGenericPointerMask | asynDrvUserMask,
      interruptMask | asynOctetMask | asynInt32Mask | asynFloat64Mask | asynFloat64ArrayMask | asynGenericPointerMask,
      asynFlags, autoConnect, priority, stackSize),
    shuttingDown_(0), numAxes_(numAxes)
{
  static const char *functionName = "asynMotorController";

  /*  parameters创建电机参数的基本集合  */
  createParam(motorMoveRelString,         asynParamFloat64,    &motorMoveRel_);
  createParam(motorMoveAbsString,         asynParamFloat64,    &motorMoveAbs_);
  createParam(motorMoveVelString,         asynParamFloat64,    &motorMoveVel_);
  createParam(motorHomeString,            asynParamFloat64,    &motorHome_);
  createParam(motorStopString,            asynParamInt32,      &motorStop_);
  createParam(motorVelocityString,        asynParamFloat64,    &motorVelocity_);
  createParam(motorVelBaseString,         asynParamFloat64,    &motorVelBase_);
  createParam(motorAccelString,           asynParamFloat64,    &motorAccel_);
  createParam(motorPositionString,        asynParamFloat64,    &motorPosition_);
  createParam(motorEncoderPositionString, asynParamFloat64,    &motorEncoderPosition_);
  createParam(motorDeferMovesString,      asynParamInt32,      &motorDeferMoves_);
  createParam(motorMoveToHomeString,      asynParamInt32,      &motorMoveToHome_);
  createParam(motorResolutionString,      asynParamFloat64,    &motorResolution_);
  createParam(motorEncoderRatioString,    asynParamFloat64,    &motorEncoderRatio_);
  createParam(motorPGainString,           asynParamFloat64,    &motorPGain_);
  createParam(motorIGainString,           asynParamFloat64,    &motorIGain_);
  createParam(motorDGainString,           asynParamFloat64,    &motorDGain_);
  createParam(motorHighLimitString,       asynParamFloat64,    &motorHighLimit_);
  createParam(motorLowLimitString,        asynParamFloat64,    &motorLowLimit_);
  createParam(motorClosedLoopString,      asynParamInt32,      &motorClosedLoop_);
  createParam(motorPowerAutoOnOffString,  asynParamInt32,      &motorPowerAutoOnOff_);
  createParam(motorPowerOnDelayString,    asynParamFloat64,    &motorPowerOnDelay_);
  createParam(motorPowerOffDelayString,   asynParamFloat64,    &motorPowerOffDelay_);
  createParam(motorPowerOffFractionString,asynParamInt32,      &motorPowerOffFraction_);
  createParam(motorPostMoveDelayString,   asynParamFloat64,    &motorPostMoveDelay_);
  createParam(motorStatusString,          asynParamInt32,      &motorStatus_);
  createParam(motorUpdateStatusString,    asynParamInt32,      &motorUpdateStatus_);
  createParam(motorStatusDirectionString, asynParamInt32,      &motorStatusDirection_);
  createParam(motorStatusDoneString,      asynParamInt32,      &motorStatusDone_);
  createParam(motorStatusHighLimitString, asynParamInt32,      &motorStatusHighLimit_);
  createParam(motorStatusAtHomeString,    asynParamInt32,      &motorStatusAtHome_);
  createParam(motorStatusSlipString,      asynParamInt32,      &motorStatusSlip_);
  createParam(motorStatusPowerOnString,   asynParamInt32,      &motorStatusPowerOn_);
  createParam(motorStatusFollowingErrorString, asynParamInt32, &motorStatusFollowingError_);
  createParam(motorStatusHomeString,      asynParamInt32,      &motorStatusHome_);
  createParam(motorStatusHasEncoderString,asynParamInt32,      &motorStatusHasEncoder_);
  createParam(motorStatusProblemString,   asynParamInt32,      &motorStatusProblem_);
  createParam(motorStatusMovingString,    asynParamInt32,      &motorStatusMoving_);
  createParam(motorStatusGainSupportString,asynParamInt32,      &motorStatusGainSupport_);
  createParam(motorStatusCommsErrorString,asynParamInt32,      &motorStatusCommsError_);
  createParam(motorStatusLowLimitString,  asynParamInt32,      &motorStatusLowLimit_);
  createParam(motorStatusHomedString,     asynParamInt32,      &motorStatusHomed_);

//这些是每个轴参数,用于传递更多motor基类信息给这个驱动:分辨率,方向,偏移量
createParam(motorRecResolutionString,     asynParamFloat64,    &motorRecResolution_);
createParam(motorRecDirectionString,      asynParamInt32,      &motorRecDirection_);
createParam(motorRecOffsetString,         asynParamFloat64,    &motorRecOffset_);

//这些是每个控制器参数,用于轮廓移动
createParam(profileNumAxesString,       asynParamInt32,       &profileNumAxes_);
createParam(profileNumPointsString,     asynParamInt32,       &profileNumPoints_);
createParam(profileCurrentPointString,  asynParamInt32,       &profileCurrentPoint_);
createParam(profileNumPulsesString,     asynParamInt32,       &profileNumPulses_);
createParam(profileStartPulsesString,   asynParamInt32,       &profileStartPulses_);
createParam(profileEndPulsesString,     asynParamInt32,       &profileEndPulses_);
createParam(profileActualPulsesString,  asynParamInt32,       &profileActualPulses_);
createParam(profileNumReadbacksString,  asynParamInt32,       &profileNumReadbacks_);
createParam(profileTimeModeString,      asynParamInt32,       &profileTimeMode_);
createParam(profileFixedTimeString,     asynParamFloat64,     &profileFixedTime_);
createParam(profileTimeArrayString,     asynParamFloat64Array,&profileTimeArray_);
createParam(profileAccelerationString,  asynParamFloat64,     &profileAcceleration_);
createParam(profileMoveModeString,      asynParamInt32,       &profileMoveMode_);
createParam(profileBuildString,         asynParamInt32,       &profileBuild_);
createParam(profileBuildStateString,    asynParamInt32,       &profileBuildState_);
createParam(profileBuildStatusString,   asynParamInt32,       &profileBuildStatus_);
createParam(profileBuildMessageString,  asynParamOctet,       &profileBuildMessage_);
createParam(profileExecuteString,       asynParamInt32,       &profileExecute_);
createParam(profileExecuteStateString,  asynParamInt32,       &profileExecuteState_);
createParam(profileExecuteStatusString, asynParamInt32,       &profileExecuteStatus_);
createParam(profileExecuteMessageString,asynParamOctet,       &profileExecuteMessage_);
createParam(profileAbortString,         asynParamInt32,       &profileAbort_);
createParam(profileReadbackString,      asynParamInt32,       &profileReadback_);
createParam(profileReadbackStateString, asynParamInt32,       &profileReadbackState_);
createParam(profileReadbackStatusString,asynParamInt32,       &profileReadbackStatus_);
createParam(profileReadbackMessageString,asynParamOctet,      &profileReadbackMessage_);

// 这些是每轴参数,用于轮廓移动
createParam(profileUseAxisString,        asynParamInt32,        &profileUseAxis_);
createParam(profilePositionsString,      asynParamFloat64Array, &profilePositions_);
createParam(profileReadbacksString,      asynParamFloat64Array, &profileReadbacks_);
createParam(profileFollowingErrorsString,asynParamFloat64Array, &profileFollowingErrors_);

//根据总共的轴数分配存储asynMotorAxis结构体指针的空间
pAxes_ = (asynMotorAxis**) calloc(numAxes, sizeof(asynMotorAxis*));
pollEventId_ = epicsEventMustCreate(epicsEventEmpty);  //创建用于轮询的事件
moveToHomeId_ = epicsEventMustCreate(epicsEventEmpty); 

maxProfilePoints_ = 0;
profileTimes_ = NULL;
setIntegerParam(profileExecuteState_, PROFILE_EXECUTE_DONE);

moveToHomeAxis_ = 0;

asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: constructor complete\n",
    driverName, functionName);
}

asynMotorController::~asynMotorController()
{
}

/** 在asyn客户端调用pasynManager->report()时,被调用。
  * 这个方法调用每个轴的report方法,并且接着调用基类asynPortDriver report方法。
  * 参数[in] fp FILE文件指针.
  * 参数[in] level :要输出的详细程度. 
*/

void asynMotorController::report(FILE *fp, int level)
{
  int axis;
  asynMotorAxis *pAxis;

  for (axis=0; axis<numAxes_; axis++) {
    pAxis = getAxis(axis);//根据轴编号,或者对应的asynMotorAxis结构体
    if (!pAxis) continue;
    pAxis->report(fp, level);
  }

  // 调用基类
  asynPortDriver::report(fp, level);
}


/** 当asyn客户端调用时pasynInt32->write()被调用。
  * 从pasynUser中提取功能和轴编号.
  * 设置参数库中的值.
  * 如果功能是motorStop_,则它调用pAxis->stop().
  * 如果功能是motorUpdateStatus_,则它进行一次poll并且执行一次回调.
  * 为pasynUser->reason和address,调用任何注册的回调。
  * 如果电机驱动在asynInt32接口上支持控制器专用参数,它们将重新实现这个函数。
  * 它们应该为不是控制器专用的任何参数,调用这个基类方法。
  
  *参数[in] pasynUser 编码reason和address的asynUser结构体 
  *参数 [in] value    要写的值
*/
asynStatus asynMotorController::writeInt32(asynUser *pasynUser, epicsInt32 value)
{
  // 获取功能编码 
  int function = pasynUser->reason;
  asynStatus status=asynSuccess;
  asynMotorAxis *pAxis;
  int axis;
  static const char *functionName = "writeInt32";

  // 从asynUser结构体获取对应的asynMotorAxis结构体
  pAxis = getAxis(pasynUser);
  if (!pAxis) return asynError;
  // 获取轴的编号
  axis = pAxis->axisNo_;

  /* 
     在参数库中设置这个参数和回读
  */
  pAxis->setIntegerParam(function, value);

  if (function == motorStop_) {//如果功能码motorStop_,获取加速度并且用这个加速度停止轴
    double accel;
    getDoubleParam(axis, motorAccel_, &accel);
    status = pAxis->stop(accel);
  } 
  else if (function == motorDeferMoves_) {//?
    status = setDeferredMoves(value);
  } 
  else if (function == motorClosedLoop_) {//如果功能码motorClosedLoop_,设置轴闭环情况
    status = pAxis->setClosedLoop(value);
  } 
  else if (function == motorUpdateStatus_) {//如果功能码motorUpdateStatus_
    bool moving;
    /* 做一次查询, 并且接着执行一次回调 */
    poll();
    status = pAxis->poll(&moving);
    pAxis->statusChanged_ = 1;
  } 
  else if (function == profileBuild_) {
    status = buildProfile();
  } 
  else if (function == profileExecute_) {
    status = executeProfile();
  } 
  else if (function == profileAbort_) {
    status = abortProfile();
  } 
  else if (function == profileReadback_) {
    status = readbackProfile();
  } 
  else if (function == motorMoveToHome_) {
    if (value == 1) {//进行归零的轴
      asynPrint(pasynUser, ASYN_TRACE_FLOW,
        "%s:%s:: Starting a move to home for axis %d\n",  driverName, functionName, axis);
      moveToHomeAxis_ = axis;
      epicsEventSignal(moveToHomeId_);
    }
  }

  /* 进行回调,因此更高层看到任何变化 */
  pAxis->callParamCallbacks();
  if (status)
    asynPrint(pasynUser, ASYN_TRACE_ERROR,
      "%s:%s error, status=%d axis=%d, function=%d, value=%d\n",
      driverName, functionName, status, axis, function, value);
  else
    asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
      "%s:%s:: axis=%d, function=%d, value=%d\n",
      driverName, functionName, axis, function, value);
  return status;
}

/** 当asyn客户端调用pasynFloat64->write()时,被调用.
  * 从pasynUser提取功能和轴编号.
  * 设置参数库中这个值.
  * 如果功能是motorMoveRel_, motorMoveAbs_, motorMoveVel_, motorHome_, 或 motorPosition_,
  * 则它调用pAxis->move(), pAxis->moveVelocity(), pAxis->home(), 或 pAxis->setPosition().
  * 为这个pasynUser->reason和address,调用任何已经注册的回调。
  * 如果电机驱动支持asynFloat64接口上控制器专用参数,电机驱动将重新实现这个函数。
  * 参数[in] paysnUser:编码reason和地址的结构体User
  * 参数[in] value:要写的值. */
asynStatus asynMotorController::writeFloat64(asynUser *pasynUser, epicsFloat64 value)
{
  int function = pasynUser->reason;
  double baseVelocity, velocity, acceleration;
  asynMotorAxis *pAxis;
  int axis;
  int forwards;
  int autoPower = 0;
  double autoPowerOnDelay = 0.0;
  asynStatus status = asynError;
  static const char *functionName = "writeFloat64";

  // 获取asynMotorAxis结构体
  pAxis = getAxis(pasynUser);
  if (!pAxis) return asynError;
  // 获取轴编号
  axis = pAxis->axisNo_;

  // 获取轴的两个参数
  getIntegerParam(axis, motorPowerAutoOnOff_, &autoPower);
  getDoubleParam(axis, motorPowerOnDelay_, &autoPowerOnDelay);

  /* 设置参数库中参数和回调 */
  status = pAxis->setDoubleParam(function, value);

  // 相对移动
  if (function == motorMoveRel_) {
    if (autoPower == 1) {
      status = pAxis->setClosedLoop(true);
      pAxis->setWasMovingFlag(1);
      epicsThreadSleep(autoPowerOnDelay);
    }
    // 获取轴的基速度,速度,加速度
    getDoubleParam(axis, motorVelBase_, &baseVelocity);
    getDoubleParam(axis, motorVelocity_, &velocity);
    getDoubleParam(axis, motorAccel_, &acceleration);
    // 进行相对移动
    status = pAxis->move(value, 1, baseVelocity, velocity, acceleration);
    // 设置移动结束标记
    pAxis->setIntegerParam(motorStatusDone_, 0);
    pAxis->callParamCallbacks();
    wakeupPoller();
    asynPrint(pasynUser, ASYN_TRACE_FLOW,
      "%s:%s: Set driver %s, axis %d move relative by %f, base velocity=%f, velocity=%f, acceleration=%f\n",
      driverName, functionName, portName, pAxis->axisNo_, value, baseVelocity, velocity, acceleration );

  } else if (function == motorMoveAbs_) { // 绝对移动
    if (autoPower == 1) {
      status = pAxis->setClosedLoop(true);
      pAxis->setWasMovingFlag(1);
      epicsThreadSleep(autoPowerOnDelay);
    }
    getDoubleParam(axis, motorVelBase_, &baseVelocity);
    getDoubleParam(axis, motorVelocity_, &velocity);
    getDoubleParam(axis, motorAccel_, &acceleration);
    status = pAxis->move(value, 0, baseVelocity, velocity, acceleration);
    pAxis->setIntegerParam(motorStatusDone_, 0);
    pAxis->callParamCallbacks();
    wakeupPoller();
    asynPrint(pasynUser, ASYN_TRACE_FLOW,
      "%s:%s: Set driver %s, axis %d move absolute to %f, base velocity=%f, velocity=%f, acceleration=%f\n",
      driverName, functionName, portName, pAxis->axisNo_, value, baseVelocity, velocity, acceleration );

  } else if (function == motorMoveVel_) {/以指定速度移动
    if (autoPower == 1) {
      status = pAxis->setClosedLoop(true);
      pAxis->setWasMovingFlag(1);
      epicsThreadSleep(autoPowerOnDelay);
    }
    getDoubleParam(axis, motorVelBase_, &baseVelocity);
    getDoubleParam(axis, motorAccel_, &acceleration);
    status = pAxis->moveVelocity(baseVelocity, value, acceleration);
    pAxis->setIntegerParam(motorStatusDone_, 0);
    pAxis->callParamCallbacks();
    wakeupPoller();
    asynPrint(pasynUser, ASYN_TRACE_FLOW,
      "%s:%s: Set port %s, axis %d move with velocity of %f, acceleration=%f\n",
      driverName, functionName, portName, pAxis->axisNo_, value, acceleration);

 // 注意:在asynFloat64接口上发生了motorHome命令,即使这个值(方向)是一个真正的整数
  } else if (function == motorHome_) {
    if (autoPower == 1) {
      status = pAxis->setClosedLoop(true);
      pAxis->setWasMovingFlag(1);
      epicsThreadSleep(autoPowerOnDelay);
    }
    getDoubleParam(axis, motorVelBase_, &baseVelocity);
    getDoubleParam(axis, motorVelocity_, &velocity);
    getDoubleParam(axis, motorAccel_, &acceleration);
    forwards = (value == 0) ? 0 : 1; //确定方向
    status = pAxis->home(baseVelocity, velocity, acceleration, forwards);
    pAxis->setIntegerParam(motorStatusDone_, 0);
    pAxis->callParamCallbacks();
    wakeupPoller();
    asynPrint(pasynUser, ASYN_TRACE_FLOW,
      "%s:%s: Set driver %s, axis %d to home %s, base velocity=%f, velocity=%f, acceleration=%f\n",
      driverName, functionName, portName, pAxis->axisNo_, (forwards?"FORWARDS":"REVERSE"), baseVelocity, velocity, acceleration);

  } else if (function == motorPosition_) {// 设置电机位置
    status = pAxis->setPosition(value);
    pAxis->callParamCallbacks();
    asynPrint(pasynUser, ASYN_TRACE_FLOW,
      "%s:%s: Set driver %s, axis %d to position=%f\n",
      driverName, functionName, portName, pAxis->axisNo_, value);

  } else if (function == motorEncoderPosition_) {// 设置编码器位置
    status = pAxis->setEncoderPosition(value);
    pAxis->callParamCallbacks();
    asynPrint(pasynUser, ASYN_TRACE_FLOW,
      "%s:%s: Set driver %s, axis %d to encoder position=%f\n",
      driverName, functionName, portName, pAxis->axisNo_, value);

  } else if (function == motorHighLimit_) {// 设置高限位
    status = pAxis->setHighLimit(value);
    pAxis->callParamCallbacks();
    asynPrint(pasynUser, ASYN_TRACE_FLOW,
      "%s:%s: Set driver %s, axis %d high limit=%f\n",
      driverName, functionName, portName, pAxis->axisNo_, value);

  } else if (function == motorLowLimit_) { // 设置低限位
    status = pAxis->setLowLimit(value);
    pAxis->callParamCallbacks();
    asynPrint(pasynUser, ASYN_TRACE_FLOW,
      "%s:%s: Set driver %s, axis %d low limit=%f\n",
      driverName, functionName, portName, pAxis->axisNo_, value);

  } else if (function == motorPGain_) { // 设置比率增益
    status = pAxis->setPGain(value);
    pAxis->callParamCallbacks();
    asynPrint(pasynUser, ASYN_TRACE_FLOW,
      "%s:%s: Set driver %s, axis %d proportional gain=%f\n",
      driverName, functionName, portName, pAxis->axisNo_, value);
 
  } else if (function == motorIGain_) { // 设置积分增益
    status = pAxis->setIGain(value);
    pAxis->callParamCallbacks();
    asynPrint(pasynUser, ASYN_TRACE_FLOW,
      "%s:%s: Set driver %s, axis %d integral gain=%f\n",
      driverName, functionName, portName, pAxis->axisNo_, value);

  } else if (function == motorDGain_) { //设置微分增益
    status = pAxis->setDGain(value);
    pAxis->callParamCallbacks();
    asynPrint(pasynUser, ASYN_TRACE_FLOW,
      "%s:%s: Set driver %s, axis %d derivative gain=%f\n",
      driverName, functionName, portName, pAxis->axisNo_, value);

  } else if (function == motorEncoderRatio_) { // 设置编码器比率
    status = pAxis->setEncoderRatio(value);
    pAxis->callParamCallbacks();
    asynPrint(pasynUser, ASYN_TRACE_FLOW,
      "%s:%s: Set driver %s, axis %d encoder ratio=%f\n",
      driverName, functionName, portName, pAxis->axisNo_, value);

  }
  /* 进行回调,因此更高层看到任何变化*/
  pAxis->callParamCallbacks();

  if (status)
    asynPrint(pasynUser, ASYN_TRACE_ERROR,
      "%s:%s error, status=%d axis=%d, function=%d, value=%f\n",
      driverName, functionName, status, axis, function, value);
  else
    asynPrint(pasynUser, ASYN_TRACEIO_DRIVER,
      "%s:%s:: axis=%d, function=%d, value=%f\n",
      driverName, functionName, axis, function, value);
  return status;

}

/** 当asyn客户端调用pasynFloat64Array->write()时,被调用.
  * 参数[in] pasynUser :编码reason和address的pasynUser结构体
  * 参数[in] value:指向要写入数组的指针 
  * 参数[in] nElements :要写的元素数目
*/
asynStatus asynMotorController::writeFloat64Array(asynUser *pasynUser, epicsFloat64 *value, size_t nElements)
{
  int function = pasynUser->reason;
  asynMotorAxis *pAxis;
  static const char *functionName = "writeFloat64Array";

  pAxis = getAxis(pasynUser);
  if (!pAxis) return asynError;

  // 能够写的元素数目上限为maxProfilePoints_
  if (nElements > maxProfilePoints_) nElements = maxProfilePoints_;
  // 
  if (function == profileTimeArray_) {
    memcpy(profileTimes_, value, nElements*sizeof(double));
  }
  else if (function == profilePositions_) {
    pAxis->defineProfile(value, nElements);
  }
  else {
    asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,
      "%s:%s: unknown parameter number %d\n",
      driverName, functionName, function);
    return asynError ;
  }
  return asynSuccess;
}

/** 当asyn客户端调用 pasynFloat64Array->read()时,被调用.
  * 从轮廓移动返回回调或跟随的误差数组
  * 参数[in] pasynUser 编码reason和address的pasynUser结构体.
  * 参数[in] value :指向要被读取的数组的指针
  * 参数[in] nElements:要读取元素的最大数目 .
  * 参数[in] nRead :实际范围的值数目*/
asynStatus asynMotorController::readFloat64Array(asynUser *pasynUser, epicsFloat64 *value,size_t nElements, size_t *nRead)
{
  int function = pasynUser->reason;
  asynMotorAxis *pAxis;
  int numReadbacks;
  static const char *functionName = "readFloat64Array";

  pAxis = getAxis(pasynUser);
  if (!pAxis) return asynError;

  getIntegerParam(profileNumReadbacks_, &numReadbacks);
  *nRead = numReadbacks;
  if (*nRead > nElements) *nRead = nElements;

  if (function == profileReadbacks_) {
    memcpy(value, pAxis->profileReadbacks_, *nRead*sizeof(double));
  }
  else if (function == profileFollowingErrors_) {
    memcpy(value, pAxis->profileFollowingErrors_, *nRead*sizeof(double));
  }
  else {
    asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,
      "%s:%s: unknown parameter number %d\n",
      driverName, functionName, function);
    return asynError ;
  }
  return asynSuccess;
}


/** 当asyn客户端调用pasynGenericPointer->read()时,被调用。
  * 在输入指针的内存位置构建一个聚合的MotorStatus结构体
  * 参数[in] pasynUser:编码reason和address的asynUser结构体
  * 参数[in] pointer: 指向返回的MotorStatus对象的指针
 */
asynStatus asynMotorController::readGenericPointer(asynUser *pasynUser, void *pointer)
{
  MotorStatus *pStatus = (MotorStatus *)pointer; // 指向传递的地址
  int axis;
  asynMotorAxis *pAxis;
  static const char *functionName = "readGenericPointer";

  //获取传入pasynUser指向结构体对应的asynMotorAxis地址
  pAxis = getAxis(pasynUser);
  if (!pAxis) return asynError;
  // 获取轴编号
  axis = pAxis->axisNo_;

  // ? 也是获取轴编号
  getAddress(pasynUser, &axis);
  // 获取电机状态,电机位置,电机编码器位置,电机速度
  getIntegerParam(axis, motorStatus_, (int *)&pStatus->status);
  getDoubleParam(axis, motorPosition_, &pStatus->position);
  getDoubleParam(axis, motorEncoderPosition_, &pStatus->encoderPosition);
  getDoubleParam(axis, motorVelocity_, &pStatus->velocity);
  asynPrint(pasynUser, ASYN_TRACE_FLOW,
    "%s:%s: MotorStatus = status%d, position=%f, encoder position=%f, velocity=%f\n",
    driverName, functionName, pStatus->status, pStatus->position, pStatus->encoderPosition, pStatus->velocity);
  return asynSuccess;
}

/** 返回一个指向一个asynMotorAxis对象的指针. 
  * 如果在pasynUser中编码的轴编号无效,返回NULL。
  * 派生类将重新实现这个函数,返回一个指向派生轴类型的指针
  * 输入[in] pasynUser 编码这个轴索引号的asynUser结构体 
*/
asynMotorAxis* asynMotorController::getAxis(asynUser *pasynUser)
{
    int axisNo;

    getAddress(pasynUser, &axisNo); //从pasynUser传入的结构体获取轴编号
    return getAxis(axisNo);
}

/** Processes deferred moves. 处理推迟的移动。
  * 参数[in] deferMoves : 推迟移动直到之后(true)或者现在处理移动(false)
**/
asynStatus asynMotorController::setDeferredMoves(bool deferMoves)
{
  return asynSuccess;
}

/** 返回一个指向一个asynMotorAxis的指针
  * 如果轴编号无效,返回NULL
  * 派生类将重新实现这个函数,返回一个指向派生的轴类型的指针
  * 参数[in] axisNo  轴的索引编号.
 */
asynMotorAxis* asynMotorController::getAxis(int axisNo)
{
    if ((axisNo < 0) || (axisNo >= numAxes_)) return NULL; // 传入的轴索引编号无效
    return pAxes_[axisNo];
}

/** 启动一个电机查询线程。
  * 派生类一般将在它们的构造函数接近结束时调用这个。
  * 派生类一般可以使用这个查询线程的基类实现方式,但如果需要,可以自由地重新实现它。
  * 参数[in] movingPollPeriod : 当任意轴正移动时,查询之间的时间
  * 参数[in] idlePollPeriod :当没有轴正移动时,查询之间的时间T
  * 参数[in] forcedFastPolls : 在唤醒这个poller后,执行movingPollPeriod的次数
  * 对于已经通知轴启动后,不立即报告这个轴正在移动的控制器,这需要非零。
*/
asynStatus asynMotorController::startPoller(double movingPollPeriod, double idlePollPeriod, int forcedFastPolls)
{
  movingPollPeriod_ = movingPollPeriod;
  idlePollPeriod_   = idlePollPeriod;
  forcedFastPolls_  = forcedFastPolls;
  epicsThreadCreate("motorPoller",
                    epicsThreadPriorityLow,
                    epicsThreadGetStackSize(epicsThreadStackMedium),
                    (EPICSTHREADFUNC)asynMotorPollerC, (void *)this);
  return asynSuccess;
}


/** 唤醒poller线程,使其在movingPollingPeriod_时启动查询。
  * 一般在通知一个轴移动后,这被调用,因此这个poller立即快速地启动查询。
*/
asynStatus asynMotorController::wakeupPoller()
{
  epicsEventSignal(pollEventId_);
  return asynSuccess;
}

/** 查询asynMotorController (非一个特定的asynMotorAxis).
  * 就在基类asynMotorPoller线程为每个轴调用asynMotorAxis前,它调用这个这个方法一次
  * 这个基类什么也没有实现。如果有需要被查询的整个控制器参数,派生类可以实现这个方法。
  * 在某些情况下,它也可以用于提高效率。
  * 例如,某些控制器可以在单条命令中返回所有轴的状态或位置。
  * 在此情况下,asynMotorController::poll()可读取那些信息,并且接着asynMotorAxis::poll()
  * 可以才从结果提供轴相关的信息。
  */
asynStatus asynMotorController::poll()
{
  return asynSuccess;
}

static void asynMotorPollerC(void *drvPvt)
{
  asynMotorController *pController = (asynMotorController*)drvPvt;
  pController->asynMotorPoller();
}

/** 默认的poller函数,它运行在由asynMotorController::startPoller()创建的线程中。
  * 这个基类实现可以被大多数派生类使用
  * 它在没有轴移动时以idlePollPeriod_查询,而在任何轴在移动时以movingPollPeriod_查询
  * 当asynMotorController::wakeupPoller()被调用时,它将立即进行一次查询,并且如果没有轴在移动,在恢复到idlePollPeriod_前,用movingPollPeriod进行forcedFastPolls_次。
  * 当它正在查询时,它获取端口驱动上的锁。
  */
void asynMotorController::asynMotorPoller()
{
  double timeout;
  int i;
  int forcedFastPolls=0;
  bool anyMoving;
  bool moving;
  epicsTimeStamp nowTime;
  double nowTimeSecs = 0.0;
  asynMotorAxis *pAxis;
  int autoPower = 0;
  double autoPowerOffDelay = 0.0;
  int status;

  timeout = idlePollPeriod_;
  wakeupPoller();  /* 启动时,执行查询 */

  while(1) {
    // 根据timeout,带或不带超时时间地等待查询信号
    if (timeout != 0.) status = epicsEventWaitWithTimeout(pollEventId_, timeout);
    else               status = epicsEventWait(pollEventId_);
    if (status == epicsEventWaitOK) {
      /* 
       * 我们获取了一个事件,而不是超时。这是因为其它软件知道一个轴应该已经更高了状态
       * (启动地移动等)。执行最少次数的快速查询,因为前几次查询,控制器状态可能还没有变化。
       */
      forcedFastPolls = forcedFastPolls_;
    }
    anyMoving = false;
    lock();
    if (shuttingDown_) {
      unlock();
      break;
    }

    poll();
    // 根据轴编号,获取对应的asynMotorAxis结构体
    for (i=0; i<numAxes_; i++) {
      pAxis=getAxis(i);
      if (!pAxis) continue;

      // 获取motorPowerAutoOnOff_和motorPowerOffDelay_索引对应的参数值
      getIntegerParam(i, motorPowerAutoOnOff_, &autoPower);
      getDoubleParam(i, motorPowerOffDelay_, &autoPowerOffDelay);

      pAxis->poll(&moving);
      if (moving) {
        anyMoving = true;
        pAxis->setWasMovingFlag(1);
      } else {// 之前设置了移动标记,但现在停止了
        if ((pAxis->getWasMovingFlag() == 1) && (autoPower == 1)) {
          pAxis->setDisableFlag(1);
          pAxis->setWasMovingFlag(0);
          epicsTimeGetCurrent(&nowTime);
          pAxis->setLastEndOfMoveTime(nowTime.secPastEpoch + (nowTime.nsec / 1.e9));
        }
      }

      // 如果发现了移动结束,
      // 我们不再移动,
      // 电源自动关闭使能
      // 电源自动关闭延时计时器失效
      // 电源自动关闭驱动
      if ((!moving) && (autoPower == 1) && (pAxis->getDisableFlag() == 1)) {
        epicsTimeGetCurrent(&nowTime);
        nowTimeSecs = nowTime.secPastEpoch + (nowTime.nsec / 1.e9);
        if ((nowTimeSecs - pAxis->getLastEndOfMoveTime()) >= autoPowerOffDelay) {
          pAxis->setClosedLoop(0);
          pAxis->setDisableFlag(0);
        }
      }

    }
    if (forcedFastPolls > 0) {
      timeout = movingPollPeriod_;
      forcedFastPolls--;
    } else if (anyMoving) {
      timeout = movingPollPeriod_;
    } else {
      timeout = idlePollPeriod_;
    }
    unlock();
  }
}

/**
 * 启动处理移动轴到它们归零位置的线程
 * 在对象初始化时,这是被派生的具体控制器类调用,因此,不需要这个功能的驱动没有这个线程的开销
 */
asynStatus asynMotorController::startMoveToHomeThread()
{
  epicsThreadCreate("motorMoveToHome",
                    epicsThreadPriorityMedium,
                    epicsThreadGetStackSize(epicsThreadStackMedium),
                    (EPICSTHREADFUNC)asynMotorMoveToHomeC, (void *)this);
  return asynSuccess;
}

static void asynMotorMoveToHomeC(void *drvPvt)
{
  asynMotorController *pController = (asynMotorController*)drvPvt;
  pController->asynMotorMoveToHome();
}



/**
 *  默认移动到归零的线程。不通常被重写
 */
void asynMotorController::asynMotorMoveToHome()
{

  asynMotorAxis *pAxis;
  int status = 0;
  static const char *functionName = "asynMotorMoveToHome";

  while(1) {
    status = epicsEventWait(moveToHomeId_); // 等待信号moveToHomeId_
    if (status == epicsEventWaitOK) {
      pAxis = getAxis(this->moveToHomeAxis_); // 更加归零轴编号,获取对应的asynMotorAxis对象
      if (!pAxis) continue;
      status = pAxis->doMoveToHome(); // 调用轴对象的归零方法
      if (status) {
      asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,
        "%s:%s: move to home failed in asynMotorController::asynMotorMoveToHome. Axis number=%d\n",
        driverName, functionName, this->moveToHomeAxis_);
      }
    }
  }
}


/** 写字符串到控制器。
  * 用字符串的默认要写位置和默认超时时间调用writeController()。
  */
asynStatus asynMotorController::writeController()
{ // 字符串默认位置在outString_, 默认超时时间DEFAULT_CONTROLLER_TIMEOUT
  return writeController(outString_, DEFAULT_CONTROLLER_TIMEOUT);
}

/**  写一个字符串到控制器
  *  参数[in] output :要被写的字符串.
  *  参数[in] timeout : 在返回一个错误前的超时时间.*/
asynStatus asynMotorController::writeController(const char *output, double timeout)
{
  size_t nwrite;
  asynStatus status;
  // const char *functionName="writeController";

  status = pasynOctetSyncIO->write(pasynUserController_, output,
                                   strlen(output), timeout, &nwrite);

  return status ;
}

/** 写一个字符串到控制器并且回读响应,用输入和输出字符串的默认位置和默认事件调用 writeReadController()
 */
asynStatus asynMotorController::writeReadController()
{
  size_t nread;
  return writeReadController(outString_, inString_, sizeof(inString_), &nread, DEFAULT_CONTROLLER_TIMEOUT);
}

/** Writes a string to the controller and reads a response 写一个字符串到控制器并读取响应.
  * 参数[in] output :指向输出字符串的指针
  * 参数[out] input :指向输入字符串位置的指针
  * 参数[in] maxChars : 输入缓冲区的尺寸
  * 参数[out] nread :Number of characters read. 读取字符的数目
  * 参数[out] timeout :在返回错误前的超时时间T
*/
asynStatus asynMotorController::writeReadController(const char *output, char *input,
                                                    size_t maxChars, size_t *nread, double timeout)
{
  size_t nwrite;
  asynStatus status;
  int eomReason;
  // const char *functionName="writeReadController";

  status = pasynOctetSyncIO->writeRead(pasynUserController_, output,
                                       strlen(output), input, maxChars, timeout,
                                       &nwrite, nread, &eomReason);

  return status;
}



/* 这些是用于轮廓移动的函数 */
/**  初始化一个多轴的轮廓移动 */
asynStatus asynMotorController::initializeProfile(size_t maxProfilePoints)
{
  int axis;
  asynMotorAxis *pAxis;
  // static const char *functionName = "initializeProfile";

  maxProfilePoints_ = maxProfilePoints;
  if (profileTimes_) free(profileTimes_);
  profileTimes_ = (double *)calloc(maxProfilePoints, sizeof(double));
  for (axis=0; axis<numAxes_; axis++) {
    pAxis = getAxis(axis);
    if (!pAxis) continue;
    pAxis->initializeProfile(maxProfilePoints);
  }
  return asynSuccess;
}

/** 构建一个多轴的轮廓移动 */
asynStatus asynMotorController::buildProfile()
{
  //static const char *functionName = "buildProfile";
  asynMotorAxis *pAxis;
  int i;
  int status=0;
  double time;
  int timeMode;
  int numPoints;

  status |= getIntegerParam(profileTimeMode_, &timeMode);
  status |= getDoubleParam(profileFixedTime_, &time);
  status |= getIntegerParam(profileNumPoints_, &numPoints);
  if (status) return asynError;
  if (timeMode == PROFILE_TIME_MODE_FIXED) {
    memset(profileTimes_, 0, maxProfilePoints_*sizeof(double));
    for (i=0; i<numPoints; i++) {
      profileTimes_[i] = time;
    }
  }
  for (i=0; i<numAxes_; i++) {
    pAxis = getAxis(i);
    if (!pAxis) continue;
    pAxis->buildProfile();
  }
  return asynSuccess;
}

/** 执行一个多轴的轮廓移动 */
asynStatus asynMotorController::executeProfile()
{
  // static const char *functionName = "executeProfile";
  int axis;
  asynMotorAxis *pAxis;

  for (axis=0; axis<numAxes_; axis++) {
    pAxis = getAxis(axis);
    if (!pAxis) continue;
    pAxis->executeProfile();
  }
  return asynSuccess;
}

/** 取消一个轮廓移动 */
asynStatus asynMotorController::abortProfile()
{
  // static const char *functionName = "abortProfile";
  int axis;
  asynMotorAxis *pAxis;

  for (axis=0; axis<numAxes_; axis++) {
    pAxis = getAxis(axis);
    if (!pAxis) continue;
    pAxis->abortProfile();
  }
  return asynSuccess;
}

/** 从一个多轴的轮廓移动回读实际电机位置  */
asynStatus asynMotorController::readbackProfile()
{
  // static const char *functionName = "readbackProfile";
  int axis;
  asynMotorAxis *pAxis;

  for (axis=0; axis<numAxes_; axis++) {
    pAxis = getAxis(axis);
    if (!pAxis) continue;
    pAxis->readbackProfile();
  }
  return asynSuccess;
}

/** 在运行时,设置移动查询周期(秒为单位) */
asynStatus asynMotorController::setMovingPollPeriod(double movingPollPeriod)
{
  static const char *functionName = "setMovingPollPeriod";

  asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: Setting moving poll period to %f\n",
  driverName, functionName, movingPollPeriod);

  lock();
  movingPollPeriod_ = movingPollPeriod;
  wakeupPoller();
  unlock();
  return asynSuccess;
}

/**  运行时,设置空闲的轮询周期(以秒为单位)*/
asynStatus asynMotorController::setIdlePollPeriod(double idlePollPeriod)
{
  static const char *functionName = "setIdlePollPeriod";

  asynPrint(pasynUserSelf, ASYN_TRACE_FLOW, "%s:%s: Setting idle poll period to %f\n",
  driverName, functionName, idlePollPeriod);

  lock();
  idlePollPeriod_ = idlePollPeriod;
  wakeupPoller();
  unlock();
  return asynSuccess;
}

/**
    以下函数有C可链接的,并且可以直接或者从iocsh被调用
*/

extern "C" {

asynStatus setMovingPollPeriod(const char *portName, double movingPollPeriod)
{
  asynMotorController *pC;
  static const char *functionName = "setMovingPollPeriod";
  // 通过端口名称,查找asynMotorController结构体
  pC = (asynMotorController*) findAsynPortDriver(portName);
  if (!pC) {
    printf("%s:%s: Error port %s not found\n", driverName, functionName, portName);
    return asynError;
  }
  //在运行时,设置移动查询周期(秒为单位)
  return pC->setMovingPollPeriod(movingPollPeriod);
}

asynStatus setIdlePollPeriod(const char *portName, double idlePollPeriod)
{
  asynMotorController *pC;
  static const char *functionName = "setIdlePollPeriod";

  pC = (asynMotorController*) findAsynPortDriver(portName);
  if (!pC) {
    printf("%s:%s: Error port %s not found\n", driverName, functionName, portName);
    return asynError;
  }
  // 运行时,设置空闲的轮询周期(以秒为单位)
  return pC->setIdlePollPeriod(idlePollPeriod);
}


/*
*/
asynStatus asynMotorEnableMoveToHome(const char *portName, int axis, int distance)
{
  asynMotorController *pC = NULL;
  asynMotorAxis *pA = NULL;
  static const char *functionName = "asynMotorEnableMoveToHome";

  pC = (asynMotorController*) findAsynPortDriver(portName);
  if (!pC) {
    printf("%s:%s: Error port %s not found\n", driverName, functionName, portName);
    return asynError;
  }

  pA = pC->getAxis(axis);
  if (!pA) {
    printf("%s:%s: Error axis %d not found\n", driverName, functionName, axis);;
    return asynError;
  }

  if (distance<=0) {
    printf("%s:%s: Error distance must be positive integer axis=%d\n", driverName, functionName, axis);
  } else {
    pA->setReferencingModeMove(distance);
  }

  return asynSuccess;
}


/* setMovingPollPeriod */
static const iocshArg setMovingPollPeriodArg0 = {"Controller port name", iocshArgString};
static const iocshArg setMovingPollPeriodArg1 = {"Axis number", iocshArgDouble};
static const iocshArg * const setMovingPollPeriodArgs[] = {&setMovingPollPeriodArg0,
                                                           &setMovingPollPeriodArg1};
static const iocshFuncDef setMovingPollPeriodDef = {"setMovingPollPeriod", 2, setMovingPollPeriodArgs};

static void setMovingPollPeriodCallFunc(const iocshArgBuf *args)
{
  setMovingPollPeriod(args[0].sval, args[1].dval);
}

/* setIdlePollPeriod */
static const iocshArg setIdlePollPeriodArg0 = {"Controller port name", iocshArgString};
static const iocshArg setIdlePollPeriodArg1 = {"Axis number", iocshArgDouble};
static const iocshArg * const setIdlePollPeriodArgs[] = {&setIdlePollPeriodArg0,
                                                         &setIdlePollPeriodArg1};
static const iocshFuncDef setIdlePollPeriodDef = {"setIdlePollPeriod", 2, setIdlePollPeriodArgs};

static void setIdlePollPeriodCallFunc(const iocshArgBuf *args)
{
  setIdlePollPeriod(args[0].sval, args[1].dval);
}


/* asynMotorEnableMoveToHome */
static const iocshArg asynMotorEnableMoveToHomeArg0 = {"Controller port name", iocshArgString};
static const iocshArg asynMotorEnableMoveToHomeArg1 = {"Axis number", iocshArgInt};
static const iocshArg asynMotorEnableMoveToHomeArg2 = {"Distance", iocshArgInt};
static const iocshArg * const asynMotorEnableMoveToHomeArgs[] = {&asynMotorEnableMoveToHomeArg0,  &asynMotorEnableMoveToHomeArg1,                                                                                                           &asynMotorEnableMoveToHomeArg2};
static const iocshFuncDef enableMoveToHome = {"asynMotorEnableMoveToHome", 3, asynMotorEnableMoveToHomeArgs};

static void enableMoveToHomeCallFunc(const iocshArgBuf *args)
{
  asynMotorEnableMoveToHome(args[0].sval, args[1].ival, args[2].ival);
}


static void asynMotorControllerRegister(void)
{
  iocshRegister(&setMovingPollPeriodDef, setMovingPollPeriodCallFunc);
  iocshRegister(&setIdlePollPeriodDef, setIdlePollPeriodCallFunc);
  iocshRegister(&enableMoveToHome, enableMoveToHomeCallFunc);
}
epicsExportRegistrar(asynMotorControllerRegister);

} //extern C

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/873800.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《Attention Is All You Need》论文导读

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl论文背景 《Attention Is All You Need》这篇具有里程碑意义的论文,彻底改变了自然语言处理(NLP)的研究和应用格局。在此之前,循环神经网络(RNN)及其变体,如长短期记忆网络(LSTM),是处理…

【原创】java+springboot+mysql学生信息管理系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

内网穿透的应用-本地化部署Elasticsearch平替工具OpenObserve并实现无公网IP远程分析数据

文章目录 前言1. 安装Docker2. Docker镜像源添加方法3. 创建并启动OpenObserve容器4. 本地访问测试5. 公网访问本地部署的OpenObserve5.1 内网穿透工具安装5.2 创建公网地址 6. 配置固定公网地址 前言 本文主要介绍如何在Linux系统使用Docker快速本地化部署OpenObserve云原生可…

景联文科技:提供高质量多模态数据标注,推动智能化转型

随着人工智能技术的快速发展&#xff0c;多模态数据标注成为推动智能系统更深层次理解和应用的关键技术之一。 作为行业领先的多模态数据标注服务商&#xff0c;景联文科技凭借其在技术、流程和人才方面的综合优势&#xff0c;推出了全面的多模态标注解决方案&#xff0c;助力…

网上花店管理系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;客服聊天管理&#xff0c;基础数据管理&#xff0c;论坛交流管理&#xff0c;公告信息管理&#xff0c;用户管理&#xff0c;轮播图信息 微信端账号功能包括&#xff1a;系统首…

微波无源器件2 用于双极化波束形成网络的增强型双极化定向耦合器

摘要&#xff1a; 定向耦合器和混合相移器是用于实现波束形成网络的关键器件。通常一个波束形成网络用线极化和正交极化两个极化给天线馈电。双极化器件被用于降低波束形成网络的复杂性和尺寸。双极化定向耦合器由相同的作者提出。一种增强型的双极化耦合器在本文中提出。此器件…

【Java 优选算法】双指针(上)

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 移动零 分析 代码 复写零 分析 代码 快乐数 分析 代码 盛最多水的容器 分析 代码 移动零 题目链接 分析 双指针算法,利用两个指针cur和dest将数组划分为三个区间…

基于Java的垃圾分类网站系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot框架&#xff0c;B/S架构 工具&#xff1a;MyEclipse, Tomcat 系统展示 首页 用户管理…

面试笔试 场景题(部分总结)

文章目录 题目--找出一堆随机数中的前 k 大数字PriorityQueue 类PriorityQueue 常用方法 题目--数组中的第 K 个最大元素题目--二叉搜索树中第 K 小的元素 题目–找出一堆随机数中的前 k 大数字 找出一堆随机数中的前 k 大数字(小根堆)&#xff0c;找出一堆随机数中的前 k 小数…

捷途山海T2纯电续航突破200km,直达208km!

若你向我询问“方盒子”造型的SUV该如何选择&#xff0c;我会毫不犹豫地推荐捷途山海T2。这款车型以其独特的硬派风格&#xff0c;在众多SUV中脱颖而出。不同于坦克300和北京BJ40的单一性格&#xff0c;捷途山海T2在双电机与高性能电池组的共同加持下&#xff0c;展现出了更为全…

大模型好书分享:《精通Transformer,从零开始构建最先进的NLP模型》(附PDF)

这本大模型书籍我已经上传CSDN&#xff0c;朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】 内容简介 国内第1本Transformer——变形金刚红书 如果一定要说未来谁能引领人工智能世界&#xff0c;是Transformer而非chatGPT&#xff01; 编…

python-新冠病毒

题目描述 假设我们掌握了特定时间段内特定城市的新冠病毒感染病例的信息。在排名 i 的当天有 i 个案例&#xff0c;即&#xff1a; 第一天有一例感染第二天有两例感染第三天有三例感染以此类推...... 请计算 n 天内的感染总数和每天平均感染数。 输入 整数 n 表示天数&…

免费的文章生成器有哪些?盘点5款为你自动生成文章

文章生成器的普及&#xff0c;为创作者提供了全新的创作视角和效率提升途径。那么&#xff0c;市面上有哪些免费的文章生成器可供我们使用呢&#xff1f;接下来&#xff0c;本文将为大家详细介绍5款功能强大、操作简便的免费文章生成器&#xff0c;它们将有助大家在内容创作的道…

基于人工智能的智能农业监控系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 智能农业是利用现代信息技术和人工智能进行农业生产的优化管理&#xff0c;通过实时监控和预测系统&#xff0c;可以改善作物的生产效…

KAN 学习 Day4 —— MultKAN 正向传播代码解读及测试

在KAN学习Day1——模型框架解析及HelloKAN中&#xff0c;我对KAN模型的基本原理进行了简单说明&#xff0c;并将作者团队给出的入门教程hellokan跑了一遍&#xff1b; 在KAN 学习 Day2 —— utils.py及spline.py 代码解读及测试中&#xff0c;我对项目的基本模块代码进行了解释…

顶级出图效果!免费在线使用FLux.1 模型,5s出图无限制!

最近发现一个可以在线免费使用 FLux.1 模型 生成图片的AI工具。 先看效果图&#xff1a; 工具不需要登录即可使用&#xff0c;目前还是完全免费的&#xff0c;国内可以直接使用。 在提示词输入框直接输入提示词即可&#xff0c;选择图片比例之后&#xff0c;直接生图。 出图的…

24年9月通信基础知识补充1

看文献过程中不断发现有太多不懂的基础知识&#xff0c;故长期更新这类blog不断补充在这过程中学到的知识。由于这些内容与我的研究方向并不一定强相关&#xff0c;故记录不会很深入请见谅。 【通信基础知识补充2】9月通信基础知识补充1 一、Zadoff-Chu 序列1.1 Zadoff-Chu 序列…

3GPP协议入门——物理层基础(一)

1. 频段/带宽 NR指定了两个频率范围&#xff0c;FR1&#xff1a;通常称Sub 6GHz&#xff0c;也称低频5G&#xff1b;FR2&#xff1a;通常称毫米波&#xff08;Millimeter Wave&#xff09;&#xff0c;也称高频5G。 2. 子载波间隔 NR中有15kHz&#xff0c;30kHz&#xff0c;6…

C++——入门基础(下)

目录 一、引用 &#xff08;1&#xff09;引用的概念和定义 &#xff08;2&#xff09;引用的特性 &#xff08;3&#xff09;引用的使用 &#xff08;4&#xff09;const引用 &#xff08;5&#xff09;指针和引用的关系 二、inline 三、nullptr 四、写在最后 一、引用…

带相对位置表示的自注意力(201803)

Self-Attention with Relative Position Representations 带相对位置表示的自注意力 https://arxiv.org/pdf/1803.02155v1 Abstract Relying entirely on an attention mechanism, the Transformer introduced by Vaswani et al. (2017) achieves state-of-the-art results …