轴对象的基类,具体的轴类是派生自这个类,控制器类是其友元类。
派生类根据需要实现以下方法:
asynMotorAxis.h 头文件:
/* asynMotorAxis.h
** 这个文件为一个asynMotorAxis定义了一个基类。
** 真实电机从这个类派生
*/
#ifndef asynMotorAxis_H
#define asynMotorAxis_H
#include <epicsEvent.h>
#include <epicsTypes.h>
#ifdef __cplusplus
#include <asynPortDriver.h>
#include "asynMotorController.h"
class epicsShareFunc asynMotorAxis {
public:
/* 这是这个类的构造器:控制器对象的指针以及这个轴编号 */
asynMotorAxis(class asynMotorController *pController, int axisNumber);
// 根据索引设置参数库中的整数变量
virtual asynStatus setIntegerParam(int index, int value);
// 根据索引设置参数库中的双精度浮点变量
virtual asynStatus setDoubleParam(int index, double value);
virtual void report(FILE *fp, int details);
virtual asynStatus callParamCallbacks();
// 移动:要移动的位置,相对或绝对,最低速度,最高速度,加速度
// 要移动的位置以step为单位
virtual asynStatus move(double position, int relative, double minVelocity, double maxVelocity, double acceleration);
//以一个指定速度移动
virtual asynStatus moveVelocity(double minVelocity, double maxVelocity, double acceleration);
//归零:最低速度,最高速度,加速度以及方向
virtual asynStatus home(double minVelocity, double maxVelocity, double acceleration, int forwards);
//轴停止
virtual asynStatus stop(double acceleration);
//轴轮询
virtual asynStatus poll(bool *moving);
//设置轴位置
virtual asynStatus setPosition(double position);
//设置编码器位置
virtual asynStatus setEncoderPosition(double position);
//设置上限位
virtual asynStatus setHighLimit(double highLimit);
//设置下限位
virtual asynStatus setLowLimit(double lowLimit);
//设置比例增益
00040 virtual asynStatus setPGain(double pGain);
//设置积分增益
virtual asynStatus setIGain(double iGain);
//设置微分增益
00042 virtual asynStatus setDGain(double dGain);
//设置是否闭环
virtual asynStatus setClosedLoop(bool closedLoop);
//设置编码器比率
virtual asynStatus setEncoderRatio(double ratio);
virtual asynStatus doMoveToHome();
virtual asynStatus initializeProfile(size_t maxPoints);
virtual asynStatus defineProfile(double *positions, size_t numPoints);
virtual asynStatus buildProfile();
virtual asynStatus executeProfile();
virtual asynStatus abortProfile();
virtual asynStatus readbackProfile();
void setReferencingModeMove(int distance);
int getReferencingModeMove();
protected:
class asynMotorController *pC_; //所属控制器对象
int axisNo_; //轴编号
asynUser *pasynUser_; //这个轴的使用者
double *profilePositions_;
double *profileReadbacks_;
double *profileFollowingErrors_;
int referencingMode_;
private:
/*
* typedef struct MotorStatus {
* double position; //命令电机位置
* double encoderPosition; //实际编码器位置
* double velocity; //实际速度
* epicsUInt32 status; //包含状态位(运动结束,限位等)的字
* } MotorStatus;
*/
MotorStatus status_;
int statusChanged_;
int referencingModeMove_;
friend class asynMotorController;
};
#endif /* _cplusplus */
#endif /* asynMotorAxis_H */
asynMotorAxis.cpp 实现:
/* asynMotorAxis.cpp
** 这个文件为asynMotorAxis定义了基类。真实电机轴从这个类派生
**
*/
#include <stdlib.h>
#include <string.h>
#include <epicsThread.h>
#include <asynPortDriver.h>
#define epicsExportSharedSymbols
#include <shareLib.h>
#include "asynMotorAxis.h"
#include "asynMotorController.h"
static const char *driverName = "asynMotorAxis";
/** 创建一个asynMotorAxis对象.
* 参数[in] pC :指向这个轴所属的asynMotorController的指针。
* 参数[in] axisNo:这个轴的编号,范围从0到pC->numAxes_-1.
*
* 检查pC不为空,并且axisNo在有效范围内。
* 在pC->pAxes[axisNo_]中设置一个指针指向自身
* 连接pasynUser_到这个asyn端口和asynNo
*/
asynMotorAxis::asynMotorAxis(class asynMotorController *pC, int axisNo)
: pC_(pC), axisNo_(axisNo), statusChanged_(1)
{
static const char *functionName = "asynMotorAxis";
if (!pC) {// 检查传入pC不为空
printf("%s:%s: Error, controller is NULL\n",driverName, functionName);
return;
}
// 检查axisNo在有效范围内
if ((axisNo < 0) || (axisNo >= pC->numAxes_)) {
printf("%s:%s: Error, axis=%d is not in range 0 to %d\n",
driverName, functionName, axisNo, pC->numAxes_-1);
return;
}
//pC->pAxes_的第axisNo索引指向自身
pC->pAxes_[axisNo] = this;
//初始状态置位0
status_.status = 0;
profilePositions_ = NULL;
profileReadbacks_ = NULL;
profileFollowingErrors_ = NULL;
/* 用于在驱动程序中跟踪参考模式 */
referencingMode_ = 0;
/* 用于启用/禁用移动到零位,并且告诉驱动程序要移动多远 */
referencingModeMove_ = 0;
wasMovingFlag_ = 0;
disableFlag_ = 0;
lastEndOfMoveTime_ = 0;
// 创建一个asynUser, 连接到这个轴
/* 创建一个asynUser. 调用者指定两个回调process和timeout。
这些回调仅由queueRequest的结果被调用。timeout是可选的。
位errorMesage分配errorMessageSize个字符。存储量不能被更改。
如果这个方法不能分配存储区,它不返回。
*/
pasynUser_ = pasynManager->createAsynUser(NULL, NULL);
/* 连接这个asynUser结构体到由portName,addr指定的一个设备。
这个端口号与在registerPort中指定的端口名相同。如果这个asynUser已经连接了一个设备,
这个调用将出错。如果端口不支持多设备,则忽略addr。connectDevice仅位portName,addr连接这个asynUser到端口驱动。端口驱动可能连接了实际设备,也可能没有连接。因而,connectDevice和
asynCommon:connect完全不同。
*/
pasynManager->connectDevice(pasynUser_, pC->portName, axisNo);
}
asynMotorAxis::~asynMotorAxis()
{
}
/** 移动单机到一个绝对位置或者一个相对量。
* 参数[in] position:如果relavtive=0,移动到绝对位置,relavtive=1移动一个相对距离 Units=steps.
* 参数[in] relative:指明相对移动(1)或者绝对移动(0)的标记
* 参数[in] minVelocity:初始速度,通常称为基速度,Units=steps/sec.
* 参数[in] maxVelocity:最大速度,通常称为回转速度Units=steps/sec.
* 参数[in] acceleration 加速度值. Units=steps/sec/sec.
*/
asynStatus asynMotorAxis::move(double position, int relative, double minVelocity, double maxVelocity, double acceleration)
{
return asynSuccess;
}
/** 在告诉停止前,以固定速度移动电机。
* 参数[in] minVelocity:初始速度,通常称为基速度,Units=steps/sec.Units=steps/sec.
* 参数[in] maxVelocity: 最大速度,通常称为回转速度Units=steps/sec.
* 参数[in] acceleration: 加速度值. Units=steps/sec/sec.
*/
asynStatus asynMotorAxis::moveVelocity(double minVelocity, double maxVelocity, double acceleration)
{
return asynSuccess;
}
/** 移动电机到归零位置。
* 参数[in] minVelocity :初始速度,通常称为基速度,Units=steps/sec.
* 参数[in] maxVelocity :最大速度,通常称为回转速度Units=steps/sec.
* 参数[in] acceleration: 加速度值. Units=steps/sec/sec.
* 参数[in] forwards : 指明电机在正向(1)或反向(0)移动电机的标志
* 某些电机需要告诉方向,另外知道哪个方向上归零。
*/
asynStatus asynMotorAxis::home(double minVelocity, double maxVelocity, double acceleration, int forwards)
{
return asynSuccess;
}
/** 停止电机
*参数[in] acceleration :加速度值. Units=steps/sec/sec. */
asynStatus asynMotorAxis::stop(double acceleration)
{
return asynSuccess;
}
/** Poll the axis.查询这个轴。 这个函数应该读取控制器位置,编码器位置和硬件支持一样多的*motorStatus标记。它应该位它查询的每个项调用setIntegerParam() 和 setDoubleParam(),并且
*最后调用callParamCallbacks()。
* 参数[out] moving:函数必须设定这个标记,表明轴是移动(1)或者结束(0)。
*/
asynStatus asynMotorAxis::poll(bool *moving)
{
return asynSuccess;
}
/** 设置电机的当前位置。
* 参数[in] position:应该设置到硬件中的新绝对电机位置。 Units=steps.
*/
asynStatus asynMotorAxis::setPosition(double position)
{
return asynSuccess;
}
/** 设置电机的当前编码器位置
* 参数[in] position:应该设置到硬件中的新绝对编码器位置 Units=steps.
*/
asynStatus asynMotorAxis::setEncoderPosition(double position)
{
return asynSuccess;
}
/** 设置电机的上限位位置
* 参数in] highLimit:应该设定到硬件中新的上限位置.Units=steps.
*/
asynStatus asynMotorAxis::setHighLimit(double highLimit)
{
return asynSuccess;
}
/** 设置电机的下限位位置
* 参数[in] lowLimit:应该设定到硬件中新的下限位置.Units=steps.
*/
asynStatus asynMotorAxis::setLowLimit(double lowLimit)
{
return asynSuccess;
}
/** 设置电机比例增益
*参数[in] pGain :新的比例增益
*/
asynStatus asynMotorAxis::setPGain(double pGain)
{
return asynSuccess;
}
/** 设置电机积分增益
* 参数[in] iGain 新的积分增益.
*/
asynStatus asynMotorAxis::setIGain(double iGain)
{
return asynSuccess;
}
/** 设置电机微分增益
* 参数[in] dGain 新的微分增益
*/
asynStatus asynMotorAxis::setDGain(double dGain)
{
return asynSuccess;
}
/** 设置电机闭环状态
* 参数[in] closedLoop true = 闭环, false = 开环 */
asynStatus asynMotorAxis::setClosedLoop(bool closedLoop)
{
return asynSuccess;
}
/** 设置电机编码器比率
* 参数[in] ratio 新的编码器比率 */
asynStatus asynMotorAxis::setEncoderRatio(double ratio)
{
return asynSuccess;
}
void asynMotorAxis::report(FILE *fp, int details)
{
}
/**
* doMoveToHome的默认实现,派生类需要实现这个函数实际执行这个轴移动到归零位置
*/
asynStatus asynMotorAxis::doMoveToHome()
{
static const char *functionName="doMoveToHome";
asynPrint(pasynUser_, ASYN_TRACE_ERROR,
"%s:%s: Axis=%d no implementation\n",
driverName, functionName, pC_->moveToHomeAxis_);
return asynSuccess;
}
/**
* referencingModeMove_的设置方法
*/
void asynMotorAxis::setReferencingModeMove(int distance)
{
referencingModeMove_ = distance;
}
/**
* referencingModeMove_的获取方法
*/
int asynMotorAxis::getReferencingModeMove()
{
return referencingModeMove_;
}
//我们实现setIntegerParam, setDoubleParam, 和 callParamCallbacks,因此我们可以构造
//聚合状态结构体并且对其进行回调。
/*
在参数库中为这个轴的一个整数设置值。
如果这个参数是motorStatus参数 (motorStatusDirection_, motorStatusHomed_等)之一,这个函数采取特殊操作。在这种情况中,它置位或清零其私有MotorStatus.status结构体中相应的位,并且当导演callParamCallbacks()时,如果状态发生变化,设置一个标记对devMotorAsyn进行回调。
参数[in] function :功能(参数)编号
参数[in] value:要设定的值
*/
asynStatus asynMotorAxis::setIntegerParam(int function, int value)
{
int mask;
epicsUInt32 status=0;
// 这假设以上定义的参数与电机基类预计的位相同顺序
/*
这些是状态位
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_;
*/
if (function >= pC_->motorStatusDirection_ &&
function <= pC_->motorStatusHomed_) {
status = status_.status; // 获取状态
mask = 1 << (function - pC_->motorStatusDirection_);
if (value) status |= mask;
else status &= ~mask;
if (status != status_.status) {//状态发生了变化
status_.status = status;
statusChanged_ = 1;
}
}
// 设置新的状态
pC_->setIntegerParam(axisNo_, pC_->motorStatus_, status);
return pC_->setIntegerParam(axisNo_, function, value);
}
/** 在参数库中位这个轴的一个double设置值.
* 如果这个参数是motorPosition_ 或 motorEncoderPosition_采取特殊操作。
* 在这种情况下,它设置私有MotorStatus结构体中这个值,并且如果这个值发生变化,则
* 设置置位一个在callParamCallbacks()被调用时对devMotorAsyn进行回调的标记
* 参数[in] function :功能(参数)编号
* 参数[in] value : 要设置的值
*/
asynStatus asynMotorAxis::setDoubleParam(int function, double value)
{
if (function == pC_->motorPosition_) {
if (value != status_.position) {// 传入的电机位置不能与状态结构体中的电机位置
statusChanged_ = 1;
status_.position = value;
}
} else if (function == pC_->motorEncoderPosition_) {
if (value != status_.encoderPosition) {
statusChanged_ = 1;
status_.encoderPosition = value;
}
}
// 调用控制类对象,存储这个功能编号对应的值
return pC_->setDoubleParam(axisNo_, function, value);
}
/**
* 位参数库中这个轴的一个字符串设置值
* 参数[in] function: 功能(参数)编号
* 参数[in] value: 要设定的值
*/
asynStatus asynMotorAxis::setStringParam(int function, const char *value)
{
// 调用控制类对象的setStringParam
return pC_->setStringParam(axisNo_, function, value);
}
/** 为在参数库中对应这个轴已经发生变化的任何参数,调用回调函数。
*如果聚合结构体已经发生变化,这个参数采取特殊操作。
*在这种情况,它对asynGenericPointer进行回调,尤其对devMotorAsyn
*/
asynStatus asynMotorAxis::callParamCallbacks()
{
if (statusChanged_) {
statusChanged_ = 0;
pC_->doCallbacksGenericPointer((void *)&status_, pC_->motorStatus_, axisNo_);
}
return pC_->callParamCallbacks(axisNo_);
}
/* 这些函数用于轮廓移动 */
asynStatus asynMotorAxis::initializeProfile(size_t maxProfilePoints)
{
//轮廓移动的目标位置数组
if (profilePositions_) free(profilePositions_);
profilePositions_ = (double *)calloc(maxProfilePoints, sizeof(double));
//轮廓移动的回读位置数组
if (profileReadbacks_) free(profileReadbacks_);
profileReadbacks_ = (double *)calloc(maxProfilePoints, sizeof(double));
// 以下轮廓移动的误差的数组
if (profileFollowingErrors_) free(profileFollowingErrors_);
profileFollowingErrors_ = (double *)calloc(maxProfilePoints, sizeof(double));
return asynSuccess;
}
/** 为一个轮廓移动定义电机位置的函数
* 这个基类函数使用profileMotorOffset_, profileMotorDirection_和profileMotorResolution参数从用户单位转换位置为控制器单位,
* 参数[in] positions :这个轴以用户单位的轮廓位置的数组A
* 参数[in] numPoints :数组中位置数目
*/
asynStatus asynMotorAxis::defineProfile(double *positions, size_t numPoints)
{
size_t i;
double resolution;
double offset;
int direction;
double scale;
int status=0;
static const char *functionName = "defineProfile";
asynPrint(pasynUser_, ASYN_TRACE_FLOW,
"%s:%s: axis=%d, numPoints=%d, positions[0]=%f\n",
driverName, functionName, axisNo_, (int)numPoints, positions[0]);
if (numPoints > pC_->maxProfilePoints_) return asynError;
status |= pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_, &resolution);
status |= pC_->getDoubleParam(axisNo_, pC_->motorRecOffset_, &offset);
status |= pC_->getIntegerParam(axisNo_, pC_->motorRecDirection_, &direction);
asynPrint(pasynUser_, ASYN_TRACE_FLOW,
"%s:%s: axis=%d, status=%d, offset=%f direction=%d, resolution=%f\n",
driverName, functionName, axisNo_, status, offset, direction, resolution);
if (status) return asynError;
if (resolution == 0.0) return asynError;
// 转换成控制器单位
scale = 1.0/resolution;
if (direction != 0) scale = -scale;
for (i=0; i<numPoints; i++) {
profilePositions_[i] = (positions[i] - offset)*scale;
}
asynPrint(pasynUser_, ASYN_TRACE_FLOW,
"%s:%s: axis=%d, scale=%f, offset=%f positions[0]=%f, profilePositions_[0]=%f\n",
driverName, functionName, axisNo_, scale, offset, positions[0], profilePositions_[0]);
return asynSuccess;
}
/** 构建一个多轴联动移动的函数*/
asynStatus asynMotorAxis::buildProfile()
{
// static const char *functionName = "buildProfile";
return asynSuccess;
}
/** 执行一个多轴联动移动的函数. */
asynStatus asynMotorAxis::executeProfile()
{
// static const char *functionName = "executeProfile";
return asynSuccess;
}
/** 终止一个轮廓的函数 */
asynStatus asynMotorAxis::abortProfile()
{
// static const char *functionName = "abortProfile";
return asynSuccess;
}
/** 从多轴联动移动回读实际电机位置的函数。这个基类函数转从控制器单位换这些回读和以下误差为用户单位,并且对这些数组进行回调。
注意:这个函数原地修改这些回读,因此它必须每个回读操作仅调用一次。
*/
asynStatus asynMotorAxis::readbackProfile()
{
int i;
double resolution;
double offset;
int direction;
int numReadbacks;
int status=0;
//static const char *functionName = "readbackProfile";
status |= pC_->getDoubleParam(axisNo_, pC_->motorRecResolution_, &resolution);
status |= pC_->getDoubleParam(axisNo_, pC_->motorRecOffset_, &offset);
status |= pC_->getIntegerParam(axisNo_, pC_->motorRecDirection_, &direction);
status |= pC_->getIntegerParam(0, pC_->profileNumReadbacks_, &numReadbacks);
if (status) return asynError;
// 转换成用户单位
if (direction != 0) resolution = -resolution;
for (i=0; i<numReadbacks; i++) {
profileReadbacks_[i] = profileReadbacks_[i] * resolution + offset;
profileFollowingErrors_[i] = profileFollowingErrors_[i] * resolution;
}
status = pC_->doCallbacksFloat64Array(profileReadbacks_, numReadbacks, pC_->profileReadbacks_, axisNo_);
status |= pC_->doCallbacksFloat64Array(profileFollowingErrors_, numReadbacks, pC_->profileFollowingErrors_, axisNo_);
return asynSuccess;
}
/****************************************************************************/
/*
以下函数是由自动化驱动电源控制在asynMotorControler中基类poller中使用。
*/
/**
* 读取指示上次poll是否正在运行的标记
*/
int asynMotorAxis::getWasMovingFlag(void)
{
return wasMovingFlag_;
}
/**
* 如果之前查询表明移动状态,设置为1
*/
void asynMotorAxis::setWasMovingFlag(int wasMovingFlag)
{
wasMovingFlag_ = wasMovingFlag;
}
/**
* 表明驱动是否应该自定被禁用的标记
*/
int asynMotorAxis::getDisableFlag(void)
{
return disableFlag_;
}
/**
* 如果驱动应该自动被禁用,设置此为1
*/
void asynMotorAxis::setDisableFlag(int disableFlag)
{
disableFlag_ = disableFlag;
}
/**
* 读取上次移动结束的时间(以秒为单位)
*/
double asynMotorAxis::getLastEndOfMoveTime(void)
{
return lastEndOfMoveTime_;
}
/**
* Set this to the current time at the end of a move.
* 将这个设置到移动结束时的当前时间
*/
void asynMotorAxis::setLastEndOfMoveTime(double time)
{
lastEndOfMoveTime_ = time;
}