Commit first working version of the new PMAC-V3 driver. This version has
been tested on the SINQTEST instrument. However, some important improvements (such as central polling in the controller) are still missing. They will be implemented in the next commit.
This commit is contained in:
@ -13,7 +13,7 @@ REQUIRED+=scaler
|
|||||||
REQUIRED+=asynMotor
|
REQUIRED+=asynMotor
|
||||||
|
|
||||||
# Release version
|
# Release version
|
||||||
LIBVERSION=2024-dev
|
LIBVERSION=2024-newPmacV3
|
||||||
|
|
||||||
# DB files to include in the release
|
# DB files to include in the release
|
||||||
TEMPLATES += sinqEPICSApp/Db/dimetix.db
|
TEMPLATES += sinqEPICSApp/Db/dimetix.db
|
||||||
@ -38,6 +38,9 @@ SOURCES += sinqEPICSApp/src/pmacController.cpp
|
|||||||
SOURCES += sinqEPICSApp/src/MasterMACSDriver.cpp
|
SOURCES += sinqEPICSApp/src/MasterMACSDriver.cpp
|
||||||
SOURCES += sinqEPICSApp/src/C804Axis.cpp
|
SOURCES += sinqEPICSApp/src/C804Axis.cpp
|
||||||
SOURCES += sinqEPICSApp/src/C804Controller.cpp
|
SOURCES += sinqEPICSApp/src/C804Controller.cpp
|
||||||
|
SOURCES += sinqEPICSApp/src/newPmacV3Axis.cpp
|
||||||
|
SOURCES += sinqEPICSApp/src/newPmacV3Controller.cpp
|
||||||
|
SOURCES += sinqEPICSApp/src/pmacController.cpp
|
||||||
|
|
||||||
USR_CFLAGS += -Wall -Wextra # -Werror
|
USR_CFLAGS += -Wall -Wextra # -Werror
|
||||||
|
|
||||||
|
@ -46,9 +46,9 @@ asynStatus C804Axis::poll(bool *moving) {
|
|||||||
// Local variable declaration
|
// Local variable declaration
|
||||||
static const char *functionName = "C804Axis::poll";
|
static const char *functionName = "C804Axis::poll";
|
||||||
|
|
||||||
// The poll function is just a wrapper around poll_no_param_lib_update and
|
// The poll function is just a wrapper around pollNoUpdate and
|
||||||
// handles mainly the callParamCallbacks() function
|
// handles mainly the callParamCallbacks() function
|
||||||
asynStatus status_poll = C804Axis::poll_no_param_lib_update(moving);
|
asynStatus status_poll = C804Axis::pollNoUpdate(moving);
|
||||||
|
|
||||||
// According to the function documentation of asynMotorAxis::poll, this
|
// According to the function documentation of asynMotorAxis::poll, this
|
||||||
// function should be called at the end of a poll implementation.
|
// function should be called at the end of a poll implementation.
|
||||||
@ -64,7 +64,7 @@ asynStatus C804Axis::poll(bool *moving) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Perform the actual poll
|
// Perform the actual poll
|
||||||
asynStatus C804Axis::poll_no_param_lib_update(bool *moving) {
|
asynStatus C804Axis::pollNoUpdate(bool *moving) {
|
||||||
// Local variable declaration
|
// Local variable declaration
|
||||||
static const char *functionName = "C804Axis::poll";
|
static const char *functionName = "C804Axis::poll";
|
||||||
asynStatus status;
|
asynStatus status;
|
||||||
|
@ -1,40 +1,43 @@
|
|||||||
#ifndef C804Axis_H
|
#ifndef C804Axis_H
|
||||||
#define C804Axis_H
|
#define C804Axis_H
|
||||||
|
|
||||||
#include "SINQController.h"
|
|
||||||
#include "SINQAxis.h"
|
#include "SINQAxis.h"
|
||||||
|
#include "SINQController.h"
|
||||||
|
|
||||||
// Forward declaration of the controller class to resolve the cyclic dependency
|
// Forward declaration of the controller class to resolve the cyclic dependency
|
||||||
// between C804Controller.h and C804Axis.h. See https://en.cppreference.com/w/cpp/language/class.
|
// between C804Controller.h and C804Axis.h. See
|
||||||
|
// https://en.cppreference.com/w/cpp/language/class.
|
||||||
class C804Controller;
|
class C804Controller;
|
||||||
|
|
||||||
class C804Axis : public SINQAxis
|
class C804Axis : public SINQAxis {
|
||||||
{
|
public:
|
||||||
public:
|
/* These are the methods we override from the base class */
|
||||||
/* These are the methods we override from the base class */
|
C804Axis(C804Controller *pController, int axisNo);
|
||||||
C804Axis(C804Controller *pController, int axisNo);
|
virtual ~C804Axis();
|
||||||
virtual ~C804Axis();
|
asynStatus move(double position, int relative, double min_velocity,
|
||||||
asynStatus move(double position, int relative, double min_velocity, double max_velocity, double acceleration);
|
double max_velocity, double acceleration);
|
||||||
asynStatus moveVelocity(double min_velocity, double max_velocity, double acceleration);
|
asynStatus moveVelocity(double min_velocity, double max_velocity,
|
||||||
asynStatus stop(double acceleration);
|
double acceleration);
|
||||||
asynStatus home(double minVelocity, double maxVelocity, double acceleration, int forwards);
|
asynStatus stop(double acceleration);
|
||||||
asynStatus poll(bool *moving);
|
asynStatus home(double minVelocity, double maxVelocity, double acceleration,
|
||||||
asynStatus poll_no_param_lib_update(bool *moving);
|
int forwards);
|
||||||
asynStatus enable(int on);
|
asynStatus poll(bool *moving);
|
||||||
|
asynStatus pollNoUpdate(bool *moving);
|
||||||
|
asynStatus enable(int on);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
C804Controller *pC_;
|
C804Controller *pC_;
|
||||||
|
|
||||||
void checkBounds(C804Controller *pController, int axisNo);
|
void checkBounds(C804Controller *pController, int axisNo);
|
||||||
int last_position_steps_;
|
int last_position_steps_;
|
||||||
double motorRecResolution_;
|
double motorRecResolution_;
|
||||||
time_t estimatedArrivalTime_;
|
time_t estimatedArrivalTime_;
|
||||||
time_t last_poll_;
|
time_t last_poll_;
|
||||||
int errorReported_;
|
int errorReported_;
|
||||||
bool enabled_;
|
bool enabled_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class C804Controller;
|
friend class C804Controller;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -14,15 +14,13 @@ class newPmacV3Axis : public asynMotorAxis {
|
|||||||
virtual ~newPmacV3Axis();
|
virtual ~newPmacV3Axis();
|
||||||
asynStatus move(double position, int relative, double min_velocity,
|
asynStatus move(double position, int relative, double min_velocity,
|
||||||
double max_velocity, double acceleration);
|
double max_velocity, double acceleration);
|
||||||
// asynStatus moveVelocity(double min_velocity, double max_velocity,
|
|
||||||
// double acceleration);
|
|
||||||
asynStatus stop(double acceleration);
|
asynStatus stop(double acceleration);
|
||||||
// asynStatus home(double minVelocity, double maxVelocity, double
|
asynStatus home(double minVelocity, double maxVelocity, double acceleration,
|
||||||
// acceleration,
|
int forwards);
|
||||||
// int forwards);
|
|
||||||
asynStatus poll(bool *moving);
|
asynStatus poll(bool *moving);
|
||||||
asynStatus poll_no_param_lib_update(bool *moving);
|
asynStatus pollNoUpdate(bool *moving);
|
||||||
// asynStatus enable(int on);
|
asynStatus enable(int on);
|
||||||
|
asynStatus readEncoderType();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
newPmacV3Controller *pC_;
|
newPmacV3Controller *pC_;
|
||||||
@ -30,6 +28,9 @@ class newPmacV3Axis : public asynMotorAxis {
|
|||||||
void checkBounds(newPmacV3Controller *pController, int axisNo);
|
void checkBounds(newPmacV3Controller *pController, int axisNo);
|
||||||
asynStatus readConfig();
|
asynStatus readConfig();
|
||||||
bool initial_poll_;
|
bool initial_poll_;
|
||||||
|
bool waitForHandshake_;
|
||||||
|
time_t timeAtHandshake_;
|
||||||
|
time_t handshakeTimeout_;
|
||||||
time_t time_at_init_poll_;
|
time_t time_at_init_poll_;
|
||||||
time_t timeout_param_lib_init_;
|
time_t timeout_param_lib_init_;
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ newPmacV3Controller::newPmacV3Controller(const char *portName,
|
|||||||
// NICOS and in turn to the user
|
// NICOS and in turn to the user
|
||||||
status = createParam("MOTOR_MESSAGE_TEXT", asynParamOctet, &messageText_);
|
status = createParam("MOTOR_MESSAGE_TEXT", asynParamOctet, &messageText_);
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
paramLibAccessFailed(status, "messageText_");
|
paramLibAccessFailed(status, functionName, "messageText_");
|
||||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||||
"%s: FATAL ERROR: unable to create parameter (%s). "
|
"%s: FATAL ERROR: unable to create parameter (%s). "
|
||||||
"Terminating IOC.\n",
|
"Terminating IOC.\n",
|
||||||
@ -111,6 +111,15 @@ newPmacV3Controller::newPmacV3Controller(const char *portName,
|
|||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status = createParam("ENCODER_TYPE", asynParamOctet, &encoderType_);
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||||
|
"%s: FATAL ERROR: unable to create parameter (%s). "
|
||||||
|
"Terminating IOC.\n",
|
||||||
|
functionName, stringifyAsynStatus(status));
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
status = createParam("REREAD_ENCODER_POSITION", asynParamInt32,
|
status = createParam("REREAD_ENCODER_POSITION", asynParamInt32,
|
||||||
&rereadEncoderPosition_);
|
&rereadEncoderPosition_);
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
@ -140,6 +149,16 @@ newPmacV3Controller::newPmacV3Controller(const char *portName,
|
|||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
status =
|
||||||
|
createParam("MOTOR_POSITION_RBV", asynParamFloat64, &motorPositionRBV_);
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||||
|
"%s: FATAL ERROR: unable to create parameter (%s). "
|
||||||
|
"Terminating IOC.\n",
|
||||||
|
functionName, stringifyAsynStatus(status));
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Define the end-of-string of a message coming from the device to EPICS.
|
Define the end-of-string of a message coming from the device to EPICS.
|
||||||
It is not necessary to append a terminator to outgoing messages, since
|
It is not necessary to append a terminator to outgoing messages, since
|
||||||
@ -230,14 +249,14 @@ Sends the given command to the axis specified by axisNo and returns the response
|
|||||||
of the axis.
|
of the axis.
|
||||||
*/
|
*/
|
||||||
asynStatus newPmacV3Controller::writeRead(int axisNo, const char *command,
|
asynStatus newPmacV3Controller::writeRead(int axisNo, const char *command,
|
||||||
char *response) {
|
char *response,
|
||||||
|
bool expect_response) {
|
||||||
// Definition of local variables.
|
// Definition of local variables.
|
||||||
static const char *functionName = "newPmacV3Controller::writeRead";
|
static const char *functionName = "newPmacV3Controller::writeRead";
|
||||||
asynStatus status = asynSuccess;
|
asynStatus status = asynSuccess;
|
||||||
asynStatus pl_status = asynSuccess;
|
asynStatus pl_status = asynSuccess;
|
||||||
char full_command[MAXBUF_] = {0};
|
char full_command[MAXBUF_] = {0};
|
||||||
char user_message[MAXBUF_] = {0};
|
char user_message[MAXBUF_] = {0};
|
||||||
int motorStatusCommsError = 0;
|
|
||||||
int motorStatusProblem = 0;
|
int motorStatusProblem = 0;
|
||||||
|
|
||||||
// Send the message and block the thread until either a response has been
|
// Send the message and block the thread until either a response has been
|
||||||
@ -294,15 +313,48 @@ asynStatus newPmacV3Controller::writeRead(int axisNo, const char *command,
|
|||||||
"%s: Sending command: %s\n", functionName, full_command);
|
"%s: Sending command: %s\n", functionName, full_command);
|
||||||
|
|
||||||
// Perform the actual writeRead
|
// Perform the actual writeRead
|
||||||
// status =
|
|
||||||
// pasynOctetSyncIO->write(lowLevelPortUser_, full_command,
|
|
||||||
// commandLength + offset, TIMEOUT_,
|
|
||||||
// &nbytesOut);
|
|
||||||
|
|
||||||
status = pasynOctetSyncIO->writeRead(
|
status = pasynOctetSyncIO->writeRead(
|
||||||
lowLevelPortUser_, full_command, commandLength + offset, response,
|
lowLevelPortUser_, full_command, commandLength + offset, response,
|
||||||
MAXBUF_, TIMEOUT_, &nbytesOut, &nbytesIn, &eomReason);
|
MAXBUF_, TIMEOUT_, &nbytesOut, &nbytesIn, &eomReason);
|
||||||
|
|
||||||
|
/*
|
||||||
|
If we expect a response, check if we got one. If no response was received,
|
||||||
|
flush the PMAC and try again. If that fails as well, return an error
|
||||||
|
*/
|
||||||
|
if (expect_response && strlen(response) == 0) {
|
||||||
|
// Flush message as defined in Turbo PMAC User Manual, p. 430:
|
||||||
|
// \x40\xB3000
|
||||||
|
// VR_DOWNLOAD = \x40
|
||||||
|
// VR_PMAC_FLUSH = \xB3
|
||||||
|
char flush_msg[5] = {0};
|
||||||
|
flush_msg[0] = '\x40';
|
||||||
|
flush_msg[1] = '\xB3';
|
||||||
|
size_t nbytesOut = 0;
|
||||||
|
status = pasynOctetSyncIO->write(lowLevelPortUser_, flush_msg, 5,
|
||||||
|
TIMEOUT_, &nbytesOut);
|
||||||
|
|
||||||
|
// Wait after the flush so the MCU has time to prepare for the
|
||||||
|
// next command
|
||||||
|
usleep(100000);
|
||||||
|
|
||||||
|
if (status == asynSuccess) {
|
||||||
|
// If flushing the MCU succeded, try to send the command again
|
||||||
|
status = pasynOctetSyncIO->writeRead(
|
||||||
|
lowLevelPortUser_, full_command, commandLength + offset,
|
||||||
|
response, MAXBUF_, TIMEOUT_, &nbytesOut, &nbytesIn, &eomReason);
|
||||||
|
|
||||||
|
// If the command returned an empty string for the second time, give
|
||||||
|
// up and propagate the error.
|
||||||
|
if (strlen(response) == 0) {
|
||||||
|
status = asynError;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
asynPrint(pasynUserSelf, ASYN_TRACE_ERROR,
|
||||||
|
"%s: Unable to flush MCU (%s).\n", functionName,
|
||||||
|
stringifyAsynStatus(status));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Create custom error messages for different failure modes
|
// Create custom error messages for different failure modes
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case asynSuccess:
|
case asynSuccess:
|
||||||
@ -312,22 +364,19 @@ asynStatus newPmacV3Controller::writeRead(int axisNo, const char *command,
|
|||||||
"connection timeout for axis %d", axisNo);
|
"connection timeout for axis %d", axisNo);
|
||||||
break;
|
break;
|
||||||
case asynDisconnected:
|
case asynDisconnected:
|
||||||
snprintf(user_message, sizeof(user_message), "axis %d is not connected",
|
snprintf(user_message, sizeof(user_message), "axis is not connected");
|
||||||
axisNo);
|
|
||||||
break;
|
break;
|
||||||
case asynDisabled:
|
case asynDisabled:
|
||||||
snprintf(user_message, sizeof(user_message), "axis %d is disabled",
|
snprintf(user_message, sizeof(user_message), "axis is disabled");
|
||||||
axisNo);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
snprintf(user_message, sizeof(user_message),
|
snprintf(user_message, sizeof(user_message),
|
||||||
"Communication with axis %d failed (%s)", axisNo,
|
"Communication failed (%s)", stringifyAsynStatus(status));
|
||||||
stringifyAsynStatus(status));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
// Check if the axis aleady is in an error communication mode. If it is
|
// Check if the axis already is in an error communication mode. If it is
|
||||||
// not, upstream the error. This is done to avoid "flooding" the user
|
// not, upstream the error. This is done to avoid "flooding" the user
|
||||||
// with different error messages if more than one error ocurred before
|
// with different error messages if more than one error ocurred before
|
||||||
// an error-free communication
|
// an error-free communication
|
||||||
@ -335,13 +384,15 @@ asynStatus newPmacV3Controller::writeRead(int axisNo, const char *command,
|
|||||||
pl_status =
|
pl_status =
|
||||||
getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem);
|
getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem);
|
||||||
if (pl_status != asynSuccess) {
|
if (pl_status != asynSuccess) {
|
||||||
return paramLibAccessFailed(pl_status, "motorStatusProblem_");
|
return paramLibAccessFailed(pl_status, functionName,
|
||||||
|
"motorStatusProblem_");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (motorStatusProblem == 0) {
|
if (motorStatusProblem == 0) {
|
||||||
pl_status = axis->setStringParam(this->messageText_, user_message);
|
pl_status = axis->setStringParam(this->messageText_, user_message);
|
||||||
if (pl_status != asynSuccess) {
|
if (pl_status != asynSuccess) {
|
||||||
return paramLibAccessFailed(pl_status, "messageText_");
|
return paramLibAccessFailed(pl_status, functionName,
|
||||||
|
"messageText_");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -360,8 +411,10 @@ asynStatus newPmacV3Controller::writeRead(int axisNo, const char *command,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (pl_status != asynSuccess) {
|
if (pl_status != asynSuccess) {
|
||||||
return paramLibAccessFailed(pl_status, "motorStatusCommsError_");
|
return paramLibAccessFailed(pl_status, functionName,
|
||||||
|
"motorStatusCommsError_");
|
||||||
}
|
}
|
||||||
|
|
||||||
return asynSuccess;
|
return asynSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -370,10 +423,7 @@ asynStatus newPmacV3Controller::writeInt32(asynUser *pasynUser,
|
|||||||
|
|
||||||
int function = pasynUser->reason;
|
int function = pasynUser->reason;
|
||||||
asynStatus status = asynSuccess;
|
asynStatus status = asynSuccess;
|
||||||
char command[MAXBUF_] = {0};
|
|
||||||
char response[MAXBUF_] = {0};
|
|
||||||
static const char *functionName = "newPmacV3Controller::writeInt32";
|
static const char *functionName = "newPmacV3Controller::writeInt32";
|
||||||
int nvals = 0;
|
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
||||||
@ -385,92 +435,12 @@ asynStatus newPmacV3Controller::writeInt32(asynUser *pasynUser,
|
|||||||
|
|
||||||
// Handle custom PVs
|
// Handle custom PVs
|
||||||
if (function == enableMotor_) {
|
if (function == enableMotor_) {
|
||||||
|
return axis->enable(value);
|
||||||
int ax_status = 0;
|
|
||||||
asynStatus pl_status = asynSuccess;
|
|
||||||
int timeout_enable_disable = 2;
|
|
||||||
char message[MAXBUF_] = {0};
|
|
||||||
|
|
||||||
// =====================================================================
|
|
||||||
|
|
||||||
// Check if the axis is currently enabled
|
|
||||||
snprintf(command, sizeof(command), "P%2.2d00", axis->axisNo_);
|
|
||||||
status = writeRead(axis->axisNo_, command, response);
|
|
||||||
nvals = sscanf(response, "%d", &ax_status);
|
|
||||||
if (checkNumExpectedReads(1, nvals, functionName, axis->axisNo_) !=
|
|
||||||
asynSuccess) {
|
|
||||||
return asynError;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Axis is already enabled / disabled and a new enable / disable command
|
|
||||||
// was sent => Do nothing
|
|
||||||
if ((ax_status != -3) == value) {
|
|
||||||
return asynSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable / disable the axis
|
|
||||||
snprintf(command, sizeof(command), "M%2.2d14=%d", axis->axisNo_, value);
|
|
||||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
|
|
||||||
"%s: %s axis %d on controller %s\n", functionName,
|
|
||||||
value ? "Enable" : "Disable", axis->axisNo_, portName);
|
|
||||||
if (value == 0) {
|
|
||||||
pl_status = setStringParam(messageText_, "Disabling ...");
|
|
||||||
} else {
|
|
||||||
pl_status = setStringParam(messageText_, "Enabling ...");
|
|
||||||
}
|
|
||||||
if (pl_status != asynSuccess) {
|
|
||||||
return paramLibAccessFailed(pl_status, "messageText_");
|
|
||||||
}
|
|
||||||
status = writeRead(axis->axisNo_, command, response);
|
|
||||||
if (status != asynSuccess) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Query the axis status every few milliseconds until the axis has been
|
|
||||||
// enabled or until the timeout has been reached
|
|
||||||
snprintf(command, sizeof(command), "P%2.2d00", axis->axisNo_);
|
|
||||||
int startTime = time(NULL);
|
|
||||||
while (time(NULL) < startTime + timeout_enable_disable) {
|
|
||||||
|
|
||||||
// Read the axis status
|
|
||||||
usleep(100);
|
|
||||||
status = writeRead(axis->axisNo_, command, response);
|
|
||||||
if (status != asynSuccess) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
nvals = sscanf(response, "%d", &ax_status);
|
|
||||||
if (checkNumExpectedReads(1, nvals, functionName, axis->axisNo_) !=
|
|
||||||
asynSuccess) {
|
|
||||||
return asynError;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ax_status != -3) == value) {
|
|
||||||
bool moving = false;
|
|
||||||
// Perform a poll to update the parameter library
|
|
||||||
axis->poll(&moving);
|
|
||||||
return asynSuccess;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Failed to change axis status within timeout_enable_disable => Send a
|
|
||||||
// corresponding message
|
|
||||||
asynPrint(
|
|
||||||
this->pasynUserSelf, ASYN_TRACE_FLOW,
|
|
||||||
"%s: Failed to %s axis %d on controller %s within %d seconds\n",
|
|
||||||
functionName, value ? "enable" : "disable", axis->axisNo_, portName,
|
|
||||||
timeout_enable_disable);
|
|
||||||
|
|
||||||
// Output message to user
|
|
||||||
snprintf(command, sizeof(command), "Failed to %s within %d seconds",
|
|
||||||
value ? "enable" : "disable", timeout_enable_disable);
|
|
||||||
pl_status = setStringParam(messageText_, "Enabling ...");
|
|
||||||
if (pl_status != asynSuccess) {
|
|
||||||
return paramLibAccessFailed(pl_status, "messageText_");
|
|
||||||
}
|
|
||||||
return asynError;
|
|
||||||
|
|
||||||
} else if (function == rereadEncoderPosition_) {
|
} else if (function == rereadEncoderPosition_) {
|
||||||
|
|
||||||
|
char encoderType[MAXBUF_] = {0};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This is not a command that can always be run when enabling a
|
This is not a command that can always be run when enabling a
|
||||||
motor as it also causes relative encoders to reread a position
|
motor as it also causes relative encoders to reread a position
|
||||||
@ -488,68 +458,56 @@ asynStatus newPmacV3Controller::writeInt32(asynUser *pasynUser,
|
|||||||
|
|
||||||
// Poll the current status of the axis
|
// Poll the current status of the axis
|
||||||
bool moving = false;
|
bool moving = false;
|
||||||
axis->poll(&moving);
|
status = axis->poll(&moving);
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if this is an absolute encoder
|
// Check if this is an absolute encoder
|
||||||
snprintf(command, sizeof(command), "I%2.2X04", axis->axisNo_);
|
status = axis->readEncoderType();
|
||||||
status = writeRead(axis->axisNo_, command, response);
|
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
int reponse_length = strlen(response);
|
status = getStringParam(axis->axisNo_, encoderType_, encoderType);
|
||||||
if (reponse_length < 3) {
|
|
||||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
|
||||||
"%s: Unexpected reponse '%s' from axis %d on "
|
|
||||||
"controller %s while rereading encoder. Aborting...\n",
|
|
||||||
functionName, response, axis->axisNo_, portName);
|
|
||||||
return asynError;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We are only interested in the last two digits and the last value in
|
|
||||||
// the string before the terminator is \r
|
|
||||||
int encoder_id = 0;
|
|
||||||
nvals = sscanf(response + (reponse_length - 3), "%2X", &encoder_id);
|
|
||||||
if (checkNumExpectedReads(1, nvals, functionName, axis->axisNo_) !=
|
|
||||||
asynSuccess) {
|
|
||||||
return asynError;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(command, sizeof(command), "P46");
|
|
||||||
status = writeRead(axis->axisNo_, command, response);
|
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
return status;
|
return paramLibAccessFailed(status, functionName, "encoderType_");
|
||||||
}
|
}
|
||||||
|
|
||||||
int number_of_axes = strtol(response, NULL, 10);
|
// Abort if the axis is incremental
|
||||||
if (encoder_id <= number_of_axes) {
|
if (strcmp(encoderType, IncrementalEncoder) == 0) {
|
||||||
asynPrint(
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_WARNING,
|
||||||
this->pasynUserSelf, ASYN_TRACE_ERROR,
|
"%s: Trying to reread absolute encoder of axis %d on "
|
||||||
"%s: Trying to reread absolute encoder of axis %d on "
|
"controller %s, but it is a relative encoder.\n",
|
||||||
"controller %s, but it is a relative encoder. Aborting...\n",
|
functionName, axis->axisNo_, portName);
|
||||||
functionName, axis->axisNo_, portName);
|
status = setStringParam(messageText_,
|
||||||
|
"Cannot reread an incremental encoder.");
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
return paramLibAccessFailed(status, functionName,
|
||||||
|
"messageText_");
|
||||||
|
}
|
||||||
return asynError;
|
return asynError;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the axis is disabled. If not, inform the user that this is
|
// Check if the axis is disabled. If not, inform the user that this
|
||||||
// necessary
|
// is necessary
|
||||||
int enabled = 0;
|
int enabled = 0;
|
||||||
status = getIntegerParam(axis->axisNo_, motorEnabled_, &enabled);
|
status = getIntegerParam(axis->axisNo_, motorEnabled_, &enabled);
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
return paramLibAccessFailed(status, "motorEnabled_");
|
return paramLibAccessFailed(status, functionName, "motorEnabled_");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enabled == 1) {
|
if (enabled == 1) {
|
||||||
asynPrint(
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_WARNING,
|
||||||
this->pasynUserSelf, ASYN_TRACE_WARNING,
|
"%s: Axis %d on controller %s must be disabled before "
|
||||||
"%s: Trying to reread absolute encoder of axis %d on "
|
"rereading the encoder.\n",
|
||||||
"controller %s, but it is a relative encoder. Aborting...\n",
|
functionName, axis->axisNo_, portName);
|
||||||
functionName, axis->axisNo_, portName);
|
|
||||||
status = setStringParam(
|
status = setStringParam(
|
||||||
messageText_,
|
messageText_,
|
||||||
"Axis must be disabled before rereading the encoder.");
|
"Axis must be disabled before rereading the encoder.");
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
return paramLibAccessFailed(status, "messageText_");
|
return paramLibAccessFailed(status, functionName,
|
||||||
|
"messageText_");
|
||||||
}
|
}
|
||||||
return asynError;
|
return asynError;
|
||||||
}
|
}
|
||||||
@ -564,7 +522,8 @@ asynStatus newPmacV3Controller::writeInt32(asynUser *pasynUser,
|
|||||||
// though
|
// though
|
||||||
status = axis->setIntegerParam(rereadEncoderPosition_, 0);
|
status = axis->setIntegerParam(rereadEncoderPosition_, 0);
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
return paramLibAccessFailed(status, "rereadEncoderPosition_");
|
return paramLibAccessFailed(status, functionName,
|
||||||
|
"rereadEncoderPosition_");
|
||||||
}
|
}
|
||||||
return asynSuccess;
|
return asynSuccess;
|
||||||
|
|
||||||
@ -581,11 +540,9 @@ and "rereading the encoder" must be covered.
|
|||||||
asynStatus newPmacV3Controller::readInt32(asynUser *pasynUser,
|
asynStatus newPmacV3Controller::readInt32(asynUser *pasynUser,
|
||||||
epicsInt32 *value) {
|
epicsInt32 *value) {
|
||||||
|
|
||||||
int function = pasynUser->reason, axStat;
|
int function = pasynUser->reason;
|
||||||
asynStatus status = asynError;
|
asynStatus status = asynError;
|
||||||
static const char *functionName = "newPmacV3Controller::readInt32";
|
static const char *functionName = "newPmacV3Controller::readInt32";
|
||||||
char command[MAXBUF_];
|
|
||||||
char response[MAXBUF_];
|
|
||||||
|
|
||||||
// =====================================================================
|
// =====================================================================
|
||||||
|
|
||||||
@ -595,42 +552,22 @@ asynStatus newPmacV3Controller::readInt32(asynUser *pasynUser,
|
|||||||
return asynError;
|
return asynError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (function == motorEnabled_) {
|
if (function == rereadEncoderPositionRBV_) {
|
||||||
// Readback value for motorEnabled
|
|
||||||
|
|
||||||
snprintf(command, sizeof(command), "P%2.2d00", axis->axisNo_);
|
|
||||||
status = this->writeRead(axis->axisNo_, command, response);
|
|
||||||
if (status != asynSuccess) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
int nvals = sscanf(response, "%d", value);
|
|
||||||
if (checkNumExpectedReads(1, nvals, functionName, axis->axisNo_) !=
|
|
||||||
asynSuccess) {
|
|
||||||
return asynError;
|
|
||||||
}
|
|
||||||
status = setIntegerParam(motorEnabled_, (*value != -3));
|
|
||||||
if (status != asynSuccess) {
|
|
||||||
return paramLibAccessFailed(status, "motorEnabled_");
|
|
||||||
}
|
|
||||||
return callParamCallbacks(); // Update the PVs from the parameter
|
|
||||||
// library
|
|
||||||
|
|
||||||
} else if (function == rereadEncoderPositionRBV_) {
|
|
||||||
// Readback value for rereadEncoderPosition
|
// Readback value for rereadEncoderPosition
|
||||||
|
|
||||||
status = getIntegerParam(axis->axisNo_, rereadEncoderPosition_, value);
|
status = getIntegerParam(axis->axisNo_, rereadEncoderPosition_, value);
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "HERE\n");
|
return paramLibAccessFailed(status, functionName,
|
||||||
return paramLibAccessFailed(status, "rereadEncoderPosition_");
|
"rereadEncoderPosition_");
|
||||||
}
|
}
|
||||||
status =
|
status =
|
||||||
setIntegerParam(axis->axisNo_, rereadEncoderPositionRBV_, *value);
|
setIntegerParam(axis->axisNo_, rereadEncoderPositionRBV_, *value);
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
return paramLibAccessFailed(status, "rereadEncoderPositionRBV_");
|
return paramLibAccessFailed(status, functionName,
|
||||||
|
"rereadEncoderPositionRBV_");
|
||||||
}
|
}
|
||||||
return callParamCallbacks(); // Update the PVs from the parameter
|
|
||||||
// library
|
// Update the PVs from the parameter library
|
||||||
|
return callParamCallbacks();
|
||||||
} else {
|
} else {
|
||||||
return asynMotorController::readInt32(pasynUser, value);
|
return asynMotorController::readInt32(pasynUser, value);
|
||||||
}
|
}
|
||||||
@ -676,6 +613,7 @@ const char *newPmacV3Controller::stringifyAsynStatus(asynStatus status) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
asynStatus newPmacV3Controller::paramLibAccessFailed(asynStatus status,
|
asynStatus newPmacV3Controller::paramLibAccessFailed(asynStatus status,
|
||||||
|
const char *functionName,
|
||||||
const char *parameter) {
|
const char *parameter) {
|
||||||
char message[MAXBUF_] = {0};
|
char message[MAXBUF_] = {0};
|
||||||
snprintf(message, sizeof(message),
|
snprintf(message, sizeof(message),
|
||||||
@ -684,23 +622,27 @@ asynStatus newPmacV3Controller::paramLibAccessFailed(asynStatus status,
|
|||||||
parameter, stringifyAsynStatus(status));
|
parameter, stringifyAsynStatus(status));
|
||||||
|
|
||||||
// Log the error message and try to propagate it
|
// Log the error message and try to propagate it
|
||||||
asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR, message);
|
asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR, "%s: %s", functionName,
|
||||||
|
message);
|
||||||
setStringParam(messageText_, message);
|
setStringParam(messageText_, message);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
asynStatus newPmacV3Controller::checkNumExpectedReads(int expected, int read,
|
asynStatus newPmacV3Controller::checkNumExpectedReads(int expected, int read,
|
||||||
const char *functionName,
|
const char *functionName,
|
||||||
|
const char *command,
|
||||||
|
const char *response,
|
||||||
int axisNo_) {
|
int axisNo_) {
|
||||||
if (expected == read) {
|
if (expected == read) {
|
||||||
return asynSuccess;
|
return asynSuccess;
|
||||||
} else {
|
} else {
|
||||||
char message[MAXBUF_] = {0};
|
char message[MAXBUF_] = {0};
|
||||||
snprintf(message, sizeof(message),
|
snprintf(message, sizeof(message),
|
||||||
"Could not interpret controller response in function %s for "
|
"Could not interpret response %s for command %s (axis %d). "
|
||||||
"axis %d. This is a bug, please inform the software support.",
|
"This is a bug.",
|
||||||
functionName, axisNo_);
|
response, command, axisNo_);
|
||||||
asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR, message);
|
asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR, "%s: %s", functionName,
|
||||||
|
message);
|
||||||
setStringParam(messageText_, message);
|
setStringParam(messageText_, message);
|
||||||
setIntegerParam(motorStatusCommsError_, 1);
|
setIntegerParam(motorStatusCommsError_, 1);
|
||||||
return asynError;
|
return asynError;
|
||||||
@ -868,4 +810,4 @@ epicsExportRegistrar(newPmacV3ControllerRegister);
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
@ -12,6 +12,9 @@
|
|||||||
#include "asynMotorController.h"
|
#include "asynMotorController.h"
|
||||||
#include "newPmacV3Axis.h"
|
#include "newPmacV3Axis.h"
|
||||||
|
|
||||||
|
#define IncrementalEncoder "Incremental encoder"
|
||||||
|
#define AbsoluteEncoder "Absolute encoder"
|
||||||
|
|
||||||
class newPmacV3Controller : public asynMotorController {
|
class newPmacV3Controller : public asynMotorController {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@ -31,17 +34,25 @@ class newPmacV3Controller : public asynMotorController {
|
|||||||
// overloaded because we want to read the axis state
|
// overloaded because we want to read the axis state
|
||||||
asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
|
asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
|
||||||
|
|
||||||
|
// // Overloaded to read configuration details from the motor records
|
||||||
|
// asynStatus drvUserCreate(asynUser *pasynUser, const char *drvInfo,
|
||||||
|
// const char **pptypeName, size_t *psize);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
asynUser *lowLevelPortUser_;
|
asynUser *lowLevelPortUser_;
|
||||||
|
|
||||||
asynStatus writeRead(int axisNo, const char *command, char *response);
|
asynStatus writeRead(int axisNo, const char *command, char *response,
|
||||||
|
bool expect_response);
|
||||||
|
|
||||||
// Create a descriptive string out of an asynStatus which can be used for
|
// Create a descriptive string out of an asynStatus which can be used for
|
||||||
// logging or communicating with the user
|
// logging or communicating with the user
|
||||||
const char *stringifyAsynStatus(asynStatus status);
|
const char *stringifyAsynStatus(asynStatus status);
|
||||||
asynStatus paramLibAccessFailed(asynStatus status, const char *parameter);
|
asynStatus paramLibAccessFailed(asynStatus status, const char *functionName,
|
||||||
|
const char *parameter);
|
||||||
asynStatus checkNumExpectedReads(int expected, int read,
|
asynStatus checkNumExpectedReads(int expected, int read,
|
||||||
const char *function, int axisNo_);
|
const char *functionName,
|
||||||
|
const char *command, const char *response,
|
||||||
|
int axisNo_);
|
||||||
newPmacV3Axis *castToAxis(asynMotorAxis *asynAxis);
|
newPmacV3Axis *castToAxis(asynMotorAxis *asynAxis);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -58,14 +69,24 @@ class newPmacV3Controller : public asynMotorController {
|
|||||||
*/
|
*/
|
||||||
static const double TIMEOUT_;
|
static const double TIMEOUT_;
|
||||||
|
|
||||||
|
// Indices of additional PVs
|
||||||
int messageText_;
|
int messageText_;
|
||||||
int enableMotor_;
|
int enableMotor_;
|
||||||
int motorEnabled_;
|
int motorEnabled_;
|
||||||
int rereadEncoderPosition_;
|
int rereadEncoderPosition_;
|
||||||
int rereadEncoderPositionRBV_;
|
int rereadEncoderPositionRBV_;
|
||||||
int readConfig_;
|
int readConfig_;
|
||||||
|
int encoderType_;
|
||||||
|
int motorPositionRBV_;
|
||||||
|
|
||||||
|
/*
|
||||||
|
If the time between two sent messages is too short, the MCU communication
|
||||||
|
module might "lose" an answer. To prevent this, a small delay is introduced
|
||||||
|
in EPICS after each message exchange. Unit is microseconds.
|
||||||
|
*/
|
||||||
|
int afterMessageSleep_;
|
||||||
|
|
||||||
friend class newPmacV3Axis;
|
friend class newPmacV3Axis;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* pmacV3Controller_H */
|
#endif /* pmacV3Controller_H */
|
||||||
|
Reference in New Issue
Block a user