After reimplementing the pmacV3 driver using the sinqMotor parent class,
quite some changes have accumulated. Besides various code changes, especially the documentation has been improved.
This commit is contained in:
@@ -3,8 +3,8 @@
|
||||
#include <math.h>
|
||||
#include <unistd.h>
|
||||
|
||||
sinqAxis::sinqAxis(class sinqController *pC, int axis)
|
||||
: asynMotorAxis((asynMotorController *)pC, axis), pC_(pC) {
|
||||
sinqAxis::sinqAxis(class sinqController *pC, int axisNo)
|
||||
: asynMotorAxis((asynMotorController *)pC, axisNo), pC_(pC) {
|
||||
|
||||
initial_poll_ = true;
|
||||
watchdogMovActive_ = false;
|
||||
@@ -21,12 +21,13 @@ asynStatus sinqAxis::poll(bool *moving) {
|
||||
// =========================================================================
|
||||
|
||||
// If this poll is the initial poll, check if the parameter library has
|
||||
// already been initialized. If not, force EPCIS to repeat the poll until
|
||||
// already been initialized. If not, force EPICS to repeat the poll until
|
||||
// the initialization is complete (or until a timeout is reached). Once the
|
||||
// parameter library has been initialized, read configuration data from the
|
||||
// motor controller into it.
|
||||
if (initial_poll_) {
|
||||
poll_status = atFirstPoll();
|
||||
|
||||
if (poll_status == asynSuccess) {
|
||||
initial_poll_ = false;
|
||||
} else {
|
||||
@@ -35,7 +36,7 @@ asynStatus sinqAxis::poll(bool *moving) {
|
||||
if (init_poll_counter_ % 10 == 0) {
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nRunning function 'atFirstPoll' "
|
||||
"failed %d times with error %s.",
|
||||
"failed %d times with error %s.\n",
|
||||
__PRETTY_FUNCTION__, __LINE__, init_poll_counter_,
|
||||
pC_->stringifyAsynStatus(poll_status));
|
||||
}
|
||||
@@ -95,7 +96,7 @@ asynStatus sinqAxis::doPoll(bool *moving) { return asynSuccess; }
|
||||
asynStatus sinqAxis::move(double position, int relative, double minVelocity,
|
||||
double maxVelocity, double acceleration) {
|
||||
|
||||
double target_position = 0.0;
|
||||
double targetPosition = 0.0;
|
||||
|
||||
// Status of parameter library operations
|
||||
asynStatus pl_status = asynSuccess;
|
||||
@@ -113,13 +114,13 @@ asynStatus sinqAxis::move(double position, int relative, double minVelocity,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
target_position = position + motorPosition;
|
||||
targetPosition = position + motorPosition;
|
||||
} else {
|
||||
target_position = position;
|
||||
targetPosition = position;
|
||||
}
|
||||
|
||||
// Set the target position
|
||||
pl_status = setDoubleParam(pC_->motorTargetPosition_, target_position);
|
||||
pl_status = setDoubleParam(pC_->motorTargetPosition_, targetPosition);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorTargetPosition_",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
@@ -133,17 +134,65 @@ asynStatus sinqAxis::doMove(double position, int relative, double minVelocity,
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
asynStatus sinqAxis::movementTimeoutWatchdog(bool moving) {
|
||||
asynStatus pl_status;
|
||||
asynStatus sinqAxis::home(double minVelocity, double maxVelocity,
|
||||
double acceleration, int forwards) {
|
||||
|
||||
// Not moving -> Watchdog inactive
|
||||
if (!moving) {
|
||||
watchdogMovActive_ = false;
|
||||
return asynSuccess;
|
||||
double targetPosition = 0.0;
|
||||
double position = 0.0;
|
||||
double highLimit = 0.0;
|
||||
double lowLimit = 0.0;
|
||||
|
||||
// Status of parameter library operations
|
||||
asynStatus pl_status = asynSuccess;
|
||||
|
||||
// =========================================================================
|
||||
|
||||
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorPosition_, &position);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorPosition_",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
if (!watchdogMovActive_) {
|
||||
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorHighLimit_, &highLimit);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorHighLimit_",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
pl_status = pC_->getDoubleParam(axisNo_, pC_->motorLowLimit_, &lowLimit);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorLowLimit_",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
if (std::fabs(position - highLimit) > std::fabs(position - lowLimit)) {
|
||||
targetPosition = highLimit;
|
||||
} else {
|
||||
targetPosition = lowLimit;
|
||||
}
|
||||
|
||||
// Set the target position
|
||||
pl_status = setDoubleParam(pC_->motorTargetPosition_, targetPosition);
|
||||
if (pl_status != asynSuccess) {
|
||||
return pC_->paramLibAccessFailed(pl_status, "motorTargetPosition_",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
return doHome(minVelocity, maxVelocity, acceleration, forwards);
|
||||
}
|
||||
|
||||
asynStatus sinqAxis::doHome(double minVelocity, double maxVelocity,
|
||||
double acceleration, int forwards) {
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
asynStatus sinqAxis::setWatchdogEnabled(bool enable) {
|
||||
watchdogEnabled_ = enable;
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
asynStatus sinqAxis::startMovTimeoutWatchdog() {
|
||||
if (watchdogEnabled_) {
|
||||
// These parameters are only needed in this branch
|
||||
double motorPosition = 0.0;
|
||||
double motorTargetPosition = 0.0;
|
||||
@@ -151,6 +200,7 @@ asynStatus sinqAxis::movementTimeoutWatchdog(bool moving) {
|
||||
double motorAccel = 0.0;
|
||||
time_t timeContSpeed = 0;
|
||||
time_t timeAccel = 0;
|
||||
asynStatus pl_status;
|
||||
|
||||
// Activate the watchdog
|
||||
watchdogMovActive_ = true;
|
||||
@@ -187,8 +237,21 @@ asynStatus sinqAxis::movementTimeoutWatchdog(bool moving) {
|
||||
expectedArrivalTime_ =
|
||||
time(NULL) + offsetMovTimeout_ +
|
||||
scaleMovTimeout_ * (timeContSpeed + 2 * timeAccel);
|
||||
}
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
} else if (expectedArrivalTime_ < time(NULL)) {
|
||||
asynStatus sinqAxis::checkMovTimeoutWatchdog(bool moving) {
|
||||
asynStatus pl_status;
|
||||
|
||||
// Not moving or watchdog not active
|
||||
if (!watchdogEnabled_ || !moving) {
|
||||
watchdogMovActive_ = false;
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
// Check if the expected time of arrival has been exceeded.
|
||||
if (expectedArrivalTime_ < time(NULL)) {
|
||||
// Check the watchdog
|
||||
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nAxis %d exceeded the expected arrival time "
|
||||
|
||||
190
src/sinqAxis.h
190
src/sinqAxis.h
@@ -3,23 +3,39 @@ This class extends asynMotorAxis by some features used in SINQ.
|
||||
|
||||
Stefan Mathis, November 2024
|
||||
*/
|
||||
|
||||
#ifndef __SINQDRIVER
|
||||
#define __SINQDRIVER
|
||||
#include "asynMotorAxis.h"
|
||||
|
||||
class epicsShareClass sinqAxis : public asynMotorAxis {
|
||||
public:
|
||||
sinqAxis(class sinqController *pC_, int axis);
|
||||
|
||||
/**
|
||||
This function is executed at the very first poll after the IOC startup. If
|
||||
it returns anything else than 'asynSuccess', the function is evaluated again
|
||||
after 100 ms until it succeeds. Every 10 trials a warning is emitted.
|
||||
* @brief Construct a new sinqAxis object
|
||||
*
|
||||
* @param pC_ Pointer to the controller of the axis
|
||||
* @param axis Index of the axis
|
||||
*/
|
||||
asynStatus atFirstPoll();
|
||||
sinqAxis(class sinqController *pC_, int axisNo);
|
||||
|
||||
/**
|
||||
Wrapper around doPoll which performs the following operations;
|
||||
* @brief This function is executed once during the very first poll.
|
||||
*
|
||||
* This function is executed at the very first poll after the IOC startup.
|
||||
If it returns anything else than 'asynSuccess', the function is evaluated
|
||||
again after 100 ms until it succeeds. Every 10 trials a warning is emitted.
|
||||
The default implementation just returns asynSuccess and is meant to be
|
||||
overwritten by concrete driver implementations.
|
||||
*
|
||||
* @return asynStatus
|
||||
*/
|
||||
virtual asynStatus atFirstPoll();
|
||||
|
||||
/**
|
||||
* @brief Perform some standardized operation before and after the concrete
|
||||
`doPoll` implementation.
|
||||
*
|
||||
* Wrapper around doPoll which performs the following operations:
|
||||
Before calling doPoll:
|
||||
- Try to execute atFirstPoll once (and retry, if that failed)
|
||||
|
||||
@@ -28,35 +44,121 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
|
||||
doPoll returned asynSuccess
|
||||
- Run `callParamCallbacks`
|
||||
- Return the status of `doPoll`
|
||||
*
|
||||
* @param moving Forwarded to `doPoll`.
|
||||
* @return asynStatus Forward the status of `doPoll`, unless one of
|
||||
the parameter library operation fails (in that case, returns the failed
|
||||
operation status).
|
||||
*/
|
||||
asynStatus poll(bool *moving);
|
||||
|
||||
/**
|
||||
Implementation of the "proper", device-specific poll method. This method
|
||||
should be implemented by a child class of sinqAxis.
|
||||
* @brief Implementation of the "proper", device-specific poll method. This
|
||||
method should be implemented by a child class of sinqAxis.
|
||||
*
|
||||
* @param moving Should be set to true, if the axis is moving,
|
||||
* and false otherwise.
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus doPoll(bool *moving);
|
||||
virtual asynStatus doPoll(bool *moving);
|
||||
|
||||
/**
|
||||
Wrapper around move which calculates the (absolute) target position and
|
||||
stores it in the parameter library. After that, it calls and returns doMove
|
||||
* @brief Perform some standardized operation before and after the concrete
|
||||
`doMove` implementation.
|
||||
|
||||
* Wrapper around `doMove` which calculates the (absolute) target position
|
||||
and stores it in the parameter library. After that, it calls and returns
|
||||
`doMove`.
|
||||
*
|
||||
* @param position Forwarded to `doMove`.
|
||||
* @param relative Forwarded to `doMove`.
|
||||
* @param minVelocity Forwarded to `doMove`.
|
||||
* @param maxVelocity Forwarded to `doMove`.
|
||||
* @param acceleration Forwarded to `doMove`.
|
||||
* @return asynStatus Forward the status of `doMove`, unless one of
|
||||
the parameter library operation fails (in that case, returns the failed
|
||||
operation status).
|
||||
*/
|
||||
asynStatus move(double position, int relative, double minVelocity,
|
||||
double maxVelocity, double acceleration);
|
||||
|
||||
/**
|
||||
Implementation of the "proper", device-specific poll method. This method
|
||||
should be implemented by a child class of sinqAxis.
|
||||
*/
|
||||
asynStatus doMove(double position, int relative, double minVelocity,
|
||||
double maxVelocity, double acceleration);
|
||||
* @brief Implementation of the "proper", device-specific move method. This
|
||||
method should be implemented by a child class of sinqAxis.
|
||||
*
|
||||
* @param position Target position `VAL` from the motor record
|
||||
* @param relative Specifies, whether the target position is
|
||||
relative or absolute.
|
||||
* @param minVelocity Minimum velocity VMIN from the motor record
|
||||
* @param maxVelocity Maximum velocity VMAX from the motor record
|
||||
* @param acceleration Acceleration ACCEL from the motor record
|
||||
* @return asynStatus
|
||||
*/
|
||||
virtual asynStatus doMove(double position, int relative, double minVelocity,
|
||||
double maxVelocity, double acceleration);
|
||||
|
||||
/**
|
||||
* @brief Perform some standardized operation before and after the concrete
|
||||
`doHome` implementation.
|
||||
*
|
||||
* Wrapper around move which calculates the (absolute) target position and
|
||||
stores it in the parameter library. The target position in a homing maneuver
|
||||
is calculated as follows:
|
||||
|
||||
if abs(current position - high limit) > abs(current position - low limit)
|
||||
{
|
||||
high limit
|
||||
}
|
||||
else
|
||||
{
|
||||
low limit
|
||||
}
|
||||
|
||||
After that, it calls and returns doHome.
|
||||
*
|
||||
* @param minVelocity Forwarded to `doHome`.
|
||||
* @param maxVelocity Forwarded to `doHome`.
|
||||
* @param acceleration Forwarded to `doHome`.
|
||||
* @param forwards Forwarded to `doHome`.
|
||||
* @return asynStatus Forward the status of `doHome`, unless one of
|
||||
the parameter library operation fails (in that case, returns the failed
|
||||
operation status).
|
||||
*/
|
||||
asynStatus home(double minVelocity, double maxVelocity, double acceleration,
|
||||
int forwards);
|
||||
|
||||
/**
|
||||
* @brief Implementation of the "proper", device-specific home method. This
|
||||
method should be implemented by a child class of sinqAxis.
|
||||
*
|
||||
* @param minVelocity Minimum velocity VMIN from the motor record
|
||||
* @param maxVelocity Maximum velocity VMAX from the motor record
|
||||
* @param acceleration Acceleration ACCEL from the motor record
|
||||
* @param forwards Is 1, if the motor record field HOMF was used
|
||||
to trigger the homing, and 0, if HOMR was used.
|
||||
* @return asynStatus
|
||||
*/
|
||||
virtual asynStatus doHome(double minVelocity, double maxVelocity,
|
||||
double acceleration, int forwards);
|
||||
|
||||
/**
|
||||
* @brief Start the watchdog for the movement, if the watchdog is not
|
||||
disabled. See the documentation of checkMovTimeoutWatchdog for more details.
|
||||
*
|
||||
* @return asynStatus If one of the parameter library operations
|
||||
used to get the values for the timeout calculation failed, return that
|
||||
status, otherwise return asynSuccess.
|
||||
*/
|
||||
asynStatus startMovTimeoutWatchdog();
|
||||
|
||||
/**
|
||||
* @brief Check if the watchdog timed out
|
||||
*
|
||||
Manages a timeout mechanism for the movement:
|
||||
If the movement takes too long, create an error message and return
|
||||
asynError. The watchdog is started when moving switches from "false" to
|
||||
"true" and stopped when moving switches from "true" to "false". At the
|
||||
watchdog start, the estimated movement time is calculated as
|
||||
If the axis is moving and the movement takes too long, create an error
|
||||
message and return asynError. The watchdog is started when moving switches
|
||||
from "false" to "true" and stopped when moving switches from "true" to
|
||||
"false". At the watchdog start, the estimated movement time is calculated as
|
||||
|
||||
t = offsetMovTimeout_ + scaleMovTime_ * [timeContSpeed + 2*timeAccel]
|
||||
|
||||
@@ -68,24 +170,57 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
|
||||
The values motorTargetPosition, motorVelBase, motorAccel and
|
||||
positionAtMovementStart are taken from the parameter library. Therefore it
|
||||
is necessary to populate them before using this function. If they are not
|
||||
given, both speed and velocity are assumed to be infinity. This means that
|
||||
given, both speed and velocity are assumed to be infinite. This means that
|
||||
timeContSpeed and/or timeAcc are set to zero. motorTargetPosition is
|
||||
populated automatically when using the doMove function.
|
||||
|
||||
The values offsetMovTimeout_ and scaleMovTimeout_ can be set directly from
|
||||
the IOC shell with the functions setScaleMovTimeout and setOffsetMovTimeout,
|
||||
if sinqMotor is loaded via the "require" mechanism.
|
||||
*
|
||||
* @param moving Should be the "moving" status from `poll` /
|
||||
`doPoll`.
|
||||
* @return asynStatus Return asynError, if the watchdog timed out,
|
||||
and asynSuccess otherwise.
|
||||
*/
|
||||
asynStatus movementTimeoutWatchdog(bool moving);
|
||||
asynStatus checkMovTimeoutWatchdog(bool moving);
|
||||
|
||||
// Setter for offsetMovTimeout
|
||||
asynStatus setOffsetMovTimeout(time_t offsetMovTimeout) {
|
||||
/**
|
||||
* @brief Enable / disable the watchdog. Also available in the IOC shell
|
||||
* (see "extern C" section in sinqController.cpp).
|
||||
*
|
||||
* If enable is set to false and the watchdog is currently running, this
|
||||
* function stops it immediately.
|
||||
*
|
||||
* @param enabled
|
||||
* @return asynStatus
|
||||
*/
|
||||
virtual asynStatus setWatchdogEnabled(bool enable);
|
||||
|
||||
/**
|
||||
* @brief Set the offsetMovTimeout. Also available in the IOC shell
|
||||
* (see "extern C" section in sinqController.cpp).
|
||||
*
|
||||
See documentation of `checkMovTimeoutWatchdog` for details.
|
||||
*
|
||||
* @param offsetMovTimeout Offset (in seconds)
|
||||
* @return asynStatus
|
||||
*/
|
||||
virtual asynStatus setOffsetMovTimeout(time_t offsetMovTimeout) {
|
||||
offsetMovTimeout_ = offsetMovTimeout;
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
// Setter for scaleMovTimeout
|
||||
asynStatus setScaleMovTimeout(time_t scaleMovTimeout) {
|
||||
/**
|
||||
* @brief Set the scaleMovTimeout. Also available in the IOC shell
|
||||
* (see "extern C" section in sinqController.cpp).
|
||||
*
|
||||
See documentation of `checkMovTimeoutWatchdog` for details.
|
||||
*
|
||||
* @param scaleMovTimeout Scaling factor (in seconds)
|
||||
* @return asynStatus
|
||||
*/
|
||||
virtual asynStatus setScaleMovTimeout(time_t scaleMovTimeout) {
|
||||
scaleMovTimeout_ = scaleMovTimeout;
|
||||
return asynSuccess;
|
||||
}
|
||||
@@ -100,6 +235,7 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
|
||||
time_t expectedArrivalTime_;
|
||||
time_t offsetMovTimeout_;
|
||||
double scaleMovTimeout_;
|
||||
bool watchdogEnabled_;
|
||||
bool watchdogMovActive_;
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,62 +1,91 @@
|
||||
/*
|
||||
This class contains the necessary changes to have an additional text fields
|
||||
for messages with each axis.
|
||||
|
||||
Code lifted from Torsten Boegershausen ESS code.
|
||||
|
||||
Mark Koennecke, March 2017
|
||||
|
||||
Added code to manage an interMessageSleep
|
||||
|
||||
Mark Koennecke, February 2024
|
||||
*/
|
||||
|
||||
#include "sinqController.h"
|
||||
#include "asynMotorController.h"
|
||||
#include "asynOctetSyncIO.h"
|
||||
#include "epicsExport.h"
|
||||
#include "iocsh.h"
|
||||
#include "sinqAxis.h"
|
||||
#include <errlog.h>
|
||||
|
||||
sinqController::sinqController(const char *portName, const char *SINQPortName,
|
||||
int numAxes, const int &extraParams)
|
||||
sinqController::sinqController(const char *portName,
|
||||
const char *ipPortConfigName, int numAxes,
|
||||
double movingPollPeriod, double idlePollPeriod,
|
||||
int numExtraParams)
|
||||
: asynMotorController(
|
||||
portName, numAxes + 1, NUM_MOTOR_DRIVER_PARAMS + extraParams,
|
||||
portName,
|
||||
// As described in the function documentation, an offset of 1 is
|
||||
// added for better readability of the configuration.
|
||||
numAxes + 1,
|
||||
/*
|
||||
2 Parameters are added in sinqController:
|
||||
- MOTOR_MESSAGE_TEXT
|
||||
- MOTOR_TARGET_POSITION
|
||||
*/
|
||||
NUM_MOTOR_DRIVER_PARAMS + numExtraParams + 2,
|
||||
0, // No additional interfaces beyond those in base class
|
||||
0, // No additional callback interfaces beyond those in base class
|
||||
ASYN_CANBLOCK | ASYN_MULTIDEVICE,
|
||||
1, // autoconnect
|
||||
0, 0) // Default priority and stack size
|
||||
1, // autoconnect
|
||||
0,
|
||||
0) // Default priority and stack size
|
||||
{
|
||||
|
||||
// Initialization of local variables
|
||||
asynStatus status = asynSuccess;
|
||||
|
||||
// Initialization of all member variables
|
||||
lowLevelPortUser_ = nullptr;
|
||||
|
||||
// =========================================================================;
|
||||
|
||||
// MOTOR_MESSAGE_TEXT corresponds to the PV definition inside
|
||||
// sinqn_asyn_motor.db. This text is used to forward status messages to
|
||||
// NICOS and in turn to the user.
|
||||
/*
|
||||
We try to connect to the port via the port name provided by the constructor.
|
||||
If this fails, the function is terminated via exit.
|
||||
*/
|
||||
pasynOctetSyncIO->connect(ipPortConfigName, 0, &lowLevelPortUser_, NULL);
|
||||
if (status != asynSuccess || lowLevelPortUser_ == nullptr) {
|
||||
errlogPrintf(
|
||||
"%s => line %d:\nFATAL ERROR (cannot connect to MCU controller).\n"
|
||||
"Terminating IOC",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// =========================================================================;
|
||||
|
||||
// MOTOR_MESSAGE_TEXT corresponds to the PV definition inside sinqMotor.db.
|
||||
// This text is used to forward status messages to NICOS and in turn to the
|
||||
// user.
|
||||
status =
|
||||
createParam("MOTOR_MESSAGE_TEXT", asynParamOctet, &motorMessageText_);
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
|
||||
"with %s)\n. Terminating IOC",
|
||||
"with %s).\nTerminating IOC",
|
||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Internal parameter library entry which stores the movement target
|
||||
status = createParam("MOTOR_TARGET_POSITION", asynParamOctet,
|
||||
status = createParam("MOTOR_TARGET_POSITION", asynParamFloat64,
|
||||
&motorTargetPosition_);
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
|
||||
"with %s)\n. Terminating IOC",
|
||||
"with %s).\nTerminating IOC",
|
||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Poller configuration
|
||||
status = startPoller(movingPollPeriod, idlePollPeriod, 1);
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nFATAL ERROR (starting the poller failed "
|
||||
"with %s).\nTerminating IOC",
|
||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
||||
pasynOctetSyncIO->disconnect(lowLevelPortUser_);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
sinqController::~sinqController(void) {
|
||||
@@ -76,9 +105,9 @@ asynStatus sinqController::errMsgCouldNotParseResponse(const char *command,
|
||||
"command %s.\n",
|
||||
functionName, lineNumber, response, command);
|
||||
|
||||
setStringParam(motorMessageText_,
|
||||
"Could not interpret MCU response. Please "
|
||||
"call the software support");
|
||||
setStringParam(
|
||||
motorMessageText_,
|
||||
"Could not interpret MCU response. Please call the software support");
|
||||
setIntegerParam(motorStatusCommsError_, 1);
|
||||
return asynError;
|
||||
}
|
||||
@@ -92,7 +121,7 @@ asynStatus sinqController::paramLibAccessFailed(asynStatus status,
|
||||
// Log the error message and try to propagate it
|
||||
asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\n Accessing the parameter library failed for "
|
||||
"parameter %s",
|
||||
"parameter %s.\n",
|
||||
functionName, lineNumber, parameter);
|
||||
setStringParam(
|
||||
motorMessageText_,
|
||||
@@ -105,18 +134,21 @@ asynStatus sinqController::paramLibAccessFailed(asynStatus status,
|
||||
// Static pointers (valid for the entire lifetime of the IOC). The number behind
|
||||
// the strings gives the integer number of each variant (see also method
|
||||
// stringifyAsynStatus)
|
||||
const char *asynSuccessStringified = "success"; // 0
|
||||
const char *asynTimeoutStringified = "timeout"; // 1
|
||||
const char *asynOverflowStringified = "overflow"; // 2
|
||||
const char *asynErrorStringified = "error"; // 3
|
||||
const char *asynDisconnectedStringified = "disconnected"; // 4
|
||||
const char *asynDisabledStringified = "disabled"; // 5
|
||||
const char *asynParamAlreadyExistsStringified = "parameter already exists"; // 6
|
||||
const char *asynParamNotFoundStringified = "parameter not found"; // 7
|
||||
const char *asynParamWrongTypeStringified = "wrong type"; // 8
|
||||
const char *asynParamBadIndexStringified = "bad index"; // 9
|
||||
const char *asynParamUndefinedStringified = "parameter undefined"; // 10
|
||||
const char *asynParamInvalidListStringified = "invalid list"; // 11
|
||||
const char asynSuccessStringified[] = "success"; // 0
|
||||
const char asynTimeoutStringified[] = "timeout"; // 1
|
||||
const char asynOverflowStringified[] = "overflow"; // 2
|
||||
const char asynErrorStringified[] = "error"; // 3
|
||||
const char asynDisconnectedStringified[] = "disconnected"; // 4
|
||||
const char asynDisabledStringified[] = "disabled"; // 5
|
||||
const char asynParamAlreadyExistsStringified[] =
|
||||
"parameter already exists"; // 6
|
||||
const char asynParamNotFoundStringified[] = "parameter not found"; // 7
|
||||
const char asynParamWrongTypeStringified[] = "wrong type"; // 8
|
||||
const char asynParamBadIndexStringified[] = "bad index"; // 9
|
||||
const char asynParamUndefinedStringified[] = "parameter undefined"; // 10
|
||||
const char asynParamInvalidListStringified[] = "invalid list"; // 11
|
||||
const char inputDidNotMatchAsynStatus[] =
|
||||
"Input did not match any variant of asynStatus";
|
||||
|
||||
const char *sinqController::stringifyAsynStatus(asynStatus status) {
|
||||
// See
|
||||
@@ -124,7 +156,7 @@ const char *sinqController::stringifyAsynStatus(asynStatus status) {
|
||||
// and
|
||||
// https://github.com/epics-modules/asyn/blob/master/asyn/asynPortDriver/paramErrors.h
|
||||
// for the definition of the error codes
|
||||
// The pragma is necessary since the param lib error codes are "tacked on"
|
||||
// The pragma is necessary since the param lib error codes are "tacked onto"
|
||||
// the enum, which results in compiler warnings otherwise.
|
||||
#pragma GCC diagnostic ignored "-Wswitch"
|
||||
switch (status) {
|
||||
@@ -159,13 +191,67 @@ const char *sinqController::stringifyAsynStatus(asynStatus status) {
|
||||
"%s => line %d:\nInput did not match any variant of asynStatus.\n",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
|
||||
return "Input did not match any variant of asynStatus";
|
||||
return inputDidNotMatchAsynStatus;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Provide the setters to IOC shell
|
||||
extern "C" {
|
||||
|
||||
/**
|
||||
* @brief Enable / disable the watchdog (FFI implementation)
|
||||
*
|
||||
* @param portName Name of the controller
|
||||
* @param axisNo Axis number
|
||||
* @param enable If 0, disable the watchdog, otherwise enable
|
||||
* it
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus setWatchdogEnabled(const char *portName, int axisNo, int enable) {
|
||||
|
||||
sinqController *pC;
|
||||
pC = (sinqController *)findAsynPortDriver(portName);
|
||||
if (pC == nullptr) {
|
||||
errlogPrintf("%s => line %d:\nPort %s not found.", __PRETTY_FUNCTION__,
|
||||
__LINE__, portName);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
asynMotorAxis *asynAxis = pC->getAxis(axisNo);
|
||||
sinqAxis *axis = dynamic_cast<sinqAxis *>(asynAxis);
|
||||
if (axis == nullptr) {
|
||||
errlogPrintf("%s => line %d:\nAxis %d does not exist or is not an "
|
||||
"instance of sinqAxis.",
|
||||
__PRETTY_FUNCTION__, __LINE__, axisNo);
|
||||
}
|
||||
|
||||
return axis->setWatchdogEnabled(enable != 0);
|
||||
}
|
||||
|
||||
static const iocshArg setWatchdogEnabledArg0 = {"Controller port name",
|
||||
iocshArgString};
|
||||
static const iocshArg setWatchdogEnabledArg1 = {"Axis number", iocshArgInt};
|
||||
static const iocshArg setWatchdogEnabledArg2 = {
|
||||
"Enabling / disabling the watchdog", iocshArgInt};
|
||||
static const iocshArg *const setWatchdogEnabledArgs[] = {
|
||||
&setWatchdogEnabledArg0, &setWatchdogEnabledArg1, &setWatchdogEnabledArg2};
|
||||
static const iocshFuncDef setWatchdogEnabledDef = {"setWatchdogEnabled", 3,
|
||||
setWatchdogEnabledArgs};
|
||||
|
||||
static void setWatchdogEnabledCallFunc(const iocshArgBuf *args) {
|
||||
setWatchdogEnabled(args[0].sval, args[1].ival, args[2].ival);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @brief Set the offsetMovTimeout (FFI implementation)
|
||||
*
|
||||
* @param portName Name of the controller
|
||||
* @param axisNo Axis number
|
||||
* @param offsetMovTimeout Offset (in seconds)
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus setOffsetMovTimeout(const char *portName, int axisNo,
|
||||
double offsetMovTimeout) {
|
||||
|
||||
@@ -200,11 +286,19 @@ static const iocshFuncDef setOffsetMovTimeoutDef = {"setOffsetMovTimeout", 3,
|
||||
setOffsetMovTimeoutArgs};
|
||||
|
||||
static void setOffsetMovTimeoutCallFunc(const iocshArgBuf *args) {
|
||||
setOffsetMovTimeout(args[0].sval, args[1].ival, args[1].dval);
|
||||
setOffsetMovTimeout(args[0].sval, args[1].ival, args[2].dval);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// =============================================================================
|
||||
|
||||
/**
|
||||
* @brief Set the setScaleMovTimeout (FFI implementation)
|
||||
*
|
||||
* @param portName Name of the controller
|
||||
* @param axisNo Axis number
|
||||
* @param scaleMovTimeout Scaling factor (in seconds)
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus setScaleMovTimeout(const char *portName, int axisNo,
|
||||
double scaleMovTimeout) {
|
||||
|
||||
@@ -238,12 +332,15 @@ static const iocshFuncDef setScaleMovTimeoutDef = {"setScaleMovTimeout", 3,
|
||||
setScaleMovTimeoutArgs};
|
||||
|
||||
static void setScaleMovTimeoutCallFunc(const iocshArgBuf *args) {
|
||||
setScaleMovTimeout(args[0].sval, args[1].ival, args[1].dval);
|
||||
setScaleMovTimeout(args[0].sval, args[1].ival, args[2].dval);
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
static void sinqControllerRegister(void) {
|
||||
iocshRegister(&setOffsetMovTimeoutDef, setOffsetMovTimeoutCallFunc);
|
||||
iocshRegister(&setScaleMovTimeoutDef, setScaleMovTimeoutCallFunc);
|
||||
iocshRegister(&setWatchdogEnabledDef, setWatchdogEnabledCallFunc);
|
||||
}
|
||||
epicsExportRegistrar(sinqControllerRegister);
|
||||
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
/*
|
||||
This class contains the necessary changes to have an additional text fields
|
||||
for messages with each axis.
|
||||
This class extends asynMotorController by some features used in SINQ.
|
||||
|
||||
Code lifted from Torsten Boegershausens ESS code.
|
||||
|
||||
Mark Koennecke, March 2017
|
||||
Stefan Mathis, November 2024
|
||||
*/
|
||||
|
||||
#ifndef __sinqController
|
||||
@@ -16,24 +13,77 @@
|
||||
|
||||
class epicsShareClass sinqController : public asynMotorController {
|
||||
public:
|
||||
/**
|
||||
* @brief Construct a new sinqController object
|
||||
*
|
||||
* @param portName Controller can be found by findAsynPortDriver
|
||||
* with this name
|
||||
* @param ipPortConfigName IP adress and port configuration of the
|
||||
* controller unit, used to connect via pasynOctetSyncIO->connect
|
||||
* @param numAxes Pointers to the axes are stored in the array
|
||||
pAxes_ which has the length specified here. When getting an axis, the
|
||||
`getAxis` function indexes into this array. A length of 8 would therefore
|
||||
mean that the axis slots 0 to 7 are available. However, in order to keep the
|
||||
axis enumeration in sync with the electronics counting logic, we start
|
||||
counting the axes with 1 and end at 8. Therefore, an offset of 1 is added
|
||||
when forwarding this number to asynMotorController.
|
||||
* @param movingPollPeriod Time between polls when moving (in seconds)
|
||||
* @param idlePollPeriod Time between polls when not moving (in
|
||||
seconds)
|
||||
* @param extraParams Number of extra parameter library entries
|
||||
* created in a concrete driver implementation
|
||||
*/
|
||||
sinqController(const char *portName, const char *SINQPortName, int numAxes,
|
||||
const int &extraParams = 2);
|
||||
~sinqController(void);
|
||||
double movingPollPeriod, double idlePollPeriod,
|
||||
int numExtraParams);
|
||||
|
||||
/**
|
||||
* @brief Destroy the sinqController object
|
||||
*
|
||||
* In general, there is no real memory cleanup strategy in asynMotor,
|
||||
* because objects are expected to be alive for the entire lifetime of the
|
||||
* IOC. We just clean up the allocated axes array here.
|
||||
*/
|
||||
virtual ~sinqController(void);
|
||||
|
||||
friend class sinqAxis;
|
||||
|
||||
/**
|
||||
If accessing the parameter library failed (return status != asynSuccess),
|
||||
calling this function writes a standardized message to both the IOC shell
|
||||
and the motor message text PV. It then returns the input status.
|
||||
* @brief Error handling in case accessing the parameter library failed.
|
||||
*
|
||||
* If accessing the parameter library failed (return status !=
|
||||
asynSuccess), calling this function writes a standardized message to both
|
||||
the IOC shell and the motor message text PV. It then returns the input
|
||||
status.
|
||||
*
|
||||
* @param status Status of the failed parameter library access
|
||||
* @param parameter Name of the parameter, used to create the
|
||||
error messages.
|
||||
* @param functionName Name of the caller function. It is recommended
|
||||
to use a macro, e.g. __func__ or __PRETTY_FUNCTION__.
|
||||
* @param lineNumber Source code line where this function is
|
||||
called. It is recommended to use a macro, e.g. __LINE__.
|
||||
* @return asynStatus Returns input status.
|
||||
*/
|
||||
asynStatus paramLibAccessFailed(asynStatus status, const char *parameter,
|
||||
const char *functionName, int lineNumber);
|
||||
|
||||
/**
|
||||
This function writes a standardized message to both the IOC shell and
|
||||
* @brief Error handling in case parsing a command response failed.
|
||||
*
|
||||
* This function writes a standardized message to both the IOC shell and
|
||||
the motor message text PV in case parsing a response (e.g. via sscanf)
|
||||
failed. It always returns asynError.
|
||||
failed. It always returns asynError. This is convenience feature so the
|
||||
function call can be used as a return value in an error handling branch.
|
||||
*
|
||||
* @param command Command which led to the unparseable message
|
||||
* @param response Response which wasn't parseable
|
||||
* @param axisNo_ Axis where the problem occurred
|
||||
* @param functionName Name of the caller function. It is recommended
|
||||
to use a macro, e.g. __func__ or __PRETTY_FUNCTION__.
|
||||
* @param lineNumber Source code line where this function is
|
||||
called. It is recommended to use a macro, e.g. __LINE__.
|
||||
* @return asynStatus Returns asynError.
|
||||
*/
|
||||
asynStatus errMsgCouldNotParseResponse(const char *command,
|
||||
const char *response, int axisNo_,
|
||||
@@ -41,8 +91,10 @@ class epicsShareClass sinqController : public asynMotorController {
|
||||
int lineNumber);
|
||||
|
||||
/**
|
||||
Convert an asynStatus into a descriptive string. This string can then e.g.
|
||||
be used to create debugging messages.
|
||||
* @brief Convert an asynStatus into a descriptive string.
|
||||
*
|
||||
* @param status Status which should be converted to a string.
|
||||
* @return const char*
|
||||
*/
|
||||
const char *stringifyAsynStatus(asynStatus status);
|
||||
|
||||
|
||||
4
src/sinqMotor.dbd
Normal file
4
src/sinqMotor.dbd
Normal file
@@ -0,0 +1,4 @@
|
||||
#---------------------------------------------
|
||||
# SINQ specific DB definitions
|
||||
#---------------------------------------------
|
||||
registrar(sinqControllerRegister)
|
||||
Reference in New Issue
Block a user