Compare commits

...

3 Commits
0.4.0 ... 0.4.3

Author SHA1 Message Date
08d76d7953 bump sinqMotor version to 0.6.3 2025-01-08 16:04:58 +01:00
1f02001502 Various small improvements to documentation, error messages etc.
Also moved the initialization of some parameters to sinqMotor
2024-12-23 09:32:00 +01:00
2f2678546d Bumped the required version of sinqMotor 2024-12-11 09:50:22 +01:00
5 changed files with 66 additions and 88 deletions

View File

@ -11,7 +11,7 @@ REQUIRED+=asynMotor
REQUIRED+=sinqMotor REQUIRED+=sinqMotor
# Specify the version of sinqMotor we want to build against # Specify the version of sinqMotor we want to build against
sinqMotor_VERSION=0.5.0 sinqMotor_VERSION=0.6.3
# These headers allow to depend on this library for derived drivers. # These headers allow to depend on this library for derived drivers.
HEADERS += src/pmacv3Axis.h HEADERS += src/pmacv3Axis.h

View File

@ -8,6 +8,10 @@ This is a driver for the pmacV3 motion controller with the SINQ communication pr
This driver is a standard sinqMotor-derived driver and does not need any specific configuration. For the general configuration, please see https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md. This driver is a standard sinqMotor-derived driver and does not need any specific configuration. For the general configuration, please see https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.
The folder "utils" contains utility scripts for working with pmac motor controllers:
- writeRead.py: Allows sending commands to and receiving commands from a pmac controller over an ethernet connection.
- analyzeTcpDump: This script takes a tcpdump as an input and
## Developer guide ## Developer guide
### Usage in IOC shell ### Usage in IOC shell

View File

@ -50,17 +50,8 @@ pmacv3Axis::pmacv3Axis(pmacv3Controller *pC, int axisNo)
exit(-1); exit(-1);
} }
status = pC_->setIntegerParam(axisNo_, pC_->motorEnableRBV_, 0); // pmacv3 motors can always be disabled
if (status != asynSuccess) { status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable_, 1);
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFATAL ERROR (setting a parameter value failed "
"with %s)\n. Terminating IOC",
__PRETTY_FUNCTION__, __LINE__, pC_->stringifyAsynStatus(status));
exit(-1);
}
status = pC_->setDoubleParam(axisNo_, pC_->motorPosition_, 0.0);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint( asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR, pC_->pasynUserSelf, ASYN_TRACE_ERROR,
@ -84,21 +75,6 @@ pmacv3Axis::~pmacv3Axis(void) {
Read the configuration at the first poll Read the configuration at the first poll
*/ */
asynStatus pmacv3Axis::atFirstPoll() { asynStatus pmacv3Axis::atFirstPoll() {
asynStatus status = asynSuccess;
// pmacv3 motors can always be disabled
status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable_, 1);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorCanDisable_",
__PRETTY_FUNCTION__, __LINE__);
}
return readConfig();
}
/*
Read the configuration from the motor control unit and the parameter library.
*/
asynStatus pmacv3Axis::readConfig() {
// Local variable declaration // Local variable declaration
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
@ -122,7 +98,10 @@ asynStatus pmacv3Axis::readConfig() {
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
// Software limits and current position /*
Read out the axis status, the current position, current and maximum speed,
acceleration and the air cushion delay.
*/
snprintf(command, sizeof(command), snprintf(command, sizeof(command),
"P%2.2d00 Q%2.2d10 Q%2.2d03 Q%2.2d04 Q%2.2d06 P%2.2d22", axisNo_, "P%2.2d00 Q%2.2d10 Q%2.2d03 Q%2.2d04 Q%2.2d06 P%2.2d22", axisNo_,
axisNo_, axisNo_, axisNo_, axisNo_, axisNo_); axisNo_, axisNo_, axisNo_, axisNo_, axisNo_);
@ -261,7 +240,7 @@ asynStatus pmacv3Axis::doPoll(bool *moving) {
} }
// Transform from EPICS to motor coordinates (see comment in // Transform from EPICS to motor coordinates (see comment in
// pmacv3Axis::readConfig()) // pmacv3Axis::atFirstPoll)
previousPosition = previousPosition * motorRecResolution; previousPosition = previousPosition * motorRecResolution;
// Query the axis status // Query the axis status
@ -284,13 +263,13 @@ asynStatus pmacv3Axis::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 the
software limits set on the MCU and () are the hardware limit switches. In software limits set on the MCU and () are the hardware limit switches. In
other words, the EPICS/NICOS limits must be stricter than the software other words, the EPICS/NICOS limits should be stricter than the software
limits on the MCU which in turn should be stricter than the hardware limit limits on the MCU which in turn should be stricter than the hardware limit
switches. For example, if the hardware limit switches are at [-10, 10], the switches. For example, if the hardware limit switches are at [-10, 10], the
software limits could be at [-9, 9] and the EPICS / NICOS limits could be at 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 them
by 0.1 mm or 0.1 degree (depending on the axis type) on both sides. by limitsOffset on both sides.
*/ */
pl_status = pl_status =
pC_->getDoubleParam(axisNo_, pC_->motorLimitsOffset_, &limitsOffset); pC_->getDoubleParam(axisNo_, pC_->motorLimitsOffset_, &limitsOffset);
@ -542,7 +521,7 @@ asynStatus pmacv3Axis::doPoll(bool *moving) {
snprintf(command, sizeof(command), snprintf(command, sizeof(command),
"Maximum allowed following error exceeded (P%2.2d01 = %d). " "Maximum allowed following error exceeded (P%2.2d01 = %d). "
"Check if movement range is blocked." "Check if movement range is blocked. "
"Otherwise please call the support.", "Otherwise please call the support.",
axisNo_, error); axisNo_, error);
pl_status = setStringParam(pC_->motorMessageText_, command); pl_status = setStringParam(pC_->motorMessageText_, command);
@ -669,7 +648,7 @@ asynStatus pmacv3Axis::doPoll(bool *moving) {
} }
// Transform from motor to EPICS coordinates (see comment in // Transform from motor to EPICS coordinates (see comment in
// pmacv3Axis::readConfig()) // pmacv3Axis::atFirstPoll())
currentPosition = currentPosition / motorRecResolution; currentPosition = currentPosition / motorRecResolution;
pl_status = setDoubleParam(pC_->motorPosition_, currentPosition); pl_status = setDoubleParam(pC_->motorPosition_, currentPosition);

View File

@ -106,7 +106,6 @@ class pmacv3Axis : public sinqAxis {
protected: protected:
pmacv3Controller *pC_; pmacv3Controller *pC_;
asynStatus readConfig();
bool initial_poll_; bool initial_poll_;
bool waitForHandshake_; bool waitForHandshake_;
time_t timeAtHandshake_; time_t timeAtHandshake_;

View File

@ -1,6 +1,3 @@
// Needed to use strcpy_s from string.h
#define __STDC_WANT_LIB_EXT1__ 1
#include "pmacv3Controller.h" #include "pmacv3Controller.h"
#include "asynMotorController.h" #include "asynMotorController.h"
#include "asynOctetSyncIO.h" #include "asynOctetSyncIO.h"
@ -14,21 +11,21 @@
#include <unistd.h> #include <unistd.h>
/** /**
* @brief Copy src into dst and replace all carriage returns with spaces * @brief Copy src into dst and replace all carriage returns with spaces. This
* allows to print *dst with asynPrint.
*
* *
* @param dst Buffer for the modified string * @param dst Buffer for the modified string
* @param src Original string * @param src Original string
*/ */
void adjustResponseForPrint(char *dst, const char *src) { void adjustResponseForPrint(char *dst, const char *src, size_t buf_length) {
// Needed to use strcpy_s from string.h for (size_t i = 0; i < buf_length; i++) {
#ifdef __STDC_LIB_EXT1__ if (src[i] == '\r') {
strcpy_s(dst, src); dst[i] = ' ';
for (size_t i = 0; i < strlen(dst); i++) { } else {
if (dst[i] == '\r') { dst[i] = src[i];
dst[i] = '_';
} }
} }
#endif
} }
/** /**
@ -296,14 +293,14 @@ asynStatus pmacv3Controller::writeRead(int axisNo, const char *command,
// Second check: If this fails, give up and propagate the error. // Second check: If this fails, give up and propagate the error.
if (numExpectedResponses != numReceivedResponses) { if (numExpectedResponses != numReceivedResponses) {
adjustResponseForPrint(modResponse, response); adjustResponseForPrint(modResponse, response, MAXBUF_);
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nUnexpected response %s (_ are " "%s => line %d:\nUnexpected response '%s' (carriage "
"carriage returns) for command %s\n", "returns are replaced with spaces) for command %s\n",
__PRETTY_FUNCTION__, __LINE__, modResponse, command); __PRETTY_FUNCTION__, __LINE__, modResponse, command);
snprintf(drvMessageText, sizeof(drvMessageText), snprintf(drvMessageText, sizeof(drvMessageText),
"Received unexpected response %s (_ are " "Received unexpected response '%s' (carriage returns "
"carriage returns) for command %s. " "are replaced with spaces) for command %s. "
"Please call the support", "Please call the support",
modResponse, command); modResponse, command);
pl_status = setStringParam(motorMessageText_, drvMessageText); pl_status = setStringParam(motorMessageText_, drvMessageText);
@ -345,29 +342,6 @@ asynStatus pmacv3Controller::writeRead(int axisNo, const char *command,
} }
} }
if (status != asynSuccess) {
// 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
// with different error messages if more than one error ocurred before
// an error-free communication
pl_status =
getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem);
if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorStatusProblem_",
__PRETTY_FUNCTION__, __LINE__);
}
if (motorStatusProblem == 0) {
pl_status =
axis->setStringParam(this->motorMessageText_, drvMessageText);
if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorMessageText_",
__PRETTY_FUNCTION__, __LINE__);
}
}
}
// Log the overall status (communication successfull or not) // Log the overall status (communication successfull or not)
if (status == asynSuccess) { if (status == asynSuccess) {
@ -377,21 +351,43 @@ asynStatus pmacv3Controller::writeRead(int axisNo, const char *command,
__PRETTY_FUNCTION__, __LINE__, modResponse); __PRETTY_FUNCTION__, __LINE__, modResponse);
pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 0); pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 0);
} else { } else {
if (status == asynSuccess) { asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR,
asynPrint( "%s => line %d:\nCommunication failed for command %s (%s)\n",
lowLevelPortUser_, ASYN_TRACE_ERROR, __PRETTY_FUNCTION__, __LINE__, fullCommand,
"%s => line %d:\nCommunication failed for command %s (%s)\n", stringifyAsynStatus(status));
__PRETTY_FUNCTION__, __LINE__, fullCommand,
stringifyAsynStatus(status));
pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 1);
}
// 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
// with different error messages if more than one error ocurred before
// an error-free communication
pl_status =
getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorStatusCommsError_", return paramLibAccessFailed(pl_status, "motorStatusProblem_",
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
if (motorStatusProblem == 0) {
pl_status = axis->setStringParam(motorMessageText_, drvMessageText);
if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorMessageText_",
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = axis->setIntegerParam(motorStatusProblem_, 1);
if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorStatusProblem",
__PRETTY_FUNCTION__, __LINE__);
}
pl_status = axis->setIntegerParam(motorStatusProblem_, 1);
if (pl_status != asynSuccess) {
return paramLibAccessFailed(pl_status, "motorStatusCommsError_",
__PRETTY_FUNCTION__, __LINE__);
}
}
} }
return asynSuccess; return status;
} }
asynStatus pmacv3Controller::writeInt32(asynUser *pasynUser, epicsInt32 value) { asynStatus pmacv3Controller::writeInt32(asynUser *pasynUser, epicsInt32 value) {
@ -409,7 +405,7 @@ asynStatus pmacv3Controller::writeInt32(asynUser *pasynUser, epicsInt32 value) {
if (function == rereadEncoderPosition_) { if (function == rereadEncoderPosition_) {
return axis->rereadEncoder(); return axis->rereadEncoder();
} else if (function == readConfig_) { } else if (function == readConfig_) {
return axis->readConfig(); return axis->atFirstPoll();
} else { } else {
return sinqController::writeInt32(pasynUser, value); return sinqController::writeInt32(pasynUser, value);
} }
@ -429,7 +425,7 @@ asynStatus pmacv3Controller::errMsgCouldNotParseResponse(
const char *command, const char *response, int axisNo, const char *command, const char *response, int axisNo,
const char *functionName, int lineNumber) { const char *functionName, int lineNumber) {
char modifiedResponse[MAXBUF_] = {0}; char modifiedResponse[MAXBUF_] = {0};
adjustResponseForPrint(modifiedResponse, response); adjustResponseForPrint(modifiedResponse, response, MAXBUF_);
return sinqController::errMsgCouldNotParseResponse( return sinqController::errMsgCouldNotParseResponse(
command, modifiedResponse, axisNo, functionName, lineNumber); command, modifiedResponse, axisNo, functionName, lineNumber);
} }
@ -445,7 +441,7 @@ C wrapper for the controller constructor. Please refer to the pmacv3Controller
constructor documentation. constructor documentation.
*/ */
asynStatus pmacv3CreateController(const char *portName, asynStatus pmacv3CreateController(const char *portName,
const char *lowLevelPortName, int numAxes, const char *ipPortConfigName, int numAxes,
double movingPollPeriod, double movingPollPeriod,
double idlePollPeriod, double comTimeout) { double idlePollPeriod, double comTimeout) {
/* /*
@ -460,7 +456,7 @@ asynStatus pmacv3CreateController(const char *portName,
#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"
pmacv3Controller *pController = pmacv3Controller *pController =
new pmacv3Controller(portName, lowLevelPortName, numAxes, new pmacv3Controller(portName, ipPortConfigName, numAxes,
movingPollPeriod, idlePollPeriod, comTimeout); movingPollPeriod, idlePollPeriod, comTimeout);
return asynSuccess; return asynSuccess;