|
|
|
@@ -179,22 +179,38 @@ asynStatus turboPmacAxis::init() {
|
|
|
|
"Q%2.2d10 Q%2.2d03 Q%2.2d04 Q%2.2d06 P%2.2d22 Q%2.2d00 I%2.2d65",
|
|
|
|
"Q%2.2d10 Q%2.2d03 Q%2.2d04 Q%2.2d06 P%2.2d22 Q%2.2d00 I%2.2d65",
|
|
|
|
axisNo_, axisNo_, axisNo_, axisNo_, axisNo_, axisNo_, axisNo_);
|
|
|
|
axisNo_, axisNo_, axisNo_, axisNo_, axisNo_, axisNo_, axisNo_);
|
|
|
|
status = pC_->writeRead(axisNo_, command, response, 7);
|
|
|
|
status = pC_->writeRead(axisNo_, command, response, 7);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
msgPrintControlKey keyDisconnected = msgPrintControlKey(
|
|
|
|
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, 1);
|
|
|
|
|
|
|
|
|
|
|
|
if (status != asynSuccess) {
|
|
|
|
if (status != asynSuccess) {
|
|
|
|
|
|
|
|
if (pC_->getMsgPrintControl().shouldBePrinted(keyDisconnected, true,
|
|
|
|
|
|
|
|
pC_->pasynUser())) {
|
|
|
|
asynPrint(
|
|
|
|
asynPrint(
|
|
|
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nCould not communicate "
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nCould not "
|
|
|
|
"with controller during IOC initialization. Check if you used "
|
|
|
|
"communicate with controller during IOC initialization (error "
|
|
|
|
"\"pmacAsynIPPortConfigure\" instead of the standard "
|
|
|
|
"%s). Did you use \"drvAsynIPPortConfigure\" instead of "
|
|
|
|
"\"drvAsynIPPortConfigure\" function in the .cmd file in order to "
|
|
|
|
"\"pmacAsynIPPortConfigure\" in the .cmd file to create the "
|
|
|
|
"create the port driver.\n",
|
|
|
|
"port driver?\n",
|
|
|
|
pC_->portName, axisNo(), __PRETTY_FUNCTION__, __LINE__);
|
|
|
|
pC_->portName, axisNo(), __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
pTurboPmacA_->needInit = true;
|
|
|
|
pC_->stringifyAsynStatus(status));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pTurboPmacA_->needInit = true;
|
|
|
|
|
|
|
|
return status;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// No error has been detected -> Reset the error count
|
|
|
|
|
|
|
|
if (status == asynSuccess) {
|
|
|
|
|
|
|
|
pC_->getMsgPrintControl().resetCount(keyDisconnected, pC_->pasynUser());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
nvals = sscanf(response, "%lf %lf %lf %lf %d %lf %lf", &motorPos,
|
|
|
|
nvals = sscanf(response, "%lf %lf %lf %lf %d %lf %lf", &motorPos,
|
|
|
|
&motorVmax, &motorVelocity, &motorAccel, &acoDelay,
|
|
|
|
&motorVmax, &motorVelocity, &motorAccel, &acoDelay,
|
|
|
|
&counts_to_eu, &deadband_counts);
|
|
|
|
&counts_to_eu, &deadband_counts);
|
|
|
|
|
|
|
|
|
|
|
|
// The acoDelay is given in milliseconds -> Convert to seconds, rounded up
|
|
|
|
// The acoDelay is given in milliseconds -> Convert to seconds, rounded
|
|
|
|
|
|
|
|
// up
|
|
|
|
setOffsetMovTimeout(std::ceil(acoDelay / 1000.0));
|
|
|
|
setOffsetMovTimeout(std::ceil(acoDelay / 1000.0));
|
|
|
|
|
|
|
|
|
|
|
|
// The PMAC electronic specifies the acceleration in m/s^2. Since we
|
|
|
|
// The PMAC electronic specifies the acceleration in m/s^2. Since we
|
|
|
|
@@ -227,18 +243,18 @@ asynStatus turboPmacAxis::init() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
Check if the new target position is within the range current position +/-
|
|
|
|
Check if the new target position is within the range current position
|
|
|
|
deadband. If that is the case, no movement command should be sent. This
|
|
|
|
+/- deadband. If that is the case, no movement command should be sent.
|
|
|
|
functionality is implemented within the motor record itself, we just need to
|
|
|
|
This functionality is implemented within the motor record itself, we
|
|
|
|
populate the field SPDP (see record PushSPDB2Field in db/turboPmac.db)
|
|
|
|
just need to populate the field SPDP (see record PushSPDB2Field in
|
|
|
|
|
|
|
|
db/turboPmac.db)
|
|
|
|
|
|
|
|
|
|
|
|
First, the deadband is read out (Ixx65, see Turbo PMAC User Manual, p.
|
|
|
|
First, the deadband is read out (Ixx65, see Turbo PMAC User Manual, p.
|
|
|
|
160). This value then needs to be converted into counts (by dividing it by
|
|
|
|
160). This value then needs to be converted into counts (by dividing it
|
|
|
|
16). After words, the deadband in counts then need to be converted into
|
|
|
|
by 16). After words, the deadband in counts then need to be converted
|
|
|
|
engineering units. For this, the scaling factor Qxx10 needs to be applied
|
|
|
|
into engineering units. For this, the scaling factor Qxx10 needs to be
|
|
|
|
(see SINQ_2G_MCU_SW_manual_rev7, p. 17):
|
|
|
|
applied (see SINQ_2G_MCU_SW_manual_rev7, p. 17): [Qxx10] = engineering
|
|
|
|
[Qxx10] = engineering units (eu) / counts
|
|
|
|
units (eu) / counts deadband_eu = Qxx10 * Ixx65 / 16
|
|
|
|
deadband_eu = Qxx10 * Ixx65 / 16
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The values of Qxx10 and Ixx65 are read out during initialization and are
|
|
|
|
The values of Qxx10 and Ixx65 are read out during initialization and are
|
|
|
|
assumed to not change during operation.
|
|
|
|
assumed to not change during operation.
|
|
|
|
@@ -249,11 +265,12 @@ asynStatus turboPmacAxis::init() {
|
|
|
|
// Update the parameter library immediately
|
|
|
|
// Update the parameter library immediately
|
|
|
|
status = callParamCallbacks();
|
|
|
|
status = callParamCallbacks();
|
|
|
|
if (status != asynSuccess) {
|
|
|
|
if (status != asynSuccess) {
|
|
|
|
// If we can't communicate with the parameter library, it doesn't make
|
|
|
|
// If we can't communicate with the parameter library, it doesn't
|
|
|
|
// sense to try and upstream this to the user -> Just log the error
|
|
|
|
// make sense to try and upstream this to the user -> Just log the
|
|
|
|
asynPrint(
|
|
|
|
// error
|
|
|
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\ncallParamCallbacks "
|
|
|
|
"Controller \"%s\", axis %d => %s, line "
|
|
|
|
|
|
|
|
"%d\ncallParamCallbacks "
|
|
|
|
"failed with %s.\n",
|
|
|
|
"failed with %s.\n",
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
pC_->stringifyAsynStatus(status));
|
|
|
|
pC_->stringifyAsynStatus(status));
|
|
|
|
@@ -341,13 +358,13 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (error != 0) {
|
|
|
|
if (error != 0) {
|
|
|
|
// If an error occurred while waiting for the handshake, abort the
|
|
|
|
// If an error occurred while waiting for the handshake, abort
|
|
|
|
// waiting procedure and continue with the poll in order to handle
|
|
|
|
// the waiting procedure and continue with the poll in order to
|
|
|
|
// the error.
|
|
|
|
// handle the error.
|
|
|
|
pTurboPmacA_->waitForHandshake = false;
|
|
|
|
pTurboPmacA_->waitForHandshake = false;
|
|
|
|
} else if (handshakePerformed == 1) {
|
|
|
|
} else if (handshakePerformed == 1) {
|
|
|
|
// Handshake has been performed successfully -> Continue with the
|
|
|
|
// Handshake has been performed successfully -> Continue with
|
|
|
|
// poll
|
|
|
|
// the poll
|
|
|
|
pTurboPmacA_->waitForHandshake = false;
|
|
|
|
pTurboPmacA_->waitForHandshake = false;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
// Still waiting for the handshake - try again in the next busy
|
|
|
|
// Still waiting for the handshake - try again in the next busy
|
|
|
|
@@ -387,15 +404,16 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
The axis limits are set as: ({[]})
|
|
|
|
The axis limits are set as: ({[]})
|
|
|
|
where [] are the positive and negative limits set in EPICS/NICOS, {} are the
|
|
|
|
where [] are the positive and negative limits set in EPICS/NICOS, {} are
|
|
|
|
software limits set on the MCU and () are the hardware limit switches. In
|
|
|
|
the software limits set on the MCU and () are the hardware limit
|
|
|
|
other words, the EPICS/NICOS limits should be stricter than the software
|
|
|
|
switches. In other words, the EPICS/NICOS limits should be stricter than
|
|
|
|
limits on the MCU which in turn should be stricter than the hardware limit
|
|
|
|
the software limits on the MCU which in turn should be stricter than the
|
|
|
|
switches. For example, if the hardware limit switches are at [-10, 10], the
|
|
|
|
hardware limit switches. For example, if the hardware limit switches are
|
|
|
|
software limits could be at [-9, 9] and the EPICS / NICOS limits could be at
|
|
|
|
at [-10, 10], the software limits could be at [-9, 9] and the EPICS /
|
|
|
|
|
|
|
|
NICOS limits could be at
|
|
|
|
[-8, 8]. Therefore, we cannot use the software limits read from the MCU
|
|
|
|
[-8, 8]. Therefore, we cannot use the software limits read from the MCU
|
|
|
|
directly, but need to shrink them a bit. In this case, we're shrinking them
|
|
|
|
directly, but need to shrink them a bit. In this case, we're shrinking
|
|
|
|
by limitsOffset on both sides.
|
|
|
|
them by limitsOffset on both sides.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
getAxisParamChecked(this, motorLimitsOffset, &limitsOffset);
|
|
|
|
getAxisParamChecked(this, motorLimitsOffset, &limitsOffset);
|
|
|
|
highLimit = highLimit - limitsOffset;
|
|
|
|
highLimit = highLimit - limitsOffset;
|
|
|
|
@@ -411,8 +429,8 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
|
|
|
(axStatus != -3 && axStatus != -5));
|
|
|
|
(axStatus != -3 && axStatus != -5));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Create the unique callsite identifier manually so it can be used later in
|
|
|
|
// Create the unique callsite identifier manually so it can be used
|
|
|
|
// the shouldBePrinted calls.
|
|
|
|
// later in the shouldBePrinted calls.
|
|
|
|
msgPrintControlKey keyStatus = msgPrintControlKey(
|
|
|
|
msgPrintControlKey keyStatus = msgPrintControlKey(
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
|
|
|
bool resetCountStatus = true;
|
|
|
|
bool resetCountStatus = true;
|
|
|
|
@@ -433,9 +451,9 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
|
|
|
|
|
|
|
|
|
|
|
if (pC_->getMsgPrintControl().shouldBePrinted(keyStatus, true,
|
|
|
|
if (pC_->getMsgPrintControl().shouldBePrinted(keyStatus, true,
|
|
|
|
pC_->pasynUser())) {
|
|
|
|
pC_->pasynUser())) {
|
|
|
|
asynPrint(
|
|
|
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
"Controller \"%s\", axis %d => %s, line "
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nEmergency stop "
|
|
|
|
"%d\nEmergency stop "
|
|
|
|
"activated.%s\n",
|
|
|
|
"activated.%s\n",
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
pC_->getMsgPrintControl().getSuffix());
|
|
|
|
pC_->getMsgPrintControl().getSuffix());
|
|
|
|
@@ -508,8 +526,8 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
|
|
|
*moving = false;
|
|
|
|
*moving = false;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case 15:
|
|
|
|
case 15:
|
|
|
|
// Move is interrupted, but the motor is not ready to receive another
|
|
|
|
// Move is interrupted, but the motor is not ready to receive
|
|
|
|
// move command. Therefore it is treated as still moving.
|
|
|
|
// another move command. Therefore it is treated as still moving.
|
|
|
|
*moving = true;
|
|
|
|
*moving = true;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case 16:
|
|
|
|
case 16:
|
|
|
|
@@ -604,11 +622,11 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
|
|
|
|
if (pC_->getMsgPrintControl().shouldBePrinted(
|
|
|
|
if (pC_->getMsgPrintControl().shouldBePrinted(
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
|
|
|
pC_->pasynUser())) {
|
|
|
|
pC_->pasynUser())) {
|
|
|
|
asynPrint(
|
|
|
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nAxis is "
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nAxis is "
|
|
|
|
"still moving, but received another move command. EPICS "
|
|
|
|
"still moving, but received another move command. EPICS "
|
|
|
|
"should prevent this, check if *moving is set correctly.%s\n",
|
|
|
|
"should prevent this, check if *moving is set "
|
|
|
|
|
|
|
|
"correctly.%s\n",
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
pC_->getMsgPrintControl().getSuffix());
|
|
|
|
pC_->getMsgPrintControl().getSuffix());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -620,14 +638,17 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
|
|
|
|
if (pC_->getMsgPrintControl().shouldBePrinted(
|
|
|
|
if (pC_->getMsgPrintControl().shouldBePrinted(
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
|
|
|
pC_->pasynUser())) {
|
|
|
|
pC_->pasynUser())) {
|
|
|
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
asynPrint(
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nAir cushion "
|
|
|
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
"feedback stopped during movement (P%2.2d01 = %d).%s\n",
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nLost feedback "
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
"from auxiliary device during movement (P%2.2d01 = "
|
|
|
|
axisNo_, error, pC_->getMsgPrintControl().getSuffix());
|
|
|
|
"%d).%s\n",
|
|
|
|
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axisNo_,
|
|
|
|
|
|
|
|
error, pC_->getMsgPrintControl().getSuffix());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
snprintf(userMessage, sizeUserMessage,
|
|
|
|
snprintf(userMessage, sizeUserMessage,
|
|
|
|
"Air cushion feedback stopped during movement (P%2.2d01 = "
|
|
|
|
"Lost feedback from auxiliary device during movement "
|
|
|
|
|
|
|
|
"(P%2.2d01 = "
|
|
|
|
"%d). Please call the support.",
|
|
|
|
"%d). Please call the support.",
|
|
|
|
axisNo_, error);
|
|
|
|
axisNo_, error);
|
|
|
|
setAxisParamChecked(this, motorMessageText, userMessage);
|
|
|
|
setAxisParamChecked(this, motorMessageText, userMessage);
|
|
|
|
@@ -636,26 +657,27 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
|
|
|
|
if (pC_->getMsgPrintControl().shouldBePrinted(
|
|
|
|
if (pC_->getMsgPrintControl().shouldBePrinted(
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
|
|
|
pC_->pasynUser())) {
|
|
|
|
pC_->pasynUser())) {
|
|
|
|
asynPrint(
|
|
|
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nNo feedback "
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nNo air cushion "
|
|
|
|
"from "
|
|
|
|
"feedback before movement start (P%2.2d01 = %d).%s\n",
|
|
|
|
"auxiliary device before movement (P%2.2d01 = %d).%s\n",
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axisNo_,
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
error, pC_->getMsgPrintControl().getSuffix());
|
|
|
|
axisNo_, error, pC_->getMsgPrintControl().getSuffix());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
snprintf(userMessage, sizeUserMessage,
|
|
|
|
snprintf(
|
|
|
|
"No air cushion feedback before movement start (P%2.2d01 = "
|
|
|
|
userMessage, sizeUserMessage,
|
|
|
|
|
|
|
|
"No feedback from auxiliary device before movement (P%2.2d01 = "
|
|
|
|
"%d). Please call the support.",
|
|
|
|
"%d). Please call the support.",
|
|
|
|
axisNo_, error);
|
|
|
|
axisNo_, error);
|
|
|
|
setAxisParamChecked(this, motorMessageText, userMessage);
|
|
|
|
setAxisParamChecked(this, motorMessageText, userMessage);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
case 10:
|
|
|
|
case 10:
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
Software limits of the controller have been hit. Since the EPICS limits
|
|
|
|
Software limits of the controller have been hit. Since the EPICS
|
|
|
|
are derived from the software limits and are a little bit smaller, this
|
|
|
|
limits are derived from the software limits and are a little bit
|
|
|
|
error case can only happen if either the axis has an incremental encoder
|
|
|
|
smaller, this error case can only happen if either the axis has an
|
|
|
|
which is not properly homed or if a bug occured.
|
|
|
|
incremental encoder which is not properly homed or if a bug occured.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
if (pC_->getMsgPrintControl().shouldBePrinted(
|
|
|
|
if (pC_->getMsgPrintControl().shouldBePrinted(
|
|
|
|
@@ -682,9 +704,9 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
|
|
|
|
if (pC_->getMsgPrintControl().shouldBePrinted(
|
|
|
|
if (pC_->getMsgPrintControl().shouldBePrinted(
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
|
|
|
|
pC_->pasynUser())) {
|
|
|
|
pC_->pasynUser())) {
|
|
|
|
asynPrint(
|
|
|
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nMaximum "
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nMaximum allowed "
|
|
|
|
"allowed "
|
|
|
|
"following error exceeded.%s\n",
|
|
|
|
"following error exceeded.%s\n",
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
pC_->getMsgPrintControl().getSuffix());
|
|
|
|
pC_->getMsgPrintControl().getSuffix());
|
|
|
|
@@ -727,9 +749,9 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
pC_->getMsgPrintControl().getSuffix());
|
|
|
|
pC_->getMsgPrintControl().getSuffix());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
snprintf(
|
|
|
|
snprintf(userMessage, sizeUserMessage,
|
|
|
|
userMessage, sizeUserMessage,
|
|
|
|
"Driver hardware error (P%2.2d01 = 13). Please call the "
|
|
|
|
"Driver hardware error (P%2.2d01 = 13). Please call the support.",
|
|
|
|
"support.",
|
|
|
|
axisNo_);
|
|
|
|
axisNo_);
|
|
|
|
setAxisParamChecked(this, motorMessageText, userMessage);
|
|
|
|
setAxisParamChecked(this, motorMessageText, userMessage);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
@@ -795,9 +817,9 @@ asynStatus turboPmacAxis::doMove(double position, int relative,
|
|
|
|
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
|
|
|
|
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
|
|
|
|
|
|
|
|
|
|
|
|
if (enabled == 0) {
|
|
|
|
if (enabled == 0) {
|
|
|
|
asynPrint(
|
|
|
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nAxis is "
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nAxis is disabled.\n",
|
|
|
|
"disabled.\n",
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
|
|
|
return asynSuccess;
|
|
|
|
return asynSuccess;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -816,8 +838,8 @@ asynStatus turboPmacAxis::doMove(double position, int relative,
|
|
|
|
getAxisParamChecked(this, motorCanSetSpeed, &motorCanSetSpeed);
|
|
|
|
getAxisParamChecked(this, motorCanSetSpeed, &motorCanSetSpeed);
|
|
|
|
|
|
|
|
|
|
|
|
// Prepend the new motor speed, if the user is allowed to set the speed.
|
|
|
|
// Prepend the new motor speed, if the user is allowed to set the speed.
|
|
|
|
// Mind the " " (space) before the closing "", as the command created here
|
|
|
|
// Mind the " " (space) before the closing "", as the command created
|
|
|
|
// is prepended to the one down below.
|
|
|
|
// here is prepended to the one down below.
|
|
|
|
if (motorCanSetSpeed != 0) {
|
|
|
|
if (motorCanSetSpeed != 0) {
|
|
|
|
snprintf(command, sizeof(command), "Q%2.2d04=%lf ", axisNo_,
|
|
|
|
snprintf(command, sizeof(command), "Q%2.2d04=%lf ", axisNo_,
|
|
|
|
motorVelocity);
|
|
|
|
motorVelocity);
|
|
|
|
@@ -830,8 +852,8 @@ asynStatus turboPmacAxis::doMove(double position, int relative,
|
|
|
|
motorVelocity);
|
|
|
|
motorVelocity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Perform handshake, Set target position (and speed, if allowed) and start
|
|
|
|
// Perform handshake, Set target position (and speed, if allowed) and
|
|
|
|
// the move command
|
|
|
|
// start the move command
|
|
|
|
if (relative) {
|
|
|
|
if (relative) {
|
|
|
|
snprintf(&command[writeOffset], sizeof(command) - writeOffset,
|
|
|
|
snprintf(&command[writeOffset], sizeof(command) - writeOffset,
|
|
|
|
"P%2.2d23=0 Q%2.2d02=%lf M%2.2d=2", axisNo_, axisNo_,
|
|
|
|
"P%2.2d23=0 Q%2.2d02=%lf M%2.2d=2", axisNo_, axisNo_,
|
|
|
|
@@ -846,9 +868,9 @@ asynStatus turboPmacAxis::doMove(double position, int relative,
|
|
|
|
status = pC_->writeRead(axisNo_, command, response, 0);
|
|
|
|
status = pC_->writeRead(axisNo_, command, response, 0);
|
|
|
|
if (status != asynSuccess) {
|
|
|
|
if (status != asynSuccess) {
|
|
|
|
|
|
|
|
|
|
|
|
asynPrint(
|
|
|
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nStarting "
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nStarting movement to "
|
|
|
|
"movement to "
|
|
|
|
"target position %lf failed.\n",
|
|
|
|
"target position %lf failed.\n",
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
motorTargetPosition);
|
|
|
|
motorTargetPosition);
|
|
|
|
@@ -856,8 +878,8 @@ asynStatus turboPmacAxis::doMove(double position, int relative,
|
|
|
|
return status;
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// In the next poll, we will check if the handshake has been performed in a
|
|
|
|
// In the next poll, we will check if the handshake has been performed
|
|
|
|
// reasonable time
|
|
|
|
// in a reasonable time
|
|
|
|
pTurboPmacA_->waitForHandshake = true;
|
|
|
|
pTurboPmacA_->waitForHandshake = true;
|
|
|
|
pTurboPmacA_->timeAtHandshake = time(NULL);
|
|
|
|
pTurboPmacA_->timeAtHandshake = time(NULL);
|
|
|
|
|
|
|
|
|
|
|
|
@@ -886,25 +908,25 @@ asynStatus turboPmacAxis::stop(double acceleration) {
|
|
|
|
status = pC_->writeRead(axisNo_, command, response, 0);
|
|
|
|
status = pC_->writeRead(axisNo_, command, response, 0);
|
|
|
|
|
|
|
|
|
|
|
|
if (status != asynSuccess) {
|
|
|
|
if (status != asynSuccess) {
|
|
|
|
asynPrint(
|
|
|
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nStopping "
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nStopping the movement "
|
|
|
|
"the movement "
|
|
|
|
"failed.\n",
|
|
|
|
"failed.\n",
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
|
|
|
setAxisParamChecked(this, motorStatusProblem, true);
|
|
|
|
setAxisParamChecked(this, motorStatusProblem, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Reset the driver to idle state and move out of the handshake wait loop,
|
|
|
|
// Reset the driver to idle state and move out of the handshake wait
|
|
|
|
// if we're currently inside it.
|
|
|
|
// loop, if we're currently inside it.
|
|
|
|
pTurboPmacA_->waitForHandshake = false;
|
|
|
|
pTurboPmacA_->waitForHandshake = false;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
Stopping the motor results in a movement and further move commands have to
|
|
|
|
Stopping the motor results in a movement and further move commands have
|
|
|
|
wait until the stopping movement is done. Therefore, we need to wait until
|
|
|
|
to wait until the stopping movement is done. Therefore, we need to wait
|
|
|
|
the poller "sees" the changed state (otherwise, we risk issuing move
|
|
|
|
until the poller "sees" the changed state (otherwise, we risk issuing
|
|
|
|
commands while the motor is stopping). To ensure that at least one poll is
|
|
|
|
move commands while the motor is stopping). To ensure that at least one
|
|
|
|
done, this thread (which also runs move commands) is paused for twice the
|
|
|
|
poll is done, this thread (which also runs move commands) is paused for
|
|
|
|
idle poll period.
|
|
|
|
twice the idle poll period.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
unsigned int idlePollMicros =
|
|
|
|
unsigned int idlePollMicros =
|
|
|
|
(unsigned int)ceil(pC_->idlePollPeriod() * 1e6);
|
|
|
|
(unsigned int)ceil(pC_->idlePollPeriod() * 1e6);
|
|
|
|
@@ -935,8 +957,8 @@ asynStatus turboPmacAxis::doReset() {
|
|
|
|
setAxisParamChecked(this, motorStatusProblem, true);
|
|
|
|
setAxisParamChecked(this, motorStatusProblem, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Reset the driver to idle state and move out of the handshake wait loop,
|
|
|
|
// Reset the driver to idle state and move out of the handshake wait
|
|
|
|
// if we're currently inside it.
|
|
|
|
// loop, if we're currently inside it.
|
|
|
|
pTurboPmacA_->waitForHandshake = false;
|
|
|
|
pTurboPmacA_->waitForHandshake = false;
|
|
|
|
|
|
|
|
|
|
|
|
// Disable the axis
|
|
|
|
// Disable the axis
|
|
|
|
@@ -1034,13 +1056,14 @@ asynStatus turboPmacAxis::readEncoderType() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
This is not a command that can always be run when enabling a motor as it also
|
|
|
|
This is not a command that can always be run when enabling a motor as it
|
|
|
|
causes relative encoders to reread a position necessitating recalibration. We
|
|
|
|
also causes relative encoders to reread a position necessitating
|
|
|
|
only want it to run on absolute encoders. We also want it to be clear to
|
|
|
|
recalibration. We only want it to run on absolute encoders. We also want it
|
|
|
|
instrument scientists, that power has to be cut to the motor, in order to reread
|
|
|
|
to be clear to instrument scientists, that power has to be cut to the motor,
|
|
|
|
the encoder as not all motors have breaks and they may start to move when
|
|
|
|
in order to reread the encoder as not all motors have breaks and they may
|
|
|
|
disabled. For that reason, we don't automatically disable the motors to run the
|
|
|
|
start to move when disabled. For that reason, we don't automatically disable
|
|
|
|
command and instead require that the scientists first disable the motor.
|
|
|
|
the motors to run the command and instead require that the scientists first
|
|
|
|
|
|
|
|
disable the motor.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
asynStatus turboPmacAxis::rereadEncoder() {
|
|
|
|
asynStatus turboPmacAxis::rereadEncoder() {
|
|
|
|
char command[pC_->MAXBUF_] = {0};
|
|
|
|
char command[pC_->MAXBUF_] = {0};
|
|
|
|
@@ -1084,11 +1107,12 @@ asynStatus turboPmacAxis::rereadEncoder() {
|
|
|
|
return asynError;
|
|
|
|
return asynError;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
snprintf(command, sizeof(command), "M%2.2d=15", axisNo_);
|
|
|
|
snprintf(command, sizeof(command), "M%2.2d=15", axisNo_);
|
|
|
|
asynPrint(
|
|
|
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
|
|
|
|
pC_->pasynUser(), ASYN_TRACE_FLOW,
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nRereading "
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nRereading absolute "
|
|
|
|
"absolute "
|
|
|
|
"encoder via command %s.\n",
|
|
|
|
"encoder via command %s.\n",
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, command);
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
|
|
|
|
command);
|
|
|
|
pC_->writeRead(axisNo_, command, response, 0);
|
|
|
|
pC_->writeRead(axisNo_, command, response, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@@ -1097,8 +1121,8 @@ asynStatus turboPmacAxis::rereadEncoder() {
|
|
|
|
// it is actually finished, so we instead wait for 0.5 seconds.
|
|
|
|
// it is actually finished, so we instead wait for 0.5 seconds.
|
|
|
|
usleep(500000);
|
|
|
|
usleep(500000);
|
|
|
|
|
|
|
|
|
|
|
|
// Turn off parameter as finished rereading, this will only be immediately
|
|
|
|
// Turn off parameter as finished rereading, this will only be
|
|
|
|
// noticed in the read back variable though
|
|
|
|
// immediately noticed in the read back variable though
|
|
|
|
setAxisParamChecked(this, rereadEncoderPosition, false);
|
|
|
|
setAxisParamChecked(this, rereadEncoderPosition, false);
|
|
|
|
return asynSuccess;
|
|
|
|
return asynSuccess;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@@ -1118,27 +1142,28 @@ asynStatus turboPmacAxis::enable(bool on) {
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
Continue regardless of the status returned by the poll; we just want to
|
|
|
|
Continue regardless of the status returned by the poll; we just want to
|
|
|
|
find out whether the motor is currently moving or not. If the poll
|
|
|
|
find out whether the motor is currently moving or not. If the poll
|
|
|
|
function fails before it can determine that, it is assumed that the motor
|
|
|
|
function fails before it can determine that, it is assumed that the
|
|
|
|
is not moving.
|
|
|
|
motor is not moving.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
bool moving = false;
|
|
|
|
bool moving = false;
|
|
|
|
doPoll(&moving);
|
|
|
|
doPoll(&moving);
|
|
|
|
|
|
|
|
|
|
|
|
// If the axis is currently moving, it cannot be disabled. Ignore the
|
|
|
|
// If the axis is currently moving, it cannot be disabled. Ignore the
|
|
|
|
// command and inform the user. We check the last known status of the axis
|
|
|
|
// command and inform the user. We check the last known status of the
|
|
|
|
// instead of "moving", since status -6 is also moving, but the motor can
|
|
|
|
// axis instead of "moving", since status -6 is also moving, but the
|
|
|
|
// actually be disabled in this state!
|
|
|
|
// motor can actually be disabled in this state!
|
|
|
|
int axStatus = pTurboPmacA_->axisStatus;
|
|
|
|
int axStatus = pTurboPmacA_->axisStatus;
|
|
|
|
if (axStatus == 1 || axStatus == 2 || axStatus == 3 || axStatus == 4 ||
|
|
|
|
if (axStatus == 1 || axStatus == 2 || axStatus == 3 || axStatus == 4 ||
|
|
|
|
axStatus == 5 || axStatus == 6 || axStatus == 7 || axStatus == 8 ||
|
|
|
|
axStatus == 5 || axStatus == 6 || axStatus == 7 || axStatus == 8 ||
|
|
|
|
axStatus == 9 || axStatus == 10 || axStatus == 11 || axStatus == 12 ||
|
|
|
|
axStatus == 9 || axStatus == 10 || axStatus == 11 || axStatus == 12 ||
|
|
|
|
axStatus == 13 || axStatus == 15 || axStatus == 16) {
|
|
|
|
axStatus == 13 || axStatus == 15 || axStatus == 16) {
|
|
|
|
|
|
|
|
|
|
|
|
asynPrint(
|
|
|
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
pC_->pasynUser(), ASYN_TRACE_ERROR,
|
|
|
|
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nAxis is not "
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nAxis is not "
|
|
|
|
"idle (status %d) and can therefore not be enabled / disabled.\n",
|
|
|
|
"idle (status %d) and can therefore not be enabled / "
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axStatus);
|
|
|
|
"disabled.\n",
|
|
|
|
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
|
|
|
|
axStatus);
|
|
|
|
|
|
|
|
|
|
|
|
setAxisParamChecked(this, motorMessageText,
|
|
|
|
setAxisParamChecked(this, motorMessageText,
|
|
|
|
"Axis cannot be disabled while it is moving.");
|
|
|
|
"Axis cannot be disabled while it is moving.");
|
|
|
|
@@ -1149,9 +1174,9 @@ asynStatus turboPmacAxis::enable(bool on) {
|
|
|
|
// Axis is already enabled / disabled and a new enable / disable command
|
|
|
|
// Axis is already enabled / disabled and a new enable / disable command
|
|
|
|
// was sent => Do nothing
|
|
|
|
// was sent => Do nothing
|
|
|
|
if ((axStatus != -3) == on) {
|
|
|
|
if ((axStatus != -3) == on) {
|
|
|
|
asynPrint(
|
|
|
|
asynPrint(pC_->pasynUser(), ASYN_TRACE_WARNING,
|
|
|
|
pC_->pasynUser(), ASYN_TRACE_WARNING,
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nAxis is "
|
|
|
|
"Controller \"%s\", axis %d => %s, line %d\nAxis is already %s.\n",
|
|
|
|
"already %s.\n",
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
|
|
|
on ? "enabled" : "disabled");
|
|
|
|
on ? "enabled" : "disabled");
|
|
|
|
return asynSuccess;
|
|
|
|
return asynSuccess;
|
|
|
|
@@ -1302,8 +1327,8 @@ asynStatus turboPmacCreateAxis(const char *portName, int axis) {
|
|
|
|
https://github.com/epics-modules/motor/blob/master/motorApp/MotorSrc/asynMotorController.cpp
|
|
|
|
https://github.com/epics-modules/motor/blob/master/motorApp/MotorSrc/asynMotorController.cpp
|
|
|
|
https://github.com/epics-modules/asyn/blob/master/asyn/asynPortDriver/asynPortDriver.cpp
|
|
|
|
https://github.com/epics-modules/asyn/blob/master/asyn/asynPortDriver/asynPortDriver.cpp
|
|
|
|
|
|
|
|
|
|
|
|
The created object is registered in EPICS in its constructor and can safely
|
|
|
|
The created object is registered in EPICS in its constructor and can
|
|
|
|
be "leaked" here.
|
|
|
|
safely be "leaked" here.
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
|
|
|
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
|
|
|
#pragma GCC diagnostic ignored "-Wunused-variable"
|
|
|
|
#pragma GCC diagnostic ignored "-Wunused-variable"
|
|
|
|
@@ -1326,14 +1351,15 @@ static const iocshArg *const CreateAxisArgs[] = {&CreateAxisArg0,
|
|
|
|
static const iocshFuncDef configTurboPmacCreateAxis = {
|
|
|
|
static const iocshFuncDef configTurboPmacCreateAxis = {
|
|
|
|
"turboPmacAxis", 2, CreateAxisArgs,
|
|
|
|
"turboPmacAxis", 2, CreateAxisArgs,
|
|
|
|
"Create an instance of a turboPmac axis. The first argument is the "
|
|
|
|
"Create an instance of a turboPmac axis. The first argument is the "
|
|
|
|
"controller this axis should be attached to, the second argument is the "
|
|
|
|
"controller this axis should be attached to, the second argument is "
|
|
|
|
|
|
|
|
"the "
|
|
|
|
"axis number."};
|
|
|
|
"axis number."};
|
|
|
|
static void configTurboPmacCreateAxisCallFunc(const iocshArgBuf *args) {
|
|
|
|
static void configTurboPmacCreateAxisCallFunc(const iocshArgBuf *args) {
|
|
|
|
turboPmacCreateAxis(args[0].sval, args[1].ival);
|
|
|
|
turboPmacCreateAxis(args[0].sval, args[1].ival);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// This function is made known to EPICS in turboPmac.dbd and is called by EPICS
|
|
|
|
// This function is made known to EPICS in turboPmac.dbd and is called by
|
|
|
|
// in order to register both functions in the IOC shell
|
|
|
|
// EPICS in order to register both functions in the IOC shell
|
|
|
|
static void turboPmacAxisRegister(void) {
|
|
|
|
static void turboPmacAxisRegister(void) {
|
|
|
|
iocshRegister(&configTurboPmacCreateAxis,
|
|
|
|
iocshRegister(&configTurboPmacCreateAxis,
|
|
|
|
configTurboPmacCreateAxisCallFunc);
|
|
|
|
configTurboPmacCreateAxisCallFunc);
|
|
|
|
|