Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
eca513f3a0 | |||
26175290bf | |||
e316fcf67b | |||
6cf00adb60 | |||
9710d442b8 | |||
8dd1dc4af2 | |||
a758db1211 | |||
a11d10cb6c | |||
ba353c4e5d |
@ -4,7 +4,7 @@
|
||||
|
||||
## Overview
|
||||
|
||||
This is a driver for the Turbo PMAC motion controller with the SINQ communication protocol. It is based on the sinqMotor shared library (https://git.psi.ch/sinq-epics-modules/sinqmotor). The header files contain detailed documentation for all public functions. The headers themselves are exported when building the library to allow other drivers to depend on this one.
|
||||
This is a driver for the Turbo PMAC motion controller with the SINQ communication protocol. It is based on the sinqMotor shared library (https://gitea.psi.ch/lin-epics-modules/sinqMotor). The header files contain detailed documentation for all public functions. The headers themselves are exported when building the library to allow other drivers to depend on this one.
|
||||
|
||||
## User guide
|
||||
|
||||
@ -14,8 +14,6 @@ The folder "utils" contains utility scripts for working with pmac motor controll
|
||||
- writeRead.py: Allows sending commands to and receiving commands from a pmac controller over an ethernet connection.
|
||||
- analyzeTcpDump.py: Parse the TCP communication between an IOC and a MCU and format it into a dictionary. "demo.py" shows how this data can be easily visualized for analysis.
|
||||
|
||||
|
||||
|
||||
### IOC startup script
|
||||
|
||||
turboPmac exports the following IOC shell functions:
|
||||
|
@ -31,3 +31,14 @@ record(longout, "$(INSTR)FlushHardware") {
|
||||
field(PINI, "NO")
|
||||
field(VAL, "1")
|
||||
}
|
||||
|
||||
# If this PV is set to 1 (default), the position limits are read out from the
|
||||
# controller. Otherwise, the limits given in the substitution file (DHLM and
|
||||
# DLLM) are used.
|
||||
# This record is coupled to the parameter library via limFromHardware -> LIM_FROM_HARDWARE.
|
||||
record(longout, "$(INSTR)$(M):LimFromHardware") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) LIM_FROM_HARDWARE")
|
||||
field(PINI, "YES")
|
||||
field(VAL, "$(LIMFROMHARDWARE=1)")
|
||||
}
|
Submodule sinqMotor updated: e088bfbbcb...bdefc6090d
@ -13,6 +13,14 @@
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
struct turboPmacAxisImpl {
|
||||
bool waitForHandshake;
|
||||
time_t timeAtHandshake;
|
||||
// The axis status is used when enabling / disabling the motor
|
||||
int axisStatus;
|
||||
bool needInit;
|
||||
};
|
||||
|
||||
/*
|
||||
Contains all instances of turboPmacAxis which have been created and is used in
|
||||
the initialization hook function.
|
||||
@ -54,10 +62,11 @@ turboPmacAxis::turboPmacAxis(turboPmacController *pC, int axisNo,
|
||||
axes.push_back(this);
|
||||
}
|
||||
|
||||
// Initialize all member variables
|
||||
waitForHandshake_ = false;
|
||||
timeAtHandshake_ = 0;
|
||||
axisStatus_ = 0;
|
||||
pTurboPmacA_ = std::make_unique<turboPmacAxisImpl>(
|
||||
(turboPmacAxisImpl){.waitForHandshake = false,
|
||||
.timeAtHandshake = 0,
|
||||
.axisStatus = 0,
|
||||
.needInit = false});
|
||||
|
||||
// Provide initial values for some parameter library entries
|
||||
status = pC_->setIntegerParam(axisNo_, pC_->rereadEncoderPosition(), 0);
|
||||
@ -93,8 +102,8 @@ turboPmacAxis::turboPmacAxis(turboPmacController *pC, int axisNo,
|
||||
}
|
||||
|
||||
// Default values for the watchdog timeout mechanism
|
||||
offsetMovTimeout_ = 30; // seconds
|
||||
scaleMovTimeout_ = 2.0;
|
||||
setOffsetMovTimeout(30.0); // seconds
|
||||
setScaleMovTimeout(2.0);
|
||||
}
|
||||
|
||||
turboPmacAxis::~turboPmacAxis(void) {
|
||||
@ -158,15 +167,15 @@ asynStatus turboPmacAxis::init() {
|
||||
"with controller during IOC initialization. Check if you used "
|
||||
"\"pmacAsynIPPortConfigure\" instead of the standard "
|
||||
"\"drvAsynIPPortConfigure\" function in the .cmd file in order to "
|
||||
"create the port driver.\nTerminating IOC.\n",
|
||||
"create the port driver.\n",
|
||||
pC_->portName, axisNo(), __PRETTY_FUNCTION__, __LINE__);
|
||||
exit(-1);
|
||||
pTurboPmacA_->needInit = true;
|
||||
}
|
||||
nvals = sscanf(response, "%d %lf %lf %lf %lf %d", &axStatus, &motorPos,
|
||||
&motorVmax, &motorVelocity, &motorAccel, &acoDelay);
|
||||
|
||||
// The acoDelay is given in milliseconds -> Convert to seconds, rounded up
|
||||
offsetMovTimeout_ = std::ceil(acoDelay / 1000.0);
|
||||
setOffsetMovTimeout(std::ceil(acoDelay / 1000.0));
|
||||
|
||||
// The PMAC electronic specifies the acceleration in m/s^2. Since we
|
||||
// otherwise work with the base length mm, the acceleration is converted
|
||||
@ -215,7 +224,12 @@ asynStatus turboPmacAxis::init() {
|
||||
return status;
|
||||
}
|
||||
|
||||
return this->readEncoderType();
|
||||
status = this->readEncoderType();
|
||||
if (status == asynSuccess) {
|
||||
pTurboPmacA_->needInit = false;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
// Perform the actual poll
|
||||
@ -251,13 +265,20 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
||||
|
||||
// =========================================================================
|
||||
|
||||
if (pTurboPmacA_->needInit) {
|
||||
rw_status = init();
|
||||
if (rw_status != asynSuccess) {
|
||||
return rw_status;
|
||||
}
|
||||
}
|
||||
|
||||
// Are we currently waiting for a handshake?
|
||||
if (waitForHandshake_) {
|
||||
if (pTurboPmacA_->waitForHandshake) {
|
||||
|
||||
// Check if the handshake takes too long and communicate an error in
|
||||
// this case. A handshake should not take more than 5 seconds.
|
||||
time_t currentTime = time(NULL);
|
||||
bool timedOut = (currentTime > timeAtHandshake_ + 5);
|
||||
bool timedOut = (currentTime > pTurboPmacA_->timeAtHandshake + 5);
|
||||
|
||||
if (pC_->getMsgPrintControl().shouldBePrinted(
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, timedOut,
|
||||
@ -267,7 +288,7 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
||||
"handshake at %ld s and didn't get a positive reply yet "
|
||||
"(current time is %ld s).\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
timeAtHandshake_, currentTime);
|
||||
pTurboPmacA_->timeAtHandshake, currentTime);
|
||||
}
|
||||
|
||||
if (timedOut) {
|
||||
@ -279,6 +300,7 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
pTurboPmacA_->waitForHandshake = false;
|
||||
}
|
||||
|
||||
snprintf(command, sizeof(command), "P%2.2d23 P%2.2d01", axisNo_,
|
||||
@ -298,11 +320,11 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
||||
// If an error occurred while waiting for the handshake, abort the
|
||||
// waiting procedure and continue with the poll in order to handle
|
||||
// the error.
|
||||
waitForHandshake_ = false;
|
||||
pTurboPmacA_->waitForHandshake = false;
|
||||
} else if (handshakePerformed == 1) {
|
||||
// Handshake has been performed successfully -> Continue with the
|
||||
// poll
|
||||
waitForHandshake_ = false;
|
||||
pTurboPmacA_->waitForHandshake = false;
|
||||
} else {
|
||||
// Still waiting for the handshake - try again in the next busy
|
||||
// poll. This is already part of the movement procedure.
|
||||
@ -386,7 +408,7 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
||||
lowLimit = lowLimit + limitsOffset;
|
||||
|
||||
// Store the axis status
|
||||
axisStatus_ = axStatus;
|
||||
pTurboPmacA_->axisStatus = axStatus;
|
||||
|
||||
// Update the enablement PV
|
||||
pl_status = setIntegerParam(pC_->motorEnableRBV(),
|
||||
@ -459,13 +481,53 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
||||
*moving = true;
|
||||
break;
|
||||
case 5:
|
||||
// Motor is moving normally
|
||||
*moving = true;
|
||||
break;
|
||||
case 6:
|
||||
// Jog start
|
||||
*moving = true;
|
||||
break;
|
||||
case 7:
|
||||
// Jog forward
|
||||
*moving = true;
|
||||
break;
|
||||
case 8:
|
||||
// Jog backward
|
||||
*moving = true;
|
||||
break;
|
||||
case 9:
|
||||
// Jog break
|
||||
*moving = true;
|
||||
break;
|
||||
case 10:
|
||||
// Jog step forward
|
||||
*moving = true;
|
||||
break;
|
||||
case 11:
|
||||
// Jog step backward
|
||||
*moving = true;
|
||||
break;
|
||||
case 12:
|
||||
// Jog stop
|
||||
*moving = true;
|
||||
break;
|
||||
case 13:
|
||||
// Backlash
|
||||
*moving = true;
|
||||
break;
|
||||
case 14:
|
||||
// Move order out of range - this is also handled as an error
|
||||
*moving = false;
|
||||
break;
|
||||
case 15:
|
||||
// Move is interrupted, but the motor is not ready to receive another
|
||||
// move command. Therefore it is treated as still moving.
|
||||
*moving = true;
|
||||
break;
|
||||
case 16:
|
||||
// Move is resumed
|
||||
*moving = true;
|
||||
|
||||
asynPrint(
|
||||
pC_->pasynUser(), ASYN_TRACE_FLOW,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nAxis is moving\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||
|
||||
break;
|
||||
default:
|
||||
*moving = false;
|
||||
@ -547,19 +609,30 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorHighLimitFromDriver(),
|
||||
highLimit);
|
||||
int limFromHardware = 0;
|
||||
pl_status =
|
||||
pC_->getIntegerParam(axisNo_, pC_->limFromHardware(), &limFromHardware);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorHighLimitFromDriver_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
return pC_->paramLibAccessFailed(pl_status, "limFromHardware", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
pl_status =
|
||||
pC_->setDoubleParam(axisNo_, pC_->motorLowLimitFromDriver(), lowLimit);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorLowLimit_", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
if (limFromHardware != 0) {
|
||||
pl_status = pC_->setDoubleParam(
|
||||
axisNo_, pC_->motorHighLimitFromDriver(), highLimit);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(
|
||||
pl_status, "motorHighLimitFromDriver_", axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
pl_status = pC_->setDoubleParam(axisNo_, pC_->motorLowLimitFromDriver(),
|
||||
lowLimit);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorLowLimit_",
|
||||
axisNo_, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
}
|
||||
|
||||
pl_status = setMotorPosition(currentPosition);
|
||||
@ -962,8 +1035,8 @@ asynStatus turboPmacAxis::doMove(double position, int relative,
|
||||
|
||||
// In the next poll, we will check if the handshake has been performed in a
|
||||
// reasonable time
|
||||
waitForHandshake_ = true;
|
||||
timeAtHandshake_ = time(NULL);
|
||||
pTurboPmacA_->waitForHandshake = true;
|
||||
pTurboPmacA_->timeAtHandshake = time(NULL);
|
||||
|
||||
// Waiting for a handshake is already part of the movement procedure =>
|
||||
// Start the watchdog
|
||||
@ -1005,6 +1078,10 @@ asynStatus turboPmacAxis::stop(double acceleration) {
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the driver to idle state and move out of the handshake wait loop,
|
||||
// if we're currently inside it.
|
||||
pTurboPmacA_->waitForHandshake = false;
|
||||
|
||||
return rw_status;
|
||||
}
|
||||
|
||||
@ -1039,6 +1116,10 @@ asynStatus turboPmacAxis::doReset() {
|
||||
}
|
||||
}
|
||||
|
||||
// Reset the driver to idle state and move out of the handshake wait loop,
|
||||
// if we're currently inside it.
|
||||
pTurboPmacA_->waitForHandshake = false;
|
||||
|
||||
return rw_status;
|
||||
}
|
||||
|
||||
@ -1268,8 +1349,11 @@ asynStatus turboPmacAxis::enable(bool on) {
|
||||
// command and inform the user. We check the last known status of the axis
|
||||
// instead of "moving", since status -6 is also moving, but the motor can
|
||||
// actually be disabled in this state!
|
||||
if (axisStatus_ == 1 || axisStatus_ == 2 || axisStatus_ == 3 ||
|
||||
axisStatus_ == 4 || axisStatus_ == 5) {
|
||||
int axStatus = pTurboPmacA_->axisStatus;
|
||||
if (axStatus == 1 || axStatus == 2 || axStatus == 3 || axStatus == 4 ||
|
||||
axStatus == 5 || axStatus == 6 || axStatus == 7 || axStatus == 8 ||
|
||||
axStatus == 9 || axStatus == 10 || axStatus == 11 || axStatus == 12 ||
|
||||
axStatus == 13 || axStatus == 15 || axStatus == 16) {
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nAxis is not "
|
||||
"idle and can therefore not be enabled / disabled.\n",
|
||||
@ -1289,7 +1373,7 @@ asynStatus turboPmacAxis::enable(bool on) {
|
||||
|
||||
// Axis is already enabled / disabled and a new enable / disable command
|
||||
// was sent => Do nothing
|
||||
if ((axisStatus_ != -3) == on) {
|
||||
if ((axStatus != -3) == on) {
|
||||
asynPrint(
|
||||
pC_->pasynUser(), ASYN_TRACE_WARNING,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nAxis is already %s.\n",
|
||||
@ -1330,13 +1414,13 @@ asynStatus turboPmacAxis::enable(bool on) {
|
||||
if (rw_status != asynSuccess) {
|
||||
return rw_status;
|
||||
}
|
||||
nvals = sscanf(response, "%d", &axisStatus_);
|
||||
nvals = sscanf(response, "%d", &pTurboPmacA_->axisStatus);
|
||||
if (nvals != 1) {
|
||||
return pC_->couldNotParseResponse(command, response, axisNo_,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
if ((axisStatus_ != -3) == on) {
|
||||
if ((pTurboPmacA_->axisStatus != -3) == on) {
|
||||
bool moving = false;
|
||||
// Perform a poll to update the parameter library
|
||||
poll(&moving);
|
||||
@ -1346,7 +1430,7 @@ asynStatus turboPmacAxis::enable(bool on) {
|
||||
|
||||
// Failed to change axis status within timeout_enable_disable => Send a
|
||||
// corresponding message
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
|
||||
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nFailed to %s axis "
|
||||
"within %d seconds\n",
|
||||
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
|
||||
@ -1364,6 +1448,17 @@ asynStatus turboPmacAxis::enable(bool on) {
|
||||
return asynError;
|
||||
}
|
||||
|
||||
bool turboPmacAxis::needInit() { return pTurboPmacA_->needInit; }
|
||||
|
||||
/**
|
||||
* @brief Instruct the axis to run its init() function during the next poll
|
||||
*
|
||||
* @param needInit
|
||||
*/
|
||||
void turboPmacAxis::setNeedInit(bool needInit) {
|
||||
pTurboPmacA_->needInit = needInit;
|
||||
}
|
||||
|
||||
/*************************************************************************************/
|
||||
/** The following functions are C-wrappers, and can be called directly from
|
||||
* iocsh */
|
||||
|
@ -1,6 +1,9 @@
|
||||
#ifndef turboPmacAXIS_H
|
||||
#define turboPmacAXIS_H
|
||||
#include "sinqAxis.h"
|
||||
#include <memory>
|
||||
|
||||
struct turboPmacAxisImpl;
|
||||
|
||||
// Forward declaration of the controller class to resolve the cyclic dependency
|
||||
// between the controller and the axis .h-file. See
|
||||
@ -123,14 +126,24 @@ class turboPmacAxis : public sinqAxis {
|
||||
*/
|
||||
asynStatus handleError(int error, char *userMessage, int sizeUserMessage);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Check if the axis needs to run its initialization function
|
||||
*
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool needInit();
|
||||
|
||||
/**
|
||||
* @brief Instruct the axis to run its init() function during the next poll
|
||||
*
|
||||
* @param needInit
|
||||
*/
|
||||
void setNeedInit(bool needInit);
|
||||
|
||||
private:
|
||||
turboPmacController *pC_;
|
||||
|
||||
bool waitForHandshake_;
|
||||
time_t timeAtHandshake_;
|
||||
|
||||
// The axis status is used when enabling / disabling the motor
|
||||
int axisStatus_;
|
||||
std::unique_ptr<turboPmacAxisImpl> pTurboPmacA_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -31,6 +31,24 @@ void adjustResponseForPrint(char *dst, const char *src, size_t buf_length) {
|
||||
}
|
||||
}
|
||||
|
||||
struct turboPmacControllerImpl {
|
||||
|
||||
// Timeout for the communication process in seconds
|
||||
double comTimeout;
|
||||
|
||||
char lastResponse[sinqController::MAXBUF_];
|
||||
|
||||
// User for writing int32 values to the port driver.
|
||||
asynUser *pasynInt32SyncIOipPort;
|
||||
|
||||
// Indices of additional PVs
|
||||
int rereadEncoderPosition;
|
||||
int readConfig;
|
||||
int flushHardware;
|
||||
int limFromHardware;
|
||||
};
|
||||
#define NUM_turboPmac_DRIVER_PARAMS 3
|
||||
|
||||
turboPmacController::turboPmacController(const char *portName,
|
||||
const char *ipPortConfigName,
|
||||
int numAxes, double movingPollPeriod,
|
||||
@ -47,21 +65,25 @@ turboPmacController::turboPmacController(const char *portName,
|
||||
|
||||
{
|
||||
|
||||
// The paramLib indices are populated with the calls to createParam
|
||||
pTurboPmacC_ =
|
||||
std::make_unique<turboPmacControllerImpl>((turboPmacControllerImpl){
|
||||
.comTimeout = comTimeout,
|
||||
.lastResponse = {0},
|
||||
});
|
||||
|
||||
// Initialization of local variables
|
||||
asynStatus status = asynSuccess;
|
||||
|
||||
// Initialization of all member variables
|
||||
comTimeout_ = comTimeout;
|
||||
|
||||
// Maximum allowed number of subsequent timeouts before the user is
|
||||
// informed.
|
||||
maxSubsequentTimeouts_ = 10;
|
||||
setMaxSubsequentTimeouts(10);
|
||||
|
||||
// =========================================================================
|
||||
// Create additional parameter library entries
|
||||
|
||||
status = createParam("REREAD_ENCODER_POSITION", asynParamInt32,
|
||||
&rereadEncoderPosition_);
|
||||
&pTurboPmacC_->rereadEncoderPosition);
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
|
||||
@ -71,7 +93,8 @@ turboPmacController::turboPmacController(const char *portName,
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
status = createParam("READ_CONFIG", asynParamInt32, &readConfig_);
|
||||
status =
|
||||
createParam("READ_CONFIG", asynParamInt32, &pTurboPmacC_->readConfig);
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
|
||||
@ -81,7 +104,19 @@ turboPmacController::turboPmacController(const char *portName,
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
status = createParam("FLUSH_HARDWARE", asynParamInt32, &flushHardware_);
|
||||
status = createParam("FLUSH_HARDWARE", asynParamInt32,
|
||||
&pTurboPmacC_->flushHardware);
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
|
||||
"parameter failed with %s).\nTerminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
status = createParam("LIM_FROM_HARDWARE", asynParamInt32,
|
||||
&pTurboPmacC_->limFromHardware);
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
|
||||
@ -100,7 +135,7 @@ turboPmacController::turboPmacController(const char *portName,
|
||||
const char *message_from_device =
|
||||
"\006"; // Hex-code for ACK (acknowledge) -> Each message from the MCU
|
||||
// is terminated by this value
|
||||
status = pasynOctetSyncIO->setInputEos(pasynOctetSyncIOipPort_,
|
||||
status = pasynOctetSyncIO->setInputEos(pasynOctetSyncIOipPort(),
|
||||
message_from_device,
|
||||
strlen(message_from_device));
|
||||
if (status != asynSuccess) {
|
||||
@ -109,7 +144,7 @@ turboPmacController::turboPmacController(const char *portName,
|
||||
"(setting input EOS failed with %s).\nTerminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status));
|
||||
pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort_);
|
||||
pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
@ -121,7 +156,7 @@ turboPmacController::turboPmacController(const char *portName,
|
||||
"with %s).\nTerminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status));
|
||||
pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort_);
|
||||
pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort());
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
@ -131,13 +166,15 @@ turboPmacController::turboPmacController(const char *portName,
|
||||
We try to connect to the port via the port name provided by the constructor.
|
||||
If this fails, the function is terminated via exit.
|
||||
*/
|
||||
pasynInt32SyncIO->connect(ipPortConfigName, 0, &pasynInt32SyncIOipPort_,
|
||||
NULL);
|
||||
if (status != asynSuccess || pasynInt32SyncIOipPort_ == nullptr) {
|
||||
pasynInt32SyncIO->connect(ipPortConfigName, 0,
|
||||
&pTurboPmacC_->pasynInt32SyncIOipPort, NULL);
|
||||
if (status != asynSuccess ||
|
||||
pTurboPmacC_->pasynInt32SyncIOipPort == nullptr) {
|
||||
errlogPrintf("Controller \"%s\" => %s, line %d:\nFATAL ERROR (cannot "
|
||||
"connect to MCU controller).\n"
|
||||
"Terminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__);
|
||||
pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort());
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
@ -215,20 +252,20 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
*/
|
||||
status = pasynOctetSyncIO->writeRead(
|
||||
pasynOctetSyncIOipPort(), command, commandLength, response, MAXBUF_,
|
||||
comTimeout_, &nbytesOut, &nbytesIn, &eomReason);
|
||||
pTurboPmacC_->comTimeout, &nbytesOut, &nbytesIn, &eomReason);
|
||||
|
||||
msgPrintControlKey comKey =
|
||||
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
|
||||
if (status == asynTimeout) {
|
||||
|
||||
if (msgPrintControl_.shouldBePrinted(comKey, true, pasynUser())) {
|
||||
if (getMsgPrintControl().shouldBePrinted(comKey, true, pasynUser())) {
|
||||
asynPrint(
|
||||
this->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nTimeout while "
|
||||
"writing to the controller. Retrying ...%s\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
msgPrintControl_.getSuffix());
|
||||
getMsgPrintControl().getSuffix());
|
||||
}
|
||||
|
||||
timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText,
|
||||
@ -239,13 +276,14 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
checkMaxSubsequentTimeouts(timeoutCounter, axis);
|
||||
timeoutCounter += 1;
|
||||
|
||||
if (maxSubsequentTimeoutsExceeded_) {
|
||||
if (maxSubsequentTimeoutsExceeded()) {
|
||||
break;
|
||||
}
|
||||
|
||||
status = pasynOctetSyncIO->writeRead(
|
||||
pasynOctetSyncIOipPort(), command, commandLength, response,
|
||||
MAXBUF_, comTimeout_, &nbytesOut, &nbytesIn, &eomReason);
|
||||
MAXBUF_, pTurboPmacC_->comTimeout, &nbytesOut, &nbytesIn,
|
||||
&eomReason);
|
||||
if (status != asynTimeout) {
|
||||
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
@ -255,16 +293,28 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
}
|
||||
}
|
||||
} else if (status != asynSuccess) {
|
||||
if (msgPrintControl_.shouldBePrinted(comKey, true, pasynUser())) {
|
||||
if (getMsgPrintControl().shouldBePrinted(comKey, true, pasynUser())) {
|
||||
asynPrint(
|
||||
this->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nError %s while "
|
||||
"writing to the controller.%s\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status), msgPrintControl_.getSuffix());
|
||||
stringifyAsynStatus(status), getMsgPrintControl().getSuffix());
|
||||
}
|
||||
} else {
|
||||
msgPrintControl_.resetCount(comKey, pasynUser());
|
||||
getMsgPrintControl().resetCount(comKey, pasynUser());
|
||||
}
|
||||
|
||||
if (status != asynSuccess) {
|
||||
/*
|
||||
Since the communication failed, there is the possibility that the
|
||||
controller is not connected at all to the network. In that case, we
|
||||
cannot be sure that the information read out in the init method of the
|
||||
axis is still up-to-date the next time we get a connection. Therefore,
|
||||
an info flag is set which the axis object can use at the start of its
|
||||
poll method to try to initialize itself.
|
||||
*/
|
||||
axis->setNeedInit(true);
|
||||
}
|
||||
|
||||
if (timeoutStatus == asynError) {
|
||||
@ -301,16 +351,17 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
"string detected\"). Please call the support.",
|
||||
reasonStringified);
|
||||
|
||||
if (msgPrintControl_.shouldBePrinted(terminateKey, true, pasynUser())) {
|
||||
if (getMsgPrintControl().shouldBePrinted(terminateKey, true,
|
||||
pasynUser())) {
|
||||
|
||||
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nMessage "
|
||||
"terminated due to reason %s.%s\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||
reasonStringified, msgPrintControl_.getSuffix());
|
||||
reasonStringified, getMsgPrintControl().getSuffix());
|
||||
}
|
||||
} else {
|
||||
msgPrintControl_.resetCount(terminateKey, pasynUser());
|
||||
getMsgPrintControl().resetCount(terminateKey, pasynUser());
|
||||
}
|
||||
|
||||
/*
|
||||
@ -327,15 +378,15 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
if (numExpectedResponses != numReceivedResponses) {
|
||||
adjustResponseForPrint(modResponse, response, MAXBUF_);
|
||||
|
||||
if (msgPrintControl_.shouldBePrinted(numResponsesKey, true,
|
||||
pasynUser())) {
|
||||
if (getMsgPrintControl().shouldBePrinted(numResponsesKey, true,
|
||||
pasynUser())) {
|
||||
asynPrint(
|
||||
this->pasynUser(), ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line %d\nUnexpected "
|
||||
"response '%s' (carriage returns are replaced with spaces) "
|
||||
"for command %s.%s\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, modResponse,
|
||||
command, msgPrintControl_.getSuffix());
|
||||
command, getMsgPrintControl().getSuffix());
|
||||
}
|
||||
|
||||
snprintf(drvMessageText, sizeof(drvMessageText),
|
||||
@ -345,7 +396,7 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
modResponse, command);
|
||||
status = asynError;
|
||||
} else {
|
||||
msgPrintControl_.resetCount(numResponsesKey, pasynUser());
|
||||
getMsgPrintControl().resetCount(numResponsesKey, pasynUser());
|
||||
}
|
||||
|
||||
// Create custom error messages for different failure modes, if no error
|
||||
@ -384,15 +435,15 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
paramLibStatus =
|
||||
getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem);
|
||||
if (paramLibStatus != asynSuccess) {
|
||||
return paramLibAccessFailed(paramLibStatus, "motorStatusProblem_",
|
||||
return paramLibAccessFailed(paramLibStatus, "motorStatusProblem",
|
||||
axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
if (motorStatusProblem == 0) {
|
||||
paramLibStatus =
|
||||
axis->setStringParam(motorMessageText_, drvMessageText);
|
||||
axis->setStringParam(motorMessageText(), drvMessageText);
|
||||
if (paramLibStatus != asynSuccess) {
|
||||
return paramLibAccessFailed(paramLibStatus, "motorMessageText_",
|
||||
return paramLibAccessFailed(paramLibStatus, "motorMessageText",
|
||||
axisNo, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
@ -407,7 +458,7 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
paramLibStatus = axis->setIntegerParam(motorStatusProblem_, 1);
|
||||
if (paramLibStatus != asynSuccess) {
|
||||
return paramLibAccessFailed(paramLibStatus,
|
||||
"motorStatusCommsError_", axisNo,
|
||||
"motorStatusCommsError", axisNo,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
@ -421,13 +472,13 @@ asynStatus turboPmacController::doFlushHardware() {
|
||||
constant defined in pmacAsynIPPort.c. This reason is then used within
|
||||
the write method of pasynInt32SyncIO to select the flush function.
|
||||
*/
|
||||
int temp = pasynInt32SyncIOipPort_->reason;
|
||||
pasynInt32SyncIOipPort_->reason = FLUSH_HARDWARE;
|
||||
int temp = pTurboPmacC_->pasynInt32SyncIOipPort->reason;
|
||||
pTurboPmacC_->pasynInt32SyncIOipPort->reason = FLUSH_HARDWARE;
|
||||
asynStatus status = (asynStatus)pasynInt32SyncIO->write(
|
||||
pasynInt32SyncIOipPort_, 1, comTimeout_);
|
||||
pTurboPmacC_->pasynInt32SyncIOipPort, 1, pTurboPmacC_->comTimeout);
|
||||
|
||||
// Reset the status afterwards
|
||||
pasynInt32SyncIOipPort_->reason = temp;
|
||||
pTurboPmacC_->pasynInt32SyncIOipPort->reason = temp;
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -440,11 +491,11 @@ asynStatus turboPmacController::writeInt32(asynUser *pasynUser,
|
||||
turboPmacAxis *axis = getTurboPmacAxis(pasynUser);
|
||||
|
||||
// Handle custom PVs
|
||||
if (function == rereadEncoderPosition_) {
|
||||
if (function == rereadEncoderPosition()) {
|
||||
return axis->rereadEncoder();
|
||||
} else if (function == readConfig_) {
|
||||
} else if (function == readConfig()) {
|
||||
return axis->init();
|
||||
} else if (function == flushHardware_) {
|
||||
} else if (function == flushHardware()) {
|
||||
return doFlushHardware();
|
||||
} else {
|
||||
return sinqController::writeInt32(pasynUser, value);
|
||||
@ -462,6 +513,19 @@ asynStatus turboPmacController::couldNotParseResponse(const char *command,
|
||||
command, modifiedResponse, axisNo, functionName, lineNumber);
|
||||
}
|
||||
|
||||
int turboPmacController::rereadEncoderPosition() {
|
||||
return pTurboPmacC_->rereadEncoderPosition;
|
||||
}
|
||||
int turboPmacController::readConfig() { return pTurboPmacC_->readConfig; }
|
||||
int turboPmacController::flushHardware() { return pTurboPmacC_->flushHardware; }
|
||||
int turboPmacController::limFromHardware() {
|
||||
return pTurboPmacC_->limFromHardware;
|
||||
}
|
||||
|
||||
asynUser *turboPmacController::pasynInt32SyncIOipPort() {
|
||||
return pTurboPmacC_->pasynInt32SyncIOipPort;
|
||||
}
|
||||
|
||||
/*************************************************************************************/
|
||||
/** The following functions are C-wrappers, and can be called directly from
|
||||
* iocsh */
|
||||
|
@ -11,6 +11,9 @@
|
||||
#include "sinqAxis.h"
|
||||
#include "sinqController.h"
|
||||
#include "turboPmacAxis.h"
|
||||
#include <memory>
|
||||
|
||||
struct turboPmacControllerImpl;
|
||||
|
||||
class turboPmacController : public sinqController {
|
||||
public:
|
||||
@ -116,29 +119,17 @@ class turboPmacController : public sinqController {
|
||||
asynStatus doFlushHardware();
|
||||
|
||||
// Accessors for additional PVs
|
||||
int rereadEncoderPosition() { return rereadEncoderPosition_; }
|
||||
int readConfig() { return readConfig_; }
|
||||
int flushHardware() { return flushHardware_; }
|
||||
int rereadEncoderPosition();
|
||||
int readConfig();
|
||||
int flushHardware();
|
||||
int limFromHardware();
|
||||
|
||||
asynUser *pasynInt32SyncIOipPort() { return pasynInt32SyncIOipPort_; }
|
||||
asynUser *pasynInt32SyncIOipPort();
|
||||
|
||||
private:
|
||||
std::unique_ptr<turboPmacControllerImpl> pTurboPmacC_;
|
||||
|
||||
protected:
|
||||
// Timeout for the communication process in seconds
|
||||
double comTimeout_;
|
||||
|
||||
char lastResponse[MAXBUF_];
|
||||
|
||||
// User for writing int32 values to the port driver.
|
||||
asynUser *pasynInt32SyncIOipPort_;
|
||||
|
||||
// Indices of additional PVs
|
||||
#define FIRST_turboPmac_PARAM rereadEncoderPosition_
|
||||
int rereadEncoderPosition_;
|
||||
int readConfig_;
|
||||
int flushHardware_;
|
||||
#define LAST_turboPmac_PARAM flushHardware_
|
||||
};
|
||||
#define NUM_turboPmac_DRIVER_PARAMS \
|
||||
(&LAST_turboPmac_PARAM - &FIRST_turboPmac_PARAM + 1)
|
||||
|
||||
#endif /* turboPmacController_H */
|
||||
|
Reference in New Issue
Block a user