|
|
|
@ -32,6 +32,10 @@
|
|
|
|
|
*
|
|
|
|
|
* Mark Koennecke, June 2023
|
|
|
|
|
*
|
|
|
|
|
* Added driver for GirderAxis
|
|
|
|
|
*
|
|
|
|
|
* Stefan Mathis, July 2024
|
|
|
|
|
*
|
|
|
|
|
********************************************/
|
|
|
|
|
|
|
|
|
|
#include <epicsExit.h>
|
|
|
|
@ -1479,4 +1483,275 @@ asynStatus AmorDetectorAxis::poll(bool *moving)
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
GirderAxis::GirderAxis(pmacController *pController, int axisNo)
|
|
|
|
|
: pmacV3Axis(pController, axisNo){
|
|
|
|
|
|
|
|
|
|
// GirderAxis expects a pmacV3Controller. Therefore it is checked whether the pointer pController can be cast
|
|
|
|
|
// to this object type. If this is not possible, the user made an error in the configuration files. This is documented
|
|
|
|
|
// in a corresponding error; after that, an exception is thrown to avoid returning an "illegal" instance of GirderAxis.
|
|
|
|
|
pmacV3Controller* pV3Controller = dynamic_cast<pmacV3Controller*>(pController);
|
|
|
|
|
if (pV3Controller == nullptr) {
|
|
|
|
|
errlogPrintf("A GirderAxis instance needs a pmacV3Controller. Please check the configuration files.");
|
|
|
|
|
throw std::invalid_argument("A GirderAxis instance needs a pmacV3Controller. Please check the configuration files.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initial values for member variables
|
|
|
|
|
next_poll = -1;
|
|
|
|
|
previous_position_ = 0.0;
|
|
|
|
|
previous_direction_ = 0;
|
|
|
|
|
status6Time = 0;
|
|
|
|
|
statusPos = 0.0;
|
|
|
|
|
homing = 0;
|
|
|
|
|
axisErrorCount = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*-------------------------------------------------------------------------------*/
|
|
|
|
|
asynStatus GirderAxis::move(double position, int relative, double min_velocity, double max_velocity, double acceleration) {
|
|
|
|
|
|
|
|
|
|
// If the axis is not enabled, do nothing
|
|
|
|
|
if(!IsEnable) {
|
|
|
|
|
updateMsgTxtFromDriver("Error: axis disabled");
|
|
|
|
|
return asynError;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// =======================================
|
|
|
|
|
// Local variable declaration
|
|
|
|
|
|
|
|
|
|
asynStatus status = asynError;
|
|
|
|
|
char command[pC_->PMAC_MAXBUF_] = {0};
|
|
|
|
|
char response[pC_->PMAC_MAXBUF_] = {0};
|
|
|
|
|
double realPosition = 0;
|
|
|
|
|
|
|
|
|
|
// =======================================
|
|
|
|
|
|
|
|
|
|
updateMsgTxtFromDriver("");
|
|
|
|
|
static const char *functionName = "GirderAxis::move";
|
|
|
|
|
pC_->debugFlow(functionName);
|
|
|
|
|
|
|
|
|
|
if (relative) {
|
|
|
|
|
realPosition = previous_position_ + position / MULT;
|
|
|
|
|
} else {
|
|
|
|
|
realPosition = position / MULT;
|
|
|
|
|
}
|
|
|
|
|
startTime = time(NULL);
|
|
|
|
|
status6Time = 0;
|
|
|
|
|
starting = 1;
|
|
|
|
|
|
|
|
|
|
// Set target position
|
|
|
|
|
snprintf(command, sizeof(command), "Q251=%f", realPosition);
|
|
|
|
|
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
|
|
|
|
if (status == asynError) {
|
|
|
|
|
updateMsgTxtFromDriver("Error: Could not set target position");
|
|
|
|
|
return asynError;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Start motion
|
|
|
|
|
snprintf(command, sizeof(command), "P150=1");
|
|
|
|
|
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
|
|
|
|
if (status == asynError) {
|
|
|
|
|
updateMsgTxtFromDriver("Error: Could not start motion");
|
|
|
|
|
return asynError;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
next_poll = -1;
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
asynStatus GirderAxis::stop(double acceleration) {
|
|
|
|
|
|
|
|
|
|
// =======================================
|
|
|
|
|
// Local variable declaration
|
|
|
|
|
|
|
|
|
|
asynStatus status = asynSuccess;
|
|
|
|
|
static const char *functionName = "GirderAxis::stop";
|
|
|
|
|
bool moving = false;
|
|
|
|
|
char command[pC_->PMAC_MAXBUF_] = {0};
|
|
|
|
|
char response[pC_->PMAC_MAXBUF_] = {0};
|
|
|
|
|
|
|
|
|
|
// =======================================
|
|
|
|
|
|
|
|
|
|
pC_->debugFlow(functionName);
|
|
|
|
|
this->poll(&moving);
|
|
|
|
|
|
|
|
|
|
if(moving) {
|
|
|
|
|
// only send a stop when actually moving
|
|
|
|
|
snprintf(command, sizeof(command), "P150=8");
|
|
|
|
|
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
|
|
|
|
}
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
asynStatus GirderAxis::poll(bool *moving) {
|
|
|
|
|
|
|
|
|
|
// =======================================
|
|
|
|
|
// Local variable declaration
|
|
|
|
|
|
|
|
|
|
asynStatus status = asynSuccess;
|
|
|
|
|
static const char *functionName = "GirderAxis::poll";
|
|
|
|
|
char command[pC_->PMAC_MAXBUF_] = {0};
|
|
|
|
|
char response[pC_->PMAC_MAXBUF_] = {0};
|
|
|
|
|
char message[132], *axMessage;
|
|
|
|
|
double position = 0;
|
|
|
|
|
int moving_to_position = 0, nvals = 0, axisProblemFlag = 0, axStat = 0, axError = 0, isEnabled = 1;
|
|
|
|
|
asynStatus st;
|
|
|
|
|
|
|
|
|
|
// =======================================
|
|
|
|
|
|
|
|
|
|
// Check for girder axis specific errors
|
|
|
|
|
snprintf(command, sizeof(command), "P159");
|
|
|
|
|
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
|
|
|
|
sscanf(response, "P159=%d", &axError);
|
|
|
|
|
switch(axError) {
|
|
|
|
|
case 1:
|
|
|
|
|
snprintf(message, sizeof(message), "PMAC: %s on %d",
|
|
|
|
|
"Axis not switched on", axisNo_);
|
|
|
|
|
updateMsgTxtFromDriver(message);
|
|
|
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s\n", message);
|
|
|
|
|
axisProblemFlag = 1;
|
|
|
|
|
isEnabled = 0;
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
snprintf(message, sizeof(message), "PMAC: %s on %d",
|
|
|
|
|
"Axis not ready for new command", axisNo_);
|
|
|
|
|
updateMsgTxtFromDriver(message);
|
|
|
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s\n", message);
|
|
|
|
|
axisProblemFlag = 1;
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
snprintf(message, sizeof(message), "PMAC: %s on %d",
|
|
|
|
|
"Axis 1 ERROR during motion", axisNo_);
|
|
|
|
|
updateMsgTxtFromDriver(message);
|
|
|
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s\n", message);
|
|
|
|
|
axisProblemFlag = 1;
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
snprintf(message, sizeof(message), "PMAC: %s on %d",
|
|
|
|
|
"Axis 2 ERROR during motion", axisNo_);
|
|
|
|
|
updateMsgTxtFromDriver(message);
|
|
|
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, "%s\n", message);
|
|
|
|
|
axisProblemFlag = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
st = setIntegerParam(pC_->motorStatusProblem_, axisProblemFlag);
|
|
|
|
|
status = status > st ? status : st;
|
|
|
|
|
|
|
|
|
|
// Downcast the pointer pmacController to pmacV3Controller in a typesafe manner
|
|
|
|
|
pmacV3Controller* p3C_ = dynamic_cast<pmacV3Controller*>(pC_);
|
|
|
|
|
if (p3C_ == nullptr) {
|
|
|
|
|
errlogPrintf("A GirderAxis instance needs a pmacV3Controller. Please check the configuration files.");
|
|
|
|
|
throw std::invalid_argument("A GirderAxis instance needs a pmacV3Controller. Please check the configuration files.");
|
|
|
|
|
}
|
|
|
|
|
setIntegerParam(p3C_->axisEnabled_, isEnabled);
|
|
|
|
|
|
|
|
|
|
st = setIntegerParam(p3C_->enableAxis_, isEnabled);
|
|
|
|
|
status = status > st ? status : st;
|
|
|
|
|
|
|
|
|
|
int direction = 0;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
In GirderAxis::move, the user input is scaled by /MULT. Hence, the output of
|
|
|
|
|
Qxx10 needs to be scaled by *MULT
|
|
|
|
|
*/
|
|
|
|
|
st = setDoubleParam(pC_->motorPosition_, position * MULT);
|
|
|
|
|
status = status > st ? status : st;
|
|
|
|
|
st = setDoubleParam(pC_->motorEncoderPosition_, position * MULT);
|
|
|
|
|
status = status > st ? status : st;
|
|
|
|
|
|
|
|
|
|
// Calculate the current (or last) movement direction
|
|
|
|
|
if ((position - previous_position_) > 0) {
|
|
|
|
|
direction = 1;
|
|
|
|
|
} else if (position - previous_position_ == 0.0) {
|
|
|
|
|
direction = previous_direction_;
|
|
|
|
|
} else {
|
|
|
|
|
direction = 0;
|
|
|
|
|
}
|
|
|
|
|
st = setIntegerParam(pC_->motorStatusDirection_, direction);
|
|
|
|
|
status = status > st ? status : st;
|
|
|
|
|
|
|
|
|
|
// Store the position which was read out from the hardware together with the calculated direction for the next poll
|
|
|
|
|
previous_position_ = position;
|
|
|
|
|
previous_direction_ = direction;
|
|
|
|
|
|
|
|
|
|
// Is the axis currently moving?
|
|
|
|
|
snprintf(command, sizeof(command), "P154");
|
|
|
|
|
status = pC_->lowLevelWriteRead(axisNo_, command, response);
|
|
|
|
|
sscanf(response, "P154=%d", &moving_to_position);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
This code tests whether the axis is too long in status 5 or 6.
|
|
|
|
|
If the axis is in status 5 or 6, a time counter (status6Time) is started and updated during subsequent polls.
|
|
|
|
|
If status6Time exceeds the estimated arrival time of 120 seconds, a corresponding error is returned.
|
|
|
|
|
*/
|
|
|
|
|
int EstimatedTimeOfArrival = 120; // seconds
|
|
|
|
|
if (axStat == 5 || axStat == 6) {
|
|
|
|
|
if (status6Time == 0) {
|
|
|
|
|
status6Time = time(nullptr);
|
|
|
|
|
statusPos = position;
|
|
|
|
|
} else {
|
|
|
|
|
if (time(nullptr) > status6Time + EstimatedTimeOfArrival) {
|
|
|
|
|
/* trigger error only when not moving */
|
|
|
|
|
if (abs(position - statusPos) < .1) {
|
|
|
|
|
moving_to_position = 0;
|
|
|
|
|
errlogPrintf(
|
|
|
|
|
"Axis %d stayed in status 5 or 6 for more than %d seconds BROKEN\n",
|
|
|
|
|
axisNo_, EstimatedTimeOfArrival);
|
|
|
|
|
updateMsgTxtFromDriver("Axis stayed in status 5 or 6 for more than estimated time: BROKEN");
|
|
|
|
|
status6Time = 0;
|
|
|
|
|
return status;
|
|
|
|
|
} else {
|
|
|
|
|
status6Time = time(nullptr);
|
|
|
|
|
statusPos = position;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
If the axis is moving, set the corresponding flags in the pmacController (pC_) and end the poll.
|
|
|
|
|
*/
|
|
|
|
|
if (!moving_to_position) {
|
|
|
|
|
*moving = true;
|
|
|
|
|
st = setIntegerParam(pC_->motorStatusMoving_, true);
|
|
|
|
|
status = status > st ? status : st;
|
|
|
|
|
st = setIntegerParam(pC_->motorStatusDone_, false);
|
|
|
|
|
return status > st ? status : st;
|
|
|
|
|
} else {
|
|
|
|
|
*moving = false;
|
|
|
|
|
st = setIntegerParam(pC_->motorStatusMoving_, false);
|
|
|
|
|
status = status > st ? status : st;
|
|
|
|
|
st = setIntegerParam(pC_->motorStatusDone_, true);
|
|
|
|
|
status = status > st ? status : st;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set any axis specific general problem bits. */
|
|
|
|
|
if (axError != 0) {
|
|
|
|
|
axisProblemFlag = 1;
|
|
|
|
|
if (axisErrorCount < 10) {
|
|
|
|
|
axMessage = translateAxisError(axError);
|
|
|
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
|
|
|
"drvGirderAxisGetStatus: Axis %d is in deep trouble: axis error "
|
|
|
|
|
"code %d, translated: %s:, status code = %d\n",
|
|
|
|
|
axisNo_, axError, axMessage, axStat);
|
|
|
|
|
snprintf(message, sizeof(message), "GirderAxis error: %s", axMessage);
|
|
|
|
|
updateMsgTxtFromDriver(message);
|
|
|
|
|
if (axMessage != NULL) {
|
|
|
|
|
free(axMessage);
|
|
|
|
|
}
|
|
|
|
|
axisErrorCount++;
|
|
|
|
|
} else if (axisErrorCount == 10) {
|
|
|
|
|
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
|
|
|
"Suppressing further axis error messages\n");
|
|
|
|
|
axisErrorCount++;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
axisProblemFlag = 0;
|
|
|
|
|
axisErrorCount = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
st = setIntegerParam(pC_->motorStatusProblem_, axisProblemFlag);
|
|
|
|
|
status = status > st ? status : st;
|
|
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
}
|