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:
4
Makefile
4
Makefile
@ -17,6 +17,10 @@ SOURCES += src/sinqController.cpp
|
||||
HEADERS += src/sinqAxis.h
|
||||
HEADERS += src/sinqController.h
|
||||
|
||||
# Store the record files
|
||||
TEMPLATES += db/asynRecord.db
|
||||
TEMPLATES += db/sinqMotor.db
|
||||
|
||||
USR_CFLAGS += -Wall -Wextra # -Werror
|
||||
|
||||
# MISCS would be the place to keep the stream device template files
|
||||
|
16
README.md
16
README.md
@ -6,19 +6,21 @@ This library offers base classes for EPICS motor drivers (`sinqAxis` and `sinqCo
|
||||
|
||||
## Features
|
||||
|
||||
sinqMotor offers a variety of additional methods for children classes to standardize certain patterns (e.g. writing messages to the IOC shell and the motor message PV). For a detailed description, please see the respective function documentation in the .h-file. All of these functions can be overwritten manually if e.g. a completely different implementation of `poll` is required.
|
||||
sinqMotor offers a variety of additional methods for children classes to standardize certain patterns (e.g. writing messages to the IOC shell and the motor message PV). For a detailed description, please see the respective function documentation in the .h-files. All of these functions can be overwritten manually if e.g. a completely different implementation of `poll` is required. Some functions are marked as virtual, because they are called from other functions of sinqMotor and therefore need runtime polymorphism. Functions without that marker are not called anywhere in sinqMotor.
|
||||
|
||||
### sinqController
|
||||
- `stringifyAsynStatus`: Convert the enum `asynStatus` into a human-readable string.
|
||||
- `errMsgCouldNotParseResponse`: Write a standardized message if parsing a device response failed
|
||||
- `paramLibAccessFailed`: Write a standardized message if accessing the parameter library failed
|
||||
- `errMsgCouldNotParseResponse`: Write a standardized message if parsing a device response failed.
|
||||
- `paramLibAccessFailed`: Write a standardized message if accessing the parameter library failed.
|
||||
|
||||
### sinqAxis
|
||||
- `atFirstPoll`: This function is executed once before the first poll. If it returns anything but `asynSuccess`, it retries.
|
||||
- `setWatchdogEnabled`: Enables / disables the watchdog. This function is also available in the IOC shell.
|
||||
- `checkMovTimeoutWatchdog`: Manages a watchdog mechanism for movement operations. This watchdog compares the actual time spent in a movement operation with an expected time, which is calculated based on the distance of the current and the target position.
|
||||
- `poll`: This is a wrapper around `doPoll` which performs some bookkeeping tasks before and after calling `doPoll`:
|
||||
|
||||
Before calling `doPoll`:
|
||||
- Try to execute `atFirstPoll` once (and retry, if that failed)
|
||||
- Try to execute `atFirstPoll` once during the lifetime of the IOC (and retry, if that failed)
|
||||
|
||||
After calling `doPoll`:
|
||||
- Reset `motorStatusProblem_`, `motorStatusCommsError_` and `motorMessageText_` if `doPoll` returned `asynSuccess`
|
||||
@ -27,8 +29,10 @@ sinqMotor offers a variety of additional methods for children classes to standar
|
||||
- `doPoll`: This is an empty function which should be overwritten by concrete driver implementations.
|
||||
- `move`: This function sets the absolute target position in the parameter library and then calls `doMove`.
|
||||
- `doMove`: This is an empty function which should be overwritten by concrete driver implementations.
|
||||
- `movementTimeoutWatchdog`: Manages a watchdog mechanism for movement operations.
|
||||
|
||||
- `home`: This function sets the absolute target position in the parameter library and then calls `doHome`. The target position is assumed to be the high limit, if the distance of the current position to it is larger than that to the low limit, and the low limit otherwise.
|
||||
- `doHome`: This is an empty function which should be overwritten by concrete driver implementations.
|
||||
- `setOffsetMovTimeout`: Set a linear offset for the expected movement time. This function is also available in the IOC shell.
|
||||
- `setScaleMovTimeout`: Set a scaling factor for the expected movement time. This function is also available in the IOC shell.
|
||||
|
||||
## Versioning
|
||||
|
||||
|
9
db/asynRecord.db
Executable file
9
db/asynRecord.db
Executable file
@ -0,0 +1,9 @@
|
||||
record(asyn,"$(P)$(R)")
|
||||
{
|
||||
field(DTYP,"asynRecordDevice")
|
||||
field(PORT,"$(PORT)")
|
||||
field(ADDR,"$(ADDR)")
|
||||
field(OMAX,"$(OMAX)")
|
||||
field(IMAX,"$(IMAX)")
|
||||
}
|
||||
|
126
db/sinqMotor.db
Executable file
126
db/sinqMotor.db
Executable file
@ -0,0 +1,126 @@
|
||||
record(motor,"$(P)$(M)")
|
||||
{
|
||||
field(DESC,"$(DESC)")
|
||||
field(DTYP,"$(DTYP)")
|
||||
field(DIR,"$(DIR)")
|
||||
field(VELO,"$(VELO)")
|
||||
field(VBAS,"$(VBAS)")
|
||||
field(ACCL,"$(ACCL)")
|
||||
field(BDST,"$(BDST)")
|
||||
field(BVEL,"$(BVEL)")
|
||||
field(BACC,"$(BACC)")
|
||||
field(OUT,"@asyn($(PORT),$(ADDR))")
|
||||
field(MRES,"$(MRES)")
|
||||
field(PREC,"$(PREC)")
|
||||
field(EGU,"$(EGU)")
|
||||
field(INIT,"$(INIT)")
|
||||
field(PINI, "NO")
|
||||
field(TWV,"1")
|
||||
field(RDBD,"$(RDBD)")
|
||||
field(RTRY, "0")
|
||||
}
|
||||
|
||||
# The message text
|
||||
record(waveform, "$(P)$(M)-MsgTxt") {
|
||||
field(DTYP, "asynOctetRead")
|
||||
field(INP, "@asyn($(PORT),$(N),1) MOTOR_MESSAGE_TEXT")
|
||||
field(FTVL, "CHAR")
|
||||
field(NELM, "$(MsgTxtSize=200)") # Should be the same as MAXBUF in the driver code
|
||||
field(SCAN, "I/O Intr")
|
||||
}
|
||||
|
||||
# Encoder type
|
||||
record(waveform, "$(P)$(M):Encoder_Type") {
|
||||
field(DTYP, "asynOctetRead")
|
||||
field(INP, "@asyn($(PORT),$(N),1) ENCODER_TYPE")
|
||||
field(FTVL, "CHAR")
|
||||
field(NELM, "80")
|
||||
field(SCAN, "I/O Intr")
|
||||
}
|
||||
|
||||
# enable axis
|
||||
record(longout, "$(P)$(M):Enable") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(PORT),$(N),1) ENABLE_AXIS")
|
||||
field(PINI, "NO")
|
||||
}
|
||||
|
||||
# enable axis
|
||||
record(longin, "$(P)$(M):Enable_RBV") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(INP, "@asyn($(PORT),$(N),1) AXIS_ENABLED")
|
||||
field(PINI, "YES")
|
||||
}
|
||||
|
||||
# reread encoder
|
||||
record(longout, "$(P)$(M):Reread_Encoder") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(PORT),$(N),1) REREAD_ENCODER_POSITION")
|
||||
field(PINI, "NO")
|
||||
}
|
||||
|
||||
# reread encoder
|
||||
record(longin, "$(P)$(M):Reread_Encoder_RBV") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(INP, "@asyn($(PORT),$(N),1) REREAD_ENCODER_POSITION_RBV")
|
||||
field(PINI, "YES")
|
||||
}
|
||||
|
||||
# reread encoder
|
||||
record(longout, "$(P)$(M):Read_Config") {
|
||||
field(DTYP, "asynInt32")
|
||||
field(OUT, "@asyn($(PORT),$(N),1) READ_CONFIG")
|
||||
field(PINI, "NO")
|
||||
}
|
||||
|
||||
# Provides the motor resolution MRES via an additional PV as explained here:
|
||||
# https://epics.anl.gov/tech-talk/2020/msg00378.php
|
||||
record(ao,"$(P)$(M):Resolution") {
|
||||
field(DESC, "$(M) resolution")
|
||||
field(DOL, "$(P)$(M).MRES CP MS")
|
||||
field(OMSL, "closed_loop")
|
||||
field(DTYP, "asynFloat64")
|
||||
field(OUT, "@asyn($(PORT),$(ADDR)) MOTOR_REC_RESOLUTION")
|
||||
field(PREC, "$(PREC)")
|
||||
}
|
||||
|
||||
# ===================================================================
|
||||
# The following records read the high / low limits from the parameter
|
||||
# library and copy those values into the corresponding fields of the main motor record.
|
||||
# This strategy is described here: https://epics.anl.gov/tech-talk/2022/msg00464.php
|
||||
|
||||
# Helper record for the high limit which is filled in by the driver
|
||||
record(ai, "$(P)$(M):MOTOR_HIGH_LIMIT-RBV")
|
||||
{
|
||||
field(DTYP, "asynFloat64")
|
||||
field(PREC, "$(PREC)")
|
||||
field(INP, "@asyn($(PORT),$(ADDR)) MOTOR_HIGH_LIMIT_FROM_DRIVER")
|
||||
field(SCAN, "I/O Intr")
|
||||
field(FLNK, "$(P)$(M):PUSH_DHLM_TO_FIELD")
|
||||
}
|
||||
|
||||
# Push the value into the field of the main motor record
|
||||
record(ao, "$(P)$(M):PUSH_DHLM_TO_FIELD") {
|
||||
field(PREC, "$(PREC)")
|
||||
field(DOL, "$(P)$(M):MOTOR_HIGH_LIMIT-RBV CP")
|
||||
field(OUT, "$(P)$(M).DHLM")
|
||||
field(OMSL, "closed_loop") # This configuration keeps the input value $(P)$(M):HLM_ADD_OFFSET and the output field $(P)$(M).DHLM in sync
|
||||
}
|
||||
|
||||
# Helper record for the low limit which is filled in by the driver
|
||||
record(ai, "$(P)$(M):MOTOR_LOW_LIMIT-RBV")
|
||||
{
|
||||
field(DTYP, "asynFloat64")
|
||||
field(PREC, "$(PREC)")
|
||||
field(INP, "@asyn($(PORT),$(ADDR)) MOTOR_LOW_LIMIT_FROM_DRIVER")
|
||||
field(SCAN, "I/O Intr")
|
||||
field(FLNK, "$(P)$(M):PUSH_DLLM_TO_FIELD")
|
||||
}
|
||||
|
||||
# Push the value into the field of the main motor record
|
||||
record(ao, "$(P)$(M):PUSH_DLLM_TO_FIELD") {
|
||||
field(PREC, "$(PREC)")
|
||||
field(DOL, "$(P)$(M):MOTOR_LOW_LIMIT-RBV CP")
|
||||
field(OUT, "$(P)$(M).DLLM")
|
||||
field(OMSL, "closed_loop") # This configuration keeps the input value $(P)$(M):LLM_ADD_OFFSET and the output field $(P)$(M).DLLM in sync
|
||||
}
|
@ -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