Compare commits

...

10 Commits

Author SHA1 Message Date
bdefc6090d Added getters and setters for wasMoving and targetPosition 2025-06-10 14:09:20 +02:00
c2eca33ce8 Ready for release 1.0 2025-05-23 12:25:48 +02:00
87980e403c Fixed bug regarding the poll period member variables 2025-05-23 11:17:34 +02:00
b95e782ea8 Added accessor for maxSubsequentTimeoutsExceeded flag 2025-05-23 10:10:54 +02:00
cd7cc75eb7 Added destructors for the classes in order to use the PIMPL idiom with
uniqe_ptr
2025-05-23 09:38:41 +02:00
83aa437b6b Applied PIMPL by moving members to internal struct sinqAxisImpl
This change makes sinqMotor ready for 1.x releases where ABI stability
and backwards compatibility is guaranteed.
2025-05-22 13:56:44 +02:00
275672aaef Removed lock in reset and replaced it with wakeupPoller call
Trying to lock the controller from one of its axes may lead to
segfaults, hence it is not advisable.
2025-05-16 15:50:26 +02:00
61c5ec749e Merge branch 'main' of gitea.psi.ch:lin-epics-modules/sinqMotor 2025-05-15 14:26:15 +02:00
1cf4b9ab25 Made some functions virtual so they can be overwritten 2025-05-15 14:26:00 +02:00
a0b674b26e Made some functions virtual so they can be overwritten 2025-05-15 14:24:53 +02:00
6 changed files with 379 additions and 230 deletions

View File

@ -20,6 +20,8 @@ void msgPrintControlKey::format(char *buffer, size_t bufferSize) {
// ============================================================================= // =============================================================================
msgPrintControl::~msgPrintControl() = default;
bool msgPrintControl::shouldBePrinted(msgPrintControlKey &key, bool wantToPrint, bool msgPrintControl::shouldBePrinted(msgPrintControlKey &key, bool wantToPrint,
asynUser *pasynUser) { asynUser *pasynUser) {

View File

@ -84,6 +84,12 @@ template <> struct hash<msgPrintControlKey> {
*/ */
class msgPrintControl { class msgPrintControl {
public: public:
/**
* @brief Destroy the msgPrintControl object
*
*/
~msgPrintControl();
/** /**
* @brief Checks if the error message associated with "key" has been printed * @brief Checks if the error message associated with "key" has been printed
* more than `this->maxRepetitions_` times in a row. If yes, returns false, * more than `this->maxRepetitions_` times in a row. If yes, returns false,

View File

@ -3,11 +3,31 @@
#include "sinqAxis.h" #include "sinqAxis.h"
#include "epicsExport.h" #include "epicsExport.h"
#include "iocsh.h" #include "iocsh.h"
#include "msgPrintControl.h"
#include "sinqController.h" #include "sinqController.h"
#include <epicsTime.h>
#include <errlog.h> #include <errlog.h>
#include <math.h> #include <math.h>
#include <unistd.h> #include <unistd.h>
struct sinqAxisImpl {
// Internal variables used in the movement timeout watchdog
time_t expectedArrivalTime;
time_t offsetMovTimeout;
double scaleMovTimeout;
bool watchdogMovActive;
// Store the motor target position for the movement time calculation
double targetPosition;
bool wasMoving;
/*
Store the time since the last poll
*/
epicsTimeStamp lastPollTime;
};
// Generic fallback - if the compiler tries to compile this function, it fails. // Generic fallback - if the compiler tries to compile this function, it fails.
template <typename T> template <typename T>
asynStatus setAxisParam(sinqAxis *axis, const char *indexName, asynStatus setAxisParam(sinqAxis *axis, const char *indexName,
@ -184,18 +204,23 @@ sinqAxis::sinqAxis(class sinqController *pC, int axisNo)
: asynMotorAxis((asynMotorController *)pC, axisNo), pC_(pC) { : asynMotorAxis((asynMotorController *)pC, axisNo), pC_(pC) {
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
watchdogMovActive_ = false; epicsTimeStamp lastPollTime;
scaleMovTimeout_ = 2.0; epicsTimeGetCurrent(&lastPollTime);
offsetMovTimeout_ = 30;
targetPosition_ = 0.0;
wasMoving_ = false;
epicsTimeGetCurrent(&lastPollTime_); pSinqA_ = std::make_unique<sinqAxisImpl>(
(sinqAxisImpl){.expectedArrivalTime = 0,
.offsetMovTimeout = 30,
.scaleMovTimeout = 2.0,
.watchdogMovActive = false,
.targetPosition = 0.0,
.wasMoving = false,
.lastPollTime = lastPollTime});
// This check is also done in asynMotorAxis, but there the IOC continues /*
// running even though the configuration is incorrect. When failing this This check is also done in asynMotorAxis, but there the IOC continues
// check, the IOC is stopped, since this is definitely a configuration running even though the configuration is incorrect. When failing this check,
// problem. the IOC is stopped, since this is definitely a configuration problem.
*/
if ((axisNo < 0) || (axisNo >= pC->numAxes())) { if ((axisNo < 0) || (axisNo >= pC->numAxes())) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR, asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR " "Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
@ -313,6 +338,8 @@ sinqAxis::sinqAxis(class sinqController *pC, int axisNo)
} }
} }
sinqAxis::~sinqAxis() = default;
asynStatus sinqAxis::poll(bool *moving) { asynStatus sinqAxis::poll(bool *moving) {
// Local variable declaration // Local variable declaration
@ -346,10 +373,10 @@ asynStatus sinqAxis::poll(bool *moving) {
*/ */
if (adaptivePolling != 0 && pC_->outstandingForcedFastPolls() == 0) { if (adaptivePolling != 0 && pC_->outstandingForcedFastPolls() == 0) {
// Motor wasn't moving during the last poll // Motor wasn't moving during the last poll
if (!wasMoving_) { if (!pSinqA_->wasMoving) {
// Add the idle poll period // Add the idle poll period
epicsTimeStamp earliestTimeNextPoll = lastPollTime_; epicsTimeStamp earliestTimeNextPoll = pSinqA_->lastPollTime;
epicsTimeAddSeconds(&earliestTimeNextPoll, pC_->idlePollPeriod()); epicsTimeAddSeconds(&earliestTimeNextPoll, pC_->idlePollPeriod());
if (epicsTimeLessThanEqual(&earliestTimeNextPoll, &ts) == 0) { if (epicsTimeLessThanEqual(&earliestTimeNextPoll, &ts) == 0) {
@ -360,7 +387,7 @@ asynStatus sinqAxis::poll(bool *moving) {
} }
// Update the start time of the last poll // Update the start time of the last poll
lastPollTime_ = ts; pSinqA_->lastPollTime = ts;
/* /*
If the "motorMessageText" record currently contains an error message, it If the "motorMessageText" record currently contains an error message, it
@ -411,7 +438,7 @@ asynStatus sinqAxis::poll(bool *moving) {
assumed that the homing procedure is done. assumed that the homing procedure is done.
*/ */
getAxisParamChecked(this, motorStatusHome, &homing); getAxisParamChecked(this, motorStatusHome, &homing);
if (homing == 1 && !(*moving) && wasMoving_) { if (homing == 1 && !(*moving) && pSinqA_->wasMoving) {
setAxisParamChecked(this, motorStatusHome, false); setAxisParamChecked(this, motorStatusHome, false);
setAxisParamChecked(this, motorStatusHomed, true); setAxisParamChecked(this, motorStatusHomed, true);
setAxisParamChecked(this, motorStatusAtHome, true); setAxisParamChecked(this, motorStatusAtHome, true);
@ -419,7 +446,7 @@ asynStatus sinqAxis::poll(bool *moving) {
// Update the wasMoving status // Update the wasMoving status
if (pC_->outstandingForcedFastPolls() == 0) { if (pC_->outstandingForcedFastPolls() == 0) {
wasMoving_ = *moving; pSinqA_->wasMoving = *moving;
} }
// Check and update the watchdog // Check and update the watchdog
@ -467,7 +494,7 @@ asynStatus sinqAxis::move(double position, int relative, double minVelocity,
// Store the target position internally // Store the target position internally
getAxisParamChecked(this, motorRecResolution, &motorRecRes); getAxisParamChecked(this, motorRecResolution, &motorRecRes);
targetPosition_ = position * motorRecRes; pSinqA_->targetPosition = position * motorRecRes;
status = doMove(position, relative, minVelocity, maxVelocity, acceleration); status = doMove(position, relative, minVelocity, maxVelocity, acceleration);
if (status != asynSuccess) { if (status != asynSuccess) {
@ -485,7 +512,7 @@ asynStatus sinqAxis::move(double position, int relative, double minVelocity,
setAxisParamChecked(this, motorStatusAtHome, false); setAxisParamChecked(this, motorStatusAtHome, false);
// Needed for adaptive polling // Needed for adaptive polling
wasMoving_ = true; pSinqA_->wasMoving = true;
return pC_->callParamCallbacks(); return pC_->callParamCallbacks();
} }
@ -507,7 +534,7 @@ asynStatus sinqAxis::home(double minVelocity, double maxVelocity,
setAxisParamChecked(this, motorStatusHome, true); setAxisParamChecked(this, motorStatusHome, true);
setAxisParamChecked(this, motorStatusHomed, false); setAxisParamChecked(this, motorStatusHomed, false);
setAxisParamChecked(this, motorStatusAtHome, false); setAxisParamChecked(this, motorStatusAtHome, false);
wasMoving_ = true; pSinqA_->wasMoving = true;
status = assertConnected(); status = assertConnected();
if (status != asynSuccess) { if (status != asynSuccess) {
@ -543,15 +570,7 @@ asynStatus sinqAxis::reset() {
if (status == asynSuccess) { if (status == asynSuccess) {
// Perform some fast polls // Perform some fast polls
pC_->lock(); pC_->wakeupPoller();
bool moving = false;
for (int i = 0; i < 5; i++) {
epicsThreadSleep(pC_->movingPollPeriod());
if (poll(&moving) == asynSuccess) {
break;
}
}
pC_->unlock();
} }
status = assertConnected(); status = assertConnected();
@ -689,7 +708,7 @@ asynStatus sinqAxis::startMovTimeoutWatchdog() {
time_t timeAccel = 0; time_t timeAccel = 0;
// Activate the watchdog // Activate the watchdog
watchdogMovActive_ = true; pSinqA_->watchdogMovActive = true;
/* /*
NOTE: This function must not be called in the constructor (e.g. in order NOTE: This function must not be called in the constructor (e.g. in order
@ -725,8 +744,9 @@ asynStatus sinqAxis::startMovTimeoutWatchdog() {
motorVelocity = motorVelocityRec * motorRecRes; motorVelocity = motorVelocityRec * motorRecRes;
if (pl_status == asynSuccess) { if (pl_status == asynSuccess) {
timeContSpeed = std::ceil( timeContSpeed =
std::fabs(targetPosition_ - motorPos) / motorVelocity); std::ceil(std::fabs(pSinqA_->targetPosition - motorPos) /
motorVelocity);
} }
} }
@ -742,11 +762,11 @@ asynStatus sinqAxis::startMovTimeoutWatchdog() {
} }
// Calculate the expected arrival time // Calculate the expected arrival time
expectedArrivalTime_ = pSinqA_->expectedArrivalTime =
time(NULL) + offsetMovTimeout_ + time(NULL) + pSinqA_->offsetMovTimeout +
scaleMovTimeout_ * (timeContSpeed + 2 * timeAccel); pSinqA_->scaleMovTimeout * (timeContSpeed + 2 * timeAccel);
} else { } else {
watchdogMovActive_ = false; pSinqA_->watchdogMovActive = false;
} }
return asynSuccess; return asynSuccess;
} }
@ -757,8 +777,8 @@ asynStatus sinqAxis::checkMovTimeoutWatchdog(bool moving) {
getAxisParamChecked(this, motorEnableMovWatchdog, &enableMovWatchdog); getAxisParamChecked(this, motorEnableMovWatchdog, &enableMovWatchdog);
// Not moving or watchdog not active / enabled // Not moving or watchdog not active / enabled
if (enableMovWatchdog == 0 || !moving || !watchdogMovActive_) { if (enableMovWatchdog == 0 || !moving || !pSinqA_->watchdogMovActive) {
watchdogMovActive_ = false; pSinqA_->watchdogMovActive = false;
return asynSuccess; return asynSuccess;
} }
@ -768,7 +788,7 @@ asynStatus sinqAxis::checkMovTimeoutWatchdog(bool moving) {
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
// Check if the expected time of arrival has been exceeded. // Check if the expected time of arrival has been exceeded.
if (expectedArrivalTime_ < time(NULL)) { if (pSinqA_->expectedArrivalTime < time(NULL)) {
// Check the watchdog // Check the watchdog
if (pC_->getMsgPrintControl().shouldBePrinted(key, true, if (pC_->getMsgPrintControl().shouldBePrinted(key, true,
pC_->pasynUser())) { pC_->pasynUser())) {
@ -776,7 +796,7 @@ asynStatus sinqAxis::checkMovTimeoutWatchdog(bool moving) {
"Controller \"%s\", axis %d => %s, line %d:\nExceeded " "Controller \"%s\", axis %d => %s, line %d:\nExceeded "
"expected arrival time %ld (current time is %ld).\n", "expected arrival time %ld (current time is %ld).\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
expectedArrivalTime_, time(NULL)); pSinqA_->expectedArrivalTime, time(NULL));
} }
setAxisParamChecked( setAxisParamChecked(
@ -791,6 +811,26 @@ asynStatus sinqAxis::checkMovTimeoutWatchdog(bool moving) {
return asynSuccess; return asynSuccess;
} }
asynStatus sinqAxis::setOffsetMovTimeout(time_t offsetMovTimeout) {
pSinqA_->offsetMovTimeout = offsetMovTimeout;
return asynSuccess;
}
asynStatus sinqAxis::setScaleMovTimeout(time_t scaleMovTimeout) {
pSinqA_->scaleMovTimeout = scaleMovTimeout;
return asynSuccess;
}
bool sinqAxis::wasMoving() { return pSinqA_->wasMoving; }
void sinqAxis::setWasMoving(bool wasMoving) { pSinqA_->wasMoving = wasMoving; }
double sinqAxis::targetPosition() { return pSinqA_->targetPosition; }
void sinqAxis::setTargetPosition(double targetPosition) {
pSinqA_->targetPosition = targetPosition;
}
// ============================================================================= // =============================================================================
// IOC shell functions // IOC shell functions
extern "C" { extern "C" {

View File

@ -9,9 +9,11 @@ Stefan Mathis, November 2024
#ifndef sinqAxis_H #ifndef sinqAxis_H
#define sinqAxis_H #define sinqAxis_H
#include "asynMotorAxis.h" #include "asynMotorAxis.h"
#include <epicsTime.h> #include <memory>
#include <type_traits> #include <type_traits>
struct sinqAxisImpl;
class epicsShareClass sinqAxis : public asynMotorAxis { class epicsShareClass sinqAxis : public asynMotorAxis {
public: public:
/** /**
@ -22,6 +24,13 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
*/ */
sinqAxis(class sinqController *pC_, int axisNo); sinqAxis(class sinqController *pC_, int axisNo);
/**
* @brief Destroy the sinqAxis object
*
* This destructor is necessary in order to use the PIMPL idiom.
*/
~sinqAxis();
/** /**
* @brief Perform some standardized operations before and after the concrete * @brief Perform some standardized operations before and after the concrete
`doPoll` implementation. `doPoll` implementation.
@ -49,7 +58,7 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
the parameter library operation fails (in that case, returns the status of the parameter library operation fails (in that case, returns the status of
the failed operation. the failed operation.
*/ */
asynStatus poll(bool *moving); virtual asynStatus poll(bool *moving);
/** /**
* @brief Implementation of the "proper", device-specific poll method. This * @brief Implementation of the "proper", device-specific poll method. This
@ -79,8 +88,8 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
the parameter library operation fails (in that case, returns the failed the parameter library operation fails (in that case, returns the failed
operation status). operation status).
*/ */
asynStatus move(double position, int relative, double minVelocity, virtual asynStatus move(double position, int relative, double minVelocity,
double maxVelocity, double acceleration); double maxVelocity, double acceleration);
/** /**
* @brief Implementation of the "proper", device-specific move method. This * @brief Implementation of the "proper", device-specific move method. This
@ -143,8 +152,8 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
the parameter library operation fails (in that case, returns the failed the parameter library operation fails (in that case, returns the failed
operation status). operation status).
*/ */
asynStatus home(double minVelocity, double maxVelocity, double acceleration, virtual asynStatus home(double minVelocity, double maxVelocity,
int forwards); double acceleration, int forwards);
/** /**
* @brief Implementation of the "proper", device-specific home method. This * @brief Implementation of the "proper", device-specific home method. This
@ -170,7 +179,7 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
* *
* @return asynStatus * @return asynStatus
*/ */
asynStatus reset(); virtual asynStatus reset();
/** /**
* @brief Implementation of the "proper", device-specific `reset` method. * @brief Implementation of the "proper", device-specific `reset` method.
@ -240,7 +249,7 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
used to get the values for the timeout calculation failed, return that used to get the values for the timeout calculation failed, return that
status, otherwise return asynSuccess. status, otherwise return asynSuccess.
*/ */
asynStatus startMovTimeoutWatchdog(); virtual asynStatus startMovTimeoutWatchdog();
/** /**
* @brief Check if the watchdog timed out * @brief Check if the watchdog timed out
@ -274,7 +283,7 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
* @return asynStatus Return asynError, if the watchdog timed out, * @return asynStatus Return asynError, if the watchdog timed out,
and asynSuccess otherwise. and asynSuccess otherwise.
*/ */
asynStatus checkMovTimeoutWatchdog(bool moving); virtual asynStatus checkMovTimeoutWatchdog(bool moving);
/** /**
* @brief Enable / disable the watchdog. Also available in the IOC shell * @brief Enable / disable the watchdog. Also available in the IOC shell
@ -297,10 +306,7 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
* @param offsetMovTimeout Offset (in seconds) * @param offsetMovTimeout Offset (in seconds)
* @return asynStatus * @return asynStatus
*/ */
virtual asynStatus setOffsetMovTimeout(time_t offsetMovTimeout) { virtual asynStatus setOffsetMovTimeout(time_t offsetMovTimeout);
offsetMovTimeout_ = offsetMovTimeout;
return asynSuccess;
}
/** /**
* @brief Set the scaleMovTimeout. Also available in the IOC shell * @brief Set the scaleMovTimeout. Also available in the IOC shell
@ -311,10 +317,7 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
* @param scaleMovTimeout Scaling factor (in seconds) * @param scaleMovTimeout Scaling factor (in seconds)
* @return asynStatus * @return asynStatus
*/ */
virtual asynStatus setScaleMovTimeout(time_t scaleMovTimeout) { virtual asynStatus setScaleMovTimeout(time_t scaleMovTimeout);
scaleMovTimeout_ = scaleMovTimeout;
return asynSuccess;
}
/** /**
* @brief Return the axis number of this axis * @brief Return the axis number of this axis
@ -366,24 +369,38 @@ class epicsShareClass sinqAxis : public asynMotorAxis {
*/ */
sinqController *pController() { return pC_; }; sinqController *pController() { return pC_; };
protected: /**
// Internal variables used in the movement timeout watchdog * @brief Returns true, if the axis was moving in the last poll cycle, and
time_t expectedArrivalTime_; * false otherwise.
time_t offsetMovTimeout_; *
* @return true
* @return false
*/
bool wasMoving();
double scaleMovTimeout_; /**
bool watchdogMovActive_; * @brief Override the wasMoving flag (normally, it is automatically updated
// Store the motor target position for the movement time calculation * during each poll).
double targetPosition_; *
*/
void setWasMoving(bool wasMoving);
bool wasMoving_; /**
* @brief Read out the last received target position in engineering units.
*
* @return double
*/
double targetPosition();
/* /**
Store the time since the last poll * @brief Override the targetPosition value (normally, it is automatically
*/ * updated at every call of the move() method).
epicsTimeStamp lastPollTime_; *
*/
void setTargetPosition(double targetPosition);
private: private:
std::unique_ptr<sinqAxisImpl> pSinqA_;
sinqController *pC_; sinqController *pC_;
}; };

View File

@ -5,8 +5,12 @@
#include "asynOctetSyncIO.h" #include "asynOctetSyncIO.h"
#include "epicsExport.h" #include "epicsExport.h"
#include "iocsh.h" #include "iocsh.h"
#include "msgPrintControl.h"
#include "sinqAxis.h" #include "sinqAxis.h"
#include <deque>
#include <errlog.h> #include <errlog.h>
#include <initHooks.h>
#include <unordered_map>
#include <vector> #include <vector>
/* /*
@ -34,6 +38,65 @@ void sinqController::epicsInithookFunction(initHookState iState) {
} }
} }
struct sinqControllerImpl {
// Number of fast polls which still need to be performed before adaptive
// polling is active again.
int outstandingForcedFastPolls;
// Number of polls forced by wakeupPoller which are still
// Pointer to the port user which is specified by the char array
// `ipPortConfigName` in the constructor
asynUser *pasynOctetSyncIOipPort;
// Message print control
msgPrintControl msgPrintC;
// Internal variables used in the communication timeout frequency watchdog
time_t comTimeoutWindow; // Size of the time window
size_t maxNumberTimeouts; // Maximum acceptable number of events within the
// time window
// Deque holding the timestamps of the individual events
std::deque<time_t> timeoutEvents;
// Communicate a timeout to the user after it has happened this many times
// in a row
int maxSubsequentTimeouts;
bool maxSubsequentTimeoutsExceeded;
/*
These integers are indices to paramLib entries and are populated when the
parameters are created. See the documentation in db/sinqMotor.db.
*/
int motorMessageText;
int motorReset;
int motorEnable;
int motorEnableRBV;
int motorCanDisable;
int motorEnableMovWatchdog;
int motorCanSetSpeed;
int motorLimitsOffset;
int motorForceStop;
int motorConnected;
/*
These parameters are here to write values from the hardware to the EPICS
motor record. Using motorHighLimit_ / motorLowLimit_ does not work:
https://epics.anl.gov/tech-talk/2023/msg00576.php. Therefore, some
additional records are introduced which read from these parameters and write
into the motor record.
*/
int motorVeloFromDriver;
int motorVbasFromDriver;
int motorVmaxFromDriver;
int motorAcclFromDriver;
int motorHighLimitFromDriver;
int motorLowLimitFromDriver;
int adaptivePolling;
int encoderType;
};
#define NUM_SINQMOTOR_DRIVER_PARAMS 18
sinqController::sinqController(const char *portName, sinqController::sinqController(const char *portName,
const char *ipPortConfigName, int numAxes, const char *ipPortConfigName, int numAxes,
double movingPollPeriod, double idlePollPeriod, double movingPollPeriod, double idlePollPeriod,
@ -48,29 +111,22 @@ sinqController::sinqController(const char *portName,
0, // No additional interfaces beyond those in base class 0, // No additional interfaces beyond those in base class
0, // No additional callback interfaces beyond those in base class 0, // No additional callback interfaces beyond those in base class
ASYN_CANBLOCK | ASYN_MULTIDEVICE, ASYN_CANBLOCK | ASYN_MULTIDEVICE,
1, // autoconnect 1, // autoconnect
0, 0), // Default priority and stack size 0, 0) // Default priority and stack size
msgPrintControl_() { {
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
// Handle to the asynUser of the IP port asyn driver // The paramLib indices are populated with the calls to createParam
pasynOctetSyncIOipPort_ = nullptr; pSinqC_ = std::make_unique<sinqControllerImpl>(
(sinqControllerImpl){.outstandingForcedFastPolls = 0,
// Initial values for the average timeout mechanism, can be overwritten .pasynOctetSyncIOipPort = nullptr,
// later by a FFI function .msgPrintC = msgPrintControl(),
comTimeoutWindow_ = 3600; // seconds .comTimeoutWindow = 3600,
.maxNumberTimeouts = 60,
// Number of timeouts which may occur before an error is forwarded to the .timeoutEvents = {},
// user .maxSubsequentTimeouts = 10,
maxNumberTimeouts_ = 60; .maxSubsequentTimeoutsExceeded = false});
// Queue holding the timeout event timestamps
timeoutEvents_ = {};
// Inform the user after 10 timeouts in a row (default value)
maxSubsequentTimeouts_ = 10;
maxSubsequentTimeoutsExceeded_ = false;
// Store the poll period information. The poller itself will be started // Store the poll period information. The poller itself will be started
// later (after the IOC is running in epicsInithookFunction) // later (after the IOC is running in epicsInithookFunction)
@ -83,9 +139,9 @@ sinqController::sinqController(const char *portName,
We try to connect to the port via the port name provided by the constructor. We try to connect to the port via the port name provided by the constructor.
If this fails, the function is terminated via exit. If this fails, the function is terminated via exit.
*/ */
pasynOctetSyncIO->connect(ipPortConfigName, 0, &pasynOctetSyncIOipPort_, pasynOctetSyncIO->connect(ipPortConfigName, 0,
NULL); &pSinqC_->pasynOctetSyncIOipPort, NULL);
if (status != asynSuccess || pasynOctetSyncIOipPort_ == nullptr) { if (status != asynSuccess || pSinqC_->pasynOctetSyncIOipPort == nullptr) {
errlogPrintf("Controller \"%s\" => %s, line %d:\nFATAL ERROR (cannot " errlogPrintf("Controller \"%s\" => %s, line %d:\nFATAL ERROR (cannot "
"connect to MCU controller).\n" "connect to MCU controller).\n"
"Terminating IOC", "Terminating IOC",
@ -98,8 +154,8 @@ sinqController::sinqController(const char *portName,
// MOTOR_MESSAGE_TEXT corresponds to the PV definition inside sinqMotor.db. // 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 // This text is used to forward status messages to NICOS and in turn to the
// user. // user.
status = status = createParam("MOTOR_MESSAGE_TEXT", asynParamOctet,
createParam("MOTOR_MESSAGE_TEXT", asynParamOctet, &motorMessageText_); &pSinqC_->motorMessageText);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -109,7 +165,7 @@ sinqController::sinqController(const char *portName,
exit(-1); exit(-1);
} }
status = createParam("MOTOR_ENABLE", asynParamInt32, &motorEnable_); status = createParam("MOTOR_ENABLE", asynParamInt32, &pSinqC_->motorEnable);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -119,7 +175,7 @@ sinqController::sinqController(const char *portName,
exit(-1); exit(-1);
} }
status = createParam("MOTOR_RESET", asynParamInt32, &motorReset_); status = createParam("MOTOR_RESET", asynParamInt32, &pSinqC_->motorReset);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -129,7 +185,8 @@ sinqController::sinqController(const char *portName,
exit(-1); exit(-1);
} }
status = createParam("MOTOR_ENABLE_RBV", asynParamInt32, &motorEnableRBV_); status = createParam("MOTOR_ENABLE_RBV", asynParamInt32,
&pSinqC_->motorEnableRBV);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -139,8 +196,8 @@ sinqController::sinqController(const char *portName,
exit(-1); exit(-1);
} }
status = status = createParam("MOTOR_CAN_DISABLE", asynParamInt32,
createParam("MOTOR_CAN_DISABLE", asynParamInt32, &motorCanDisable_); &pSinqC_->motorCanDisable);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -150,8 +207,8 @@ sinqController::sinqController(const char *portName,
exit(-1); exit(-1);
} }
status = status = createParam("MOTOR_CAN_SET_SPEED", asynParamInt32,
createParam("MOTOR_CAN_SET_SPEED", asynParamInt32, &motorCanSetSpeed_); &pSinqC_->motorCanSetSpeed);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -162,7 +219,7 @@ sinqController::sinqController(const char *portName,
} }
status = createParam("MOTOR_LIMITS_OFFSET", asynParamFloat64, status = createParam("MOTOR_LIMITS_OFFSET", asynParamFloat64,
&motorLimitsOffset_); &pSinqC_->motorLimitsOffset);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -172,7 +229,8 @@ sinqController::sinqController(const char *portName,
exit(-1); exit(-1);
} }
status = createParam("MOTOR_CONNECTED", asynParamInt32, &motorConnected_); status = createParam("MOTOR_CONNECTED", asynParamInt32,
&pSinqC_->motorConnected);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -188,7 +246,7 @@ sinqController::sinqController(const char *portName,
the declaration of motorHighLimitFromDriver_. the declaration of motorHighLimitFromDriver_.
*/ */
status = createParam("MOTOR_HIGH_LIMIT_FROM_DRIVER", asynParamFloat64, status = createParam("MOTOR_HIGH_LIMIT_FROM_DRIVER", asynParamFloat64,
&motorHighLimitFromDriver_); &pSinqC_->motorHighLimitFromDriver);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -199,7 +257,7 @@ sinqController::sinqController(const char *portName,
} }
status = createParam("MOTOR_LOW_LIMIT_FROM_DRIVER", asynParamFloat64, status = createParam("MOTOR_LOW_LIMIT_FROM_DRIVER", asynParamFloat64,
&motorLowLimitFromDriver_); &pSinqC_->motorLowLimitFromDriver);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -210,7 +268,7 @@ sinqController::sinqController(const char *portName,
} }
status = createParam("MOTOR_ENABLE_MOV_WATCHDOG", asynParamInt32, status = createParam("MOTOR_ENABLE_MOV_WATCHDOG", asynParamInt32,
&motorEnableMovWatchdog_); &pSinqC_->motorEnableMovWatchdog);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -221,7 +279,7 @@ sinqController::sinqController(const char *portName,
} }
status = createParam("MOTOR_VELO_FROM_DRIVER", asynParamFloat64, status = createParam("MOTOR_VELO_FROM_DRIVER", asynParamFloat64,
&motorVeloFromDriver_); &pSinqC_->motorVeloFromDriver);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -232,7 +290,7 @@ sinqController::sinqController(const char *portName,
} }
status = createParam("MOTOR_VBAS_FROM_DRIVER", asynParamFloat64, status = createParam("MOTOR_VBAS_FROM_DRIVER", asynParamFloat64,
&motorVbasFromDriver_); &pSinqC_->motorVbasFromDriver);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -243,7 +301,7 @@ sinqController::sinqController(const char *portName,
} }
status = createParam("MOTOR_VMAX_FROM_DRIVER", asynParamFloat64, status = createParam("MOTOR_VMAX_FROM_DRIVER", asynParamFloat64,
&motorVmaxFromDriver_); &pSinqC_->motorVmaxFromDriver);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -254,7 +312,7 @@ sinqController::sinqController(const char *portName,
} }
status = createParam("MOTOR_ACCL_FROM_DRIVER", asynParamFloat64, status = createParam("MOTOR_ACCL_FROM_DRIVER", asynParamFloat64,
&motorAcclFromDriver_); &pSinqC_->motorAcclFromDriver);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -264,7 +322,8 @@ sinqController::sinqController(const char *portName,
exit(-1); exit(-1);
} }
status = createParam("ADAPTIVE_POLLING", asynParamInt32, &adaptivePolling_); status = createParam("ADAPTIVE_POLLING", asynParamInt32,
&pSinqC_->adaptivePolling);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -274,7 +333,7 @@ sinqController::sinqController(const char *portName,
exit(-1); exit(-1);
} }
status = createParam("ENCODER_TYPE", asynParamOctet, &encoderType_); status = createParam("ENCODER_TYPE", asynParamOctet, &pSinqC_->encoderType);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -284,7 +343,8 @@ sinqController::sinqController(const char *portName,
exit(-1); exit(-1);
} }
status = createParam("MOTOR_FORCE_STOP", asynParamInt32, &motorForceStop_); status = createParam("MOTOR_FORCE_STOP", asynParamInt32,
&pSinqC_->motorForceStop);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a "
@ -351,11 +411,11 @@ asynStatus sinqController::writeInt32(asynUser *pasynUser, epicsInt32 value) {
} }
// Handle custom PVs // Handle custom PVs
if (function == motorEnable_) { if (function == motorEnable()) {
return axis->enable(value != 0); return axis->enable(value != 0);
} else if (function == motorReset_) { } else if (function == motorReset()) {
return axis->reset(); return axis->reset();
} else if (function == motorForceStop_) { } else if (function == motorForceStop()) {
return axis->stop(0.0); return axis->stop(0.0);
} else { } else {
return asynMotorController::writeInt32(pasynUser, value); return asynMotorController::writeInt32(pasynUser, value);
@ -373,10 +433,10 @@ asynStatus sinqController::readInt32(asynUser *pasynUser, epicsInt32 *value) {
return asynError; return asynError;
} }
if (pasynUser->reason == motorEnableRBV_) { if (pasynUser->reason == motorEnableRBV()) {
getAxisParamChecked(axis, motorEnableRBV, value); getAxisParamChecked(axis, motorEnableRBV, value);
return asynSuccess; return asynSuccess;
} else if (pasynUser->reason == motorCanDisable_) { } else if (pasynUser->reason == motorCanDisable()) {
getAxisParamChecked(axis, motorCanDisable, value); getAxisParamChecked(axis, motorCanDisable, value);
return asynSuccess; return asynSuccess;
} else { } else {
@ -389,7 +449,7 @@ asynStatus sinqController::couldNotParseResponse(const char *command,
int axisNo, int axisNo,
const char *functionName, const char *functionName,
int line) { int line) {
asynPrint(pasynOctetSyncIOipPort_, ASYN_TRACE_ERROR, asynPrint(pasynOctetSyncIOipPort(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nCould not interpret " "Controller \"%s\", axis %d => %s, line %d:\nCould not interpret "
"response \"%s\" for command \"%s\".\n", "response \"%s\" for command \"%s\".\n",
portName, axisNo, functionName, line, response, command); portName, axisNo, functionName, line, response, command);
@ -418,7 +478,7 @@ asynStatus sinqController::paramLibAccessFailed(asynStatus status,
int line) { int line) {
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(pasynOctetSyncIOipPort_, ASYN_TRACE_ERROR, asynPrint(pasynOctetSyncIOipPort(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\n Accessing the " "Controller \"%s\", axis %d => %s, line %d:\n Accessing the "
"parameter library failed for parameter %s with error %s.\n", "parameter library failed for parameter %s with error %s.\n",
portName, axisNo, functionName, line, parameter, portName, axisNo, functionName, line, parameter,
@ -440,40 +500,42 @@ asynStatus sinqController::checkComTimeoutWatchdog(int axisNo,
asynStatus paramLibStatus = asynSuccess; asynStatus paramLibStatus = asynSuccess;
// Add a new timeout event to the queue // Add a new timeout event to the queue
timeoutEvents_.push_back(time(NULL)); pSinqC_->timeoutEvents.push_back(time(NULL));
// Remove every event which is older than the time window from the deque // Remove every event which is older than the time window from the deque
while (1) { while (1) {
if (timeoutEvents_.empty()) { if (pSinqC_->timeoutEvents.empty()) {
break; break;
} }
if (timeoutEvents_[0] + comTimeoutWindow_ <= time(NULL)) { if (pSinqC_->timeoutEvents[0] + pSinqC_->comTimeoutWindow <=
timeoutEvents_.pop_front(); time(NULL)) {
pSinqC_->timeoutEvents.pop_front();
} else { } else {
break; break;
} }
} }
// Check if the maximum allowed number of events has been exceeded // Check if the maximum allowed number of events has been exceeded
bool wantToPrint = timeoutEvents_.size() > maxNumberTimeouts_; bool wantToPrint =
pSinqC_->timeoutEvents.size() > pSinqC_->maxNumberTimeouts;
if (msgPrintControl_.shouldBePrinted(portName, axisNo, __PRETTY_FUNCTION__, if (pSinqC_->msgPrintC.shouldBePrinted(portName, axisNo,
__LINE__, wantToPrint, __PRETTY_FUNCTION__, __LINE__,
pasynUserSelf)) { wantToPrint, pasynUserSelf)) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nMore than %ld " "Controller \"%s\", axis %d => %s, line %d:\nMore than %ld "
"communication timeouts in %ld " "communication timeouts in %ld "
"seconds.%s\n", "seconds.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
maxNumberTimeouts_, comTimeoutWindow_, pSinqC_->maxNumberTimeouts, pSinqC_->comTimeoutWindow,
msgPrintControl_.getSuffix()); pSinqC_->msgPrintC.getSuffix());
} }
if (wantToPrint) { if (wantToPrint) {
snprintf(motorMessage, motorMessageSize, snprintf(motorMessage, motorMessageSize,
"More than %ld communication timeouts in %ld seconds. Please " "More than %ld communication timeouts in %ld seconds. Please "
"call the support.", "call the support.",
maxNumberTimeouts_, comTimeoutWindow_); pSinqC_->maxNumberTimeouts, pSinqC_->comTimeoutWindow);
paramLibStatus = setIntegerParam(motorStatusCommsError_, 1); paramLibStatus = setIntegerParam(motorStatusCommsError_, 1);
if (paramLibStatus != asynSuccess) { if (paramLibStatus != asynSuccess) {
@ -505,8 +567,8 @@ asynStatus sinqController::checkMaxSubsequentTimeouts(int timeoutNo, int axisNo,
size_t motorMessageSize) { size_t motorMessageSize) {
asynStatus paramLibStatus = asynSuccess; asynStatus paramLibStatus = asynSuccess;
if (timeoutNo >= maxSubsequentTimeouts_) { if (timeoutNo >= pSinqC_->maxSubsequentTimeouts) {
if (!maxSubsequentTimeoutsExceeded_) { if (!pSinqC_->maxSubsequentTimeoutsExceeded) {
snprintf(motorMessage, motorMessageSize, snprintf(motorMessage, motorMessageSize,
"Communication timeout between IOC and motor controller. " "Communication timeout between IOC and motor controller. "
"Trying to reconnect ..."); "Trying to reconnect ...");
@ -516,7 +578,7 @@ asynStatus sinqController::checkMaxSubsequentTimeouts(int timeoutNo, int axisNo,
"subsequent communication timeouts. Check whether the " "subsequent communication timeouts. Check whether the "
"controller is still running and connected to the network.\n", "controller is still running and connected to the network.\n",
this->portName, axisNo, __PRETTY_FUNCTION__, __LINE__, this->portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
maxSubsequentTimeouts_); pSinqC_->maxSubsequentTimeouts);
paramLibStatus = setIntegerParam(motorStatusCommsError_, 1); paramLibStatus = setIntegerParam(motorStatusCommsError_, 1);
if (paramLibStatus != asynSuccess) { if (paramLibStatus != asynSuccess) {
@ -524,12 +586,12 @@ asynStatus sinqController::checkMaxSubsequentTimeouts(int timeoutNo, int axisNo,
"motorStatusCommsError_", axisNo, "motorStatusCommsError_", axisNo,
__PRETTY_FUNCTION__, __LINE__); __PRETTY_FUNCTION__, __LINE__);
} }
maxSubsequentTimeoutsExceeded_ = true; pSinqC_->maxSubsequentTimeoutsExceeded = true;
} }
return asynError; return asynError;
} else { } else {
maxSubsequentTimeoutsExceeded_ = false; pSinqC_->maxSubsequentTimeoutsExceeded = false;
motorMessage[0] = '\0'; motorMessage[0] = '\0';
return asynSuccess; return asynSuccess;
} }
@ -551,8 +613,8 @@ asynStatus sinqController::checkMaxSubsequentTimeouts(int timeoutNo,
asynStatus sinqController::poll() { asynStatus sinqController::poll() {
// Decrement the number of outstanding forced fast polls, if they are not // Decrement the number of outstanding forced fast polls, if they are not
// zero // zero
if (outstandingForcedFastPolls_ > 0) { if (pSinqC_->outstandingForcedFastPolls > 0) {
outstandingForcedFastPolls_--; pSinqC_->outstandingForcedFastPolls--;
} }
return asynMotorController::poll(); return asynMotorController::poll();
} }
@ -560,10 +622,86 @@ asynStatus sinqController::poll() {
asynStatus sinqController::wakeupPoller() { asynStatus sinqController::wakeupPoller() {
// + 1 since outstandingForcedFastPolls_ is reduced once at the start of // + 1 since outstandingForcedFastPolls_ is reduced once at the start of
// a poll cycle // a poll cycle
outstandingForcedFastPolls_ = forcedFastPolls_ + 1; pSinqC_->outstandingForcedFastPolls = forcedFastPolls_ + 1;
return asynMotorController::wakeupPoller(); return asynMotorController::wakeupPoller();
} }
asynStatus sinqController::setMaxSubsequentTimeouts(int maxSubsequentTimeouts) {
pSinqC_->maxSubsequentTimeouts = maxSubsequentTimeouts;
return asynSuccess;
}
bool sinqController::maxSubsequentTimeoutsExceeded() {
return pSinqC_->maxSubsequentTimeoutsExceeded;
}
/**
* @brief Get a reference to the map used to control the maximum number of
* message repetitions. See the documentation of `printRepetitionWatchdog`
* in msgPrintControl.h for details.
*/
msgPrintControl &sinqController::getMsgPrintControl() {
return pSinqC_->msgPrintC;
}
/**
* @brief Read the number of outstanding forced fast polls currently
* specified
*
*/
int sinqController::outstandingForcedFastPolls() {
return pSinqC_->outstandingForcedFastPolls;
}
/**
* @brief Return a pointer to the low-level octet (string) IP Port
*
* @return asynUser*
*/
asynUser *sinqController::pasynOctetSyncIOipPort() {
return pSinqC_->pasynOctetSyncIOipPort;
}
asynStatus sinqController::setThresholdComTimeout(time_t comTimeoutWindow,
size_t maxNumberTimeouts) {
pSinqC_->comTimeoutWindow = comTimeoutWindow;
pSinqC_->maxNumberTimeouts = maxNumberTimeouts;
return asynSuccess;
}
int sinqController::motorMessageText() { return pSinqC_->motorMessageText; }
int sinqController::motorReset() { return pSinqC_->motorReset; }
int sinqController::motorEnable() { return pSinqC_->motorEnable; }
int sinqController::motorEnableRBV() { return pSinqC_->motorEnableRBV; }
int sinqController::motorCanDisable() { return pSinqC_->motorCanDisable; }
int sinqController::motorEnableMovWatchdog() {
return pSinqC_->motorEnableMovWatchdog;
}
int sinqController::motorCanSetSpeed() { return pSinqC_->motorCanSetSpeed; }
int sinqController::motorLimitsOffset() { return pSinqC_->motorLimitsOffset; }
int sinqController::motorForceStop() { return pSinqC_->motorForceStop; }
int sinqController::motorConnected() { return pSinqC_->motorConnected; }
int sinqController::motorVeloFromDriver() {
return pSinqC_->motorVeloFromDriver;
}
int sinqController::motorVbasFromDriver() {
return pSinqC_->motorVbasFromDriver;
}
int sinqController::motorVmaxFromDriver() {
return pSinqC_->motorVmaxFromDriver;
}
int sinqController::motorAcclFromDriver() {
return pSinqC_->motorAcclFromDriver;
}
int sinqController::motorHighLimitFromDriver() {
return pSinqC_->motorHighLimitFromDriver;
}
int sinqController::motorLowLimitFromDriver() {
return pSinqC_->motorLowLimitFromDriver;
}
int sinqController::adaptivePolling() { return pSinqC_->adaptivePolling; }
int sinqController::encoderType() { return pSinqC_->encoderType; }
// Static pointers (valid for the entire lifetime of the IOC). The number behind // 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 // the strings gives the integer number of each variant (see also method
// stringifyAsynStatus) // stringifyAsynStatus)

View File

@ -11,9 +11,8 @@ Stefan Mathis, November 2024
#define sinqController_H #define sinqController_H
#include "asynMotorController.h" #include "asynMotorController.h"
#include "msgPrintControl.h" #include "msgPrintControl.h"
#include <deque>
#include <initHooks.h> #include <initHooks.h>
#include <unordered_map> #include <memory>
#define motorMessageIsFromDriverString "MOTOR_MESSAGE_DRIVER" #define motorMessageIsFromDriverString "MOTOR_MESSAGE_DRIVER"
#define motorMessageTextString "MOTOR_MESSAGE_TEXT" #define motorMessageTextString "MOTOR_MESSAGE_TEXT"
@ -21,6 +20,8 @@ Stefan Mathis, November 2024
#define AbsoluteEncoder "absolute" #define AbsoluteEncoder "absolute"
#define NoEncoder "none" #define NoEncoder "none"
struct sinqControllerImpl;
class epicsShareClass sinqController : public asynMotorController { class epicsShareClass sinqController : public asynMotorController {
public: public:
/** /**
@ -175,11 +176,7 @@ class epicsShareClass sinqController : public asynMotorController {
* @return asynStatus * @return asynStatus
*/ */
virtual asynStatus setThresholdComTimeout(time_t comTimeoutWindow, virtual asynStatus setThresholdComTimeout(time_t comTimeoutWindow,
size_t maxNumberTimeouts) { size_t maxNumberTimeouts);
comTimeoutWindow_ = comTimeoutWindow;
maxNumberTimeouts_ = maxNumberTimeouts;
return asynSuccess;
}
/** /**
* @brief Inform the user, if the number of timeouts exceeds the threshold * @brief Inform the user, if the number of timeouts exceeds the threshold
@ -211,17 +208,23 @@ class epicsShareClass sinqController : public asynMotorController {
* @param maxSubsequentTimeouts * @param maxSubsequentTimeouts
* @return asynStatus * @return asynStatus
*/ */
asynStatus setMaxSubsequentTimeouts(int maxSubsequentTimeouts) { asynStatus setMaxSubsequentTimeouts(int maxSubsequentTimeouts);
maxSubsequentTimeouts_ = maxSubsequentTimeouts;
return asynSuccess; /**
} * @brief If true, the maximum number of subsequent communication timeouts
* set in `setMaxSubsequentTimeouts` has been exceeded
*
* @return true
* @return false
*/
bool maxSubsequentTimeoutsExceeded();
/** /**
* @brief Get a reference to the map used to control the maximum number of * @brief Get a reference to the map used to control the maximum number of
* message repetitions. See the documentation of `printRepetitionWatchdog` * message repetitions. See the documentation of `printRepetitionWatchdog`
* in msgPrintControl.h for details. * in msgPrintControl.h for details.
*/ */
msgPrintControl &getMsgPrintControl() { return msgPrintControl_; } msgPrintControl &getMsgPrintControl();
/** /**
* @brief Get the axis object * @brief Get the axis object
@ -293,25 +296,26 @@ class epicsShareClass sinqController : public asynMotorController {
int motorRecDirection() { return motorRecDirection_; } int motorRecDirection() { return motorRecDirection_; }
int motorRecOffset() { return motorRecOffset_; } int motorRecOffset() { return motorRecOffset_; }
// Accessors for additional PVs defined in sinqController // Accessors for additional PVs defined in sinqController (which are hidden
int motorMessageText() { return motorMessageText_; } // in pSinqC_)
int motorReset() { return motorReset_; } int motorMessageText();
int motorEnable() { return motorEnable_; } int motorReset();
int motorEnableRBV() { return motorEnableRBV_; } int motorEnable();
int motorCanDisable() { return motorCanDisable_; } int motorEnableRBV();
int motorEnableMovWatchdog() { return motorEnableMovWatchdog_; } int motorCanDisable();
int motorCanSetSpeed() { return motorCanSetSpeed_; } int motorEnableMovWatchdog();
int motorLimitsOffset() { return motorLimitsOffset_; } int motorCanSetSpeed();
int motorForceStop() { return motorForceStop_; } int motorLimitsOffset();
int motorConnected() { return motorConnected_; } int motorForceStop();
int motorVeloFromDriver() { return motorVeloFromDriver_; } int motorConnected();
int motorVbasFromDriver() { return motorVbasFromDriver_; } int motorVeloFromDriver();
int motorVmaxFromDriver() { return motorVmaxFromDriver_; } int motorVbasFromDriver();
int motorAcclFromDriver() { return motorAcclFromDriver_; } int motorVmaxFromDriver();
int motorHighLimitFromDriver() { return motorHighLimitFromDriver_; } int motorAcclFromDriver();
int motorLowLimitFromDriver() { return motorLowLimitFromDriver_; } int motorHighLimitFromDriver();
int adaptivePolling() { return adaptivePolling_; } int motorLowLimitFromDriver();
int encoderType() { return encoderType_; } int adaptivePolling();
int encoderType();
// Additional members // Additional members
int numAxes() { return numAxes_; } int numAxes() { return numAxes_; }
@ -330,7 +334,7 @@ class epicsShareClass sinqController : public asynMotorController {
* *
* @return asynUser* * @return asynUser*
*/ */
asynUser *pasynOctetSyncIOipPort() { return pasynOctetSyncIOipPort_; } asynUser *pasynOctetSyncIOipPort();
/** /**
* @brief Overloaded version of `asynController::poll` which decreases * @brief Overloaded version of `asynController::poll` which decreases
@ -374,7 +378,7 @@ class epicsShareClass sinqController : public asynMotorController {
* specified * specified
* *
*/ */
int outstandingForcedFastPolls() { return outstandingForcedFastPolls_; } int outstandingForcedFastPolls();
// Maximum error message buffer size. This is an empirical value which must // Maximum error message buffer size. This is an empirical value which must
// be large enough to avoid overflows for all commands to the device / // be large enough to avoid overflows for all commands to the device /
@ -383,67 +387,9 @@ class epicsShareClass sinqController : public asynMotorController {
// ========================================================================= // =========================================================================
protected:
// Number of fast polls which still need to be performed before adaptive
// polling is active again.
int outstandingForcedFastPolls_;
// Number of polls forced by wakeupPoller which are still
// Pointer to the port user which is specified by the char array
// `ipPortConfigName` in the constructor
asynUser *pasynOctetSyncIOipPort_;
double movingPollPeriod_;
double idlePollPeriod_;
msgPrintControl msgPrintControl_;
// Internal variables used in the communication timeout frequency watchdog
time_t comTimeoutWindow_; // Size of the time window
size_t maxNumberTimeouts_; // Maximum acceptable number of events within the
// time window
std::deque<time_t>
timeoutEvents_; // Deque holding the timestamps of the individual events
// Communicate a timeout to the user after it has happened this many times
// in a row
int maxSubsequentTimeouts_;
bool maxSubsequentTimeoutsExceeded_;
/*
See the documentation in db/sinqMotor.db for the following integers
*/
#define FIRST_SINQMOTOR_PARAM motorMessageText_
int motorMessageText_;
int motorReset_;
int motorEnable_;
int motorEnableRBV_;
int motorCanDisable_;
int motorEnableMovWatchdog_;
int motorCanSetSpeed_;
int motorLimitsOffset_;
int motorForceStop_;
int motorConnected_;
/*
These parameters are here to write values from the hardware to the EPICS
motor record. Using motorHighLimit_ / motorLowLimit_ does not work:
https://epics.anl.gov/tech-talk/2023/msg00576.php. Therefore, some
additional records are introduced which read from these parameters and write
into the motor record.
*/
int motorVeloFromDriver_;
int motorVbasFromDriver_;
int motorVmaxFromDriver_;
int motorAcclFromDriver_;
int motorHighLimitFromDriver_;
int motorLowLimitFromDriver_;
int adaptivePolling_;
int encoderType_;
#define LAST_SINQMOTOR_PARAM encoderType_
private: private:
std::unique_ptr<sinqControllerImpl> pSinqC_;
static void epicsInithookFunction(initHookState iState); static void epicsInithookFunction(initHookState iState);
}; };
#define NUM_SINQMOTOR_DRIVER_PARAMS \
(&LAST_SINQMOTOR_PARAM - &FIRST_SINQMOTOR_PARAM + 1)
#endif #endif