From 83aa437b6b3de1e6e83fe39faae89981416eacc6 Mon Sep 17 00:00:00 2001 From: smathis Date: Thu, 22 May 2025 13:12:09 +0200 Subject: [PATCH] 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. --- src/sinqAxis.cpp | 85 +++++++++---- src/sinqAxis.h | 32 +---- src/sinqController.cpp | 279 ++++++++++++++++++++++++++++++----------- src/sinqController.h | 121 +++++------------- 4 files changed, 299 insertions(+), 218 deletions(-) diff --git a/src/sinqAxis.cpp b/src/sinqAxis.cpp index ca81331..e61e768 100644 --- a/src/sinqAxis.cpp +++ b/src/sinqAxis.cpp @@ -3,11 +3,31 @@ #include "sinqAxis.h" #include "epicsExport.h" #include "iocsh.h" +#include "msgPrintControl.h" #include "sinqController.h" +#include #include #include #include +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. template asynStatus setAxisParam(sinqAxis *axis, const char *indexName, @@ -184,13 +204,17 @@ sinqAxis::sinqAxis(class sinqController *pC, int axisNo) : asynMotorAxis((asynMotorController *)pC, axisNo), pC_(pC) { asynStatus status = asynSuccess; - watchdogMovActive_ = false; - scaleMovTimeout_ = 2.0; - offsetMovTimeout_ = 30; - targetPosition_ = 0.0; - wasMoving_ = false; + epicsTimeStamp lastPollTime; + epicsTimeGetCurrent(&lastPollTime); - epicsTimeGetCurrent(&lastPollTime_); + pSinqA_ = std::make_unique( + (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 @@ -346,10 +370,10 @@ asynStatus sinqAxis::poll(bool *moving) { */ if (adaptivePolling != 0 && pC_->outstandingForcedFastPolls() == 0) { // Motor wasn't moving during the last poll - if (!wasMoving_) { + if (!pSinqA_->wasMoving) { // Add the idle poll period - epicsTimeStamp earliestTimeNextPoll = lastPollTime_; + epicsTimeStamp earliestTimeNextPoll = pSinqA_->lastPollTime; epicsTimeAddSeconds(&earliestTimeNextPoll, pC_->idlePollPeriod()); if (epicsTimeLessThanEqual(&earliestTimeNextPoll, &ts) == 0) { @@ -360,7 +384,7 @@ asynStatus sinqAxis::poll(bool *moving) { } // Update the start time of the last poll - lastPollTime_ = ts; + pSinqA_->lastPollTime = ts; /* If the "motorMessageText" record currently contains an error message, it @@ -411,7 +435,7 @@ asynStatus sinqAxis::poll(bool *moving) { assumed that the homing procedure is done. */ getAxisParamChecked(this, motorStatusHome, &homing); - if (homing == 1 && !(*moving) && wasMoving_) { + if (homing == 1 && !(*moving) && pSinqA_->wasMoving) { setAxisParamChecked(this, motorStatusHome, false); setAxisParamChecked(this, motorStatusHomed, true); setAxisParamChecked(this, motorStatusAtHome, true); @@ -419,7 +443,7 @@ asynStatus sinqAxis::poll(bool *moving) { // Update the wasMoving status if (pC_->outstandingForcedFastPolls() == 0) { - wasMoving_ = *moving; + pSinqA_->wasMoving = *moving; } // Check and update the watchdog @@ -467,7 +491,7 @@ asynStatus sinqAxis::move(double position, int relative, double minVelocity, // Store the target position internally getAxisParamChecked(this, motorRecResolution, &motorRecRes); - targetPosition_ = position * motorRecRes; + pSinqA_->targetPosition = position * motorRecRes; status = doMove(position, relative, minVelocity, maxVelocity, acceleration); if (status != asynSuccess) { @@ -485,7 +509,7 @@ asynStatus sinqAxis::move(double position, int relative, double minVelocity, setAxisParamChecked(this, motorStatusAtHome, false); // Needed for adaptive polling - wasMoving_ = true; + pSinqA_->wasMoving = true; return pC_->callParamCallbacks(); } @@ -507,7 +531,7 @@ asynStatus sinqAxis::home(double minVelocity, double maxVelocity, setAxisParamChecked(this, motorStatusHome, true); setAxisParamChecked(this, motorStatusHomed, false); setAxisParamChecked(this, motorStatusAtHome, false); - wasMoving_ = true; + pSinqA_->wasMoving = true; status = assertConnected(); if (status != asynSuccess) { @@ -681,7 +705,7 @@ asynStatus sinqAxis::startMovTimeoutWatchdog() { time_t timeAccel = 0; // Activate the watchdog - watchdogMovActive_ = true; + pSinqA_->watchdogMovActive = true; /* NOTE: This function must not be called in the constructor (e.g. in order @@ -717,8 +741,9 @@ asynStatus sinqAxis::startMovTimeoutWatchdog() { motorVelocity = motorVelocityRec * motorRecRes; if (pl_status == asynSuccess) { - timeContSpeed = std::ceil( - std::fabs(targetPosition_ - motorPos) / motorVelocity); + timeContSpeed = + std::ceil(std::fabs(pSinqA_->targetPosition - motorPos) / + motorVelocity); } } @@ -734,11 +759,11 @@ asynStatus sinqAxis::startMovTimeoutWatchdog() { } // Calculate the expected arrival time - expectedArrivalTime_ = - time(NULL) + offsetMovTimeout_ + - scaleMovTimeout_ * (timeContSpeed + 2 * timeAccel); + pSinqA_->expectedArrivalTime = + time(NULL) + pSinqA_->offsetMovTimeout + + pSinqA_->scaleMovTimeout * (timeContSpeed + 2 * timeAccel); } else { - watchdogMovActive_ = false; + pSinqA_->watchdogMovActive = false; } return asynSuccess; } @@ -749,8 +774,8 @@ asynStatus sinqAxis::checkMovTimeoutWatchdog(bool moving) { getAxisParamChecked(this, motorEnableMovWatchdog, &enableMovWatchdog); // Not moving or watchdog not active / enabled - if (enableMovWatchdog == 0 || !moving || !watchdogMovActive_) { - watchdogMovActive_ = false; + if (enableMovWatchdog == 0 || !moving || !pSinqA_->watchdogMovActive) { + pSinqA_->watchdogMovActive = false; return asynSuccess; } @@ -760,7 +785,7 @@ asynStatus sinqAxis::checkMovTimeoutWatchdog(bool moving) { __PRETTY_FUNCTION__, __LINE__); // Check if the expected time of arrival has been exceeded. - if (expectedArrivalTime_ < time(NULL)) { + if (pSinqA_->expectedArrivalTime < time(NULL)) { // Check the watchdog if (pC_->getMsgPrintControl().shouldBePrinted(key, true, pC_->pasynUser())) { @@ -768,7 +793,7 @@ asynStatus sinqAxis::checkMovTimeoutWatchdog(bool moving) { "Controller \"%s\", axis %d => %s, line %d:\nExceeded " "expected arrival time %ld (current time is %ld).\n", pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, - expectedArrivalTime_, time(NULL)); + pSinqA_->expectedArrivalTime, time(NULL)); } setAxisParamChecked( @@ -783,6 +808,16 @@ asynStatus sinqAxis::checkMovTimeoutWatchdog(bool moving) { return asynSuccess; } +asynStatus sinqAxis::setOffsetMovTimeout(time_t offsetMovTimeout) { + pSinqA_->offsetMovTimeout = offsetMovTimeout; + return asynSuccess; +} + +asynStatus sinqAxis::setScaleMovTimeout(time_t scaleMovTimeout) { + pSinqA_->scaleMovTimeout = scaleMovTimeout; + return asynSuccess; +} + // ============================================================================= // IOC shell functions extern "C" { diff --git a/src/sinqAxis.h b/src/sinqAxis.h index a444ade..c9284f9 100644 --- a/src/sinqAxis.h +++ b/src/sinqAxis.h @@ -9,9 +9,11 @@ Stefan Mathis, November 2024 #ifndef sinqAxis_H #define sinqAxis_H #include "asynMotorAxis.h" -#include +#include #include +struct sinqAxisImpl; + class epicsShareClass sinqAxis : public asynMotorAxis { public: /** @@ -297,10 +299,7 @@ class epicsShareClass sinqAxis : public asynMotorAxis { * @param offsetMovTimeout Offset (in seconds) * @return asynStatus */ - virtual asynStatus setOffsetMovTimeout(time_t offsetMovTimeout) { - offsetMovTimeout_ = offsetMovTimeout; - return asynSuccess; - } + virtual asynStatus setOffsetMovTimeout(time_t offsetMovTimeout); /** * @brief Set the scaleMovTimeout. Also available in the IOC shell @@ -311,10 +310,7 @@ class epicsShareClass sinqAxis : public asynMotorAxis { * @param scaleMovTimeout Scaling factor (in seconds) * @return asynStatus */ - virtual asynStatus setScaleMovTimeout(time_t scaleMovTimeout) { - scaleMovTimeout_ = scaleMovTimeout; - return asynSuccess; - } + virtual asynStatus setScaleMovTimeout(time_t scaleMovTimeout); /** * @brief Return the axis number of this axis @@ -366,24 +362,8 @@ class epicsShareClass sinqAxis : public asynMotorAxis { */ sinqController *pController() { return pC_; }; - protected: - // 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_; - private: + std::unique_ptr pSinqA_; sinqController *pC_; }; diff --git a/src/sinqController.cpp b/src/sinqController.cpp index 296ed7d..a273bcd 100644 --- a/src/sinqController.cpp +++ b/src/sinqController.cpp @@ -5,8 +5,12 @@ #include "asynOctetSyncIO.h" #include "epicsExport.h" #include "iocsh.h" +#include "msgPrintControl.h" #include "sinqAxis.h" +#include #include +#include +#include #include /* @@ -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 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, const char *ipPortConfigName, int numAxes, double movingPollPeriod, double idlePollPeriod, @@ -48,34 +111,22 @@ sinqController::sinqController(const char *portName, 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 - msgPrintControl_() { + 1, // autoconnect + 0, 0) // Default priority and stack size +{ asynStatus status = asynSuccess; - // Handle to the asynUser of the IP port asyn driver - pasynOctetSyncIOipPort_ = nullptr; - - // Initial values for the average timeout mechanism, can be overwritten - // later by a FFI function - comTimeoutWindow_ = 3600; // seconds - - // Number of timeouts which may occur before an error is forwarded to the - // user - maxNumberTimeouts_ = 60; - - // 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 - // later (after the IOC is running in epicsInithookFunction) - movingPollPeriod_ = movingPollPeriod; - idlePollPeriod_ = idlePollPeriod; + // The paramLib indices are populated with the calls to createParam + pSinqC_ = std::make_unique( + (sinqControllerImpl){.outstandingForcedFastPolls = 0, + .pasynOctetSyncIOipPort = nullptr, + .msgPrintC = msgPrintControl(), + .comTimeoutWindow = 3600, + .maxNumberTimeouts = 60, + .timeoutEvents = {}, + .maxSubsequentTimeouts = 10, + .maxSubsequentTimeoutsExceeded = false}); // =========================================================================; @@ -83,9 +134,9 @@ sinqController::sinqController(const char *portName, We try to connect to the port via the port name provided by the constructor. If this fails, the function is terminated via exit. */ - pasynOctetSyncIO->connect(ipPortConfigName, 0, &pasynOctetSyncIOipPort_, - NULL); - if (status != asynSuccess || pasynOctetSyncIOipPort_ == nullptr) { + pasynOctetSyncIO->connect(ipPortConfigName, 0, + &pSinqC_->pasynOctetSyncIOipPort, NULL); + if (status != asynSuccess || pSinqC_->pasynOctetSyncIOipPort == nullptr) { errlogPrintf("Controller \"%s\" => %s, line %d:\nFATAL ERROR (cannot " "connect to MCU controller).\n" "Terminating IOC", @@ -98,8 +149,8 @@ sinqController::sinqController(const char *portName, // 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_); + status = createParam("MOTOR_MESSAGE_TEXT", asynParamOctet, + &pSinqC_->motorMessageText); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -109,7 +160,7 @@ sinqController::sinqController(const char *portName, exit(-1); } - status = createParam("MOTOR_ENABLE", asynParamInt32, &motorEnable_); + status = createParam("MOTOR_ENABLE", asynParamInt32, &pSinqC_->motorEnable); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -119,7 +170,7 @@ sinqController::sinqController(const char *portName, exit(-1); } - status = createParam("MOTOR_RESET", asynParamInt32, &motorReset_); + status = createParam("MOTOR_RESET", asynParamInt32, &pSinqC_->motorReset); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -129,7 +180,8 @@ sinqController::sinqController(const char *portName, exit(-1); } - status = createParam("MOTOR_ENABLE_RBV", asynParamInt32, &motorEnableRBV_); + status = createParam("MOTOR_ENABLE_RBV", asynParamInt32, + &pSinqC_->motorEnableRBV); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -139,8 +191,8 @@ sinqController::sinqController(const char *portName, exit(-1); } - status = - createParam("MOTOR_CAN_DISABLE", asynParamInt32, &motorCanDisable_); + status = createParam("MOTOR_CAN_DISABLE", asynParamInt32, + &pSinqC_->motorCanDisable); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -150,8 +202,8 @@ sinqController::sinqController(const char *portName, exit(-1); } - status = - createParam("MOTOR_CAN_SET_SPEED", asynParamInt32, &motorCanSetSpeed_); + status = createParam("MOTOR_CAN_SET_SPEED", asynParamInt32, + &pSinqC_->motorCanSetSpeed); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -162,7 +214,7 @@ sinqController::sinqController(const char *portName, } status = createParam("MOTOR_LIMITS_OFFSET", asynParamFloat64, - &motorLimitsOffset_); + &pSinqC_->motorLimitsOffset); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -172,7 +224,8 @@ sinqController::sinqController(const char *portName, exit(-1); } - status = createParam("MOTOR_CONNECTED", asynParamInt32, &motorConnected_); + status = createParam("MOTOR_CONNECTED", asynParamInt32, + &pSinqC_->motorConnected); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -188,7 +241,7 @@ sinqController::sinqController(const char *portName, the declaration of motorHighLimitFromDriver_. */ status = createParam("MOTOR_HIGH_LIMIT_FROM_DRIVER", asynParamFloat64, - &motorHighLimitFromDriver_); + &pSinqC_->motorHighLimitFromDriver); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -199,7 +252,7 @@ sinqController::sinqController(const char *portName, } status = createParam("MOTOR_LOW_LIMIT_FROM_DRIVER", asynParamFloat64, - &motorLowLimitFromDriver_); + &pSinqC_->motorLowLimitFromDriver); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -210,7 +263,7 @@ sinqController::sinqController(const char *portName, } status = createParam("MOTOR_ENABLE_MOV_WATCHDOG", asynParamInt32, - &motorEnableMovWatchdog_); + &pSinqC_->motorEnableMovWatchdog); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -221,7 +274,7 @@ sinqController::sinqController(const char *portName, } status = createParam("MOTOR_VELO_FROM_DRIVER", asynParamFloat64, - &motorVeloFromDriver_); + &pSinqC_->motorVeloFromDriver); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -232,7 +285,7 @@ sinqController::sinqController(const char *portName, } status = createParam("MOTOR_VBAS_FROM_DRIVER", asynParamFloat64, - &motorVbasFromDriver_); + &pSinqC_->motorVbasFromDriver); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -243,7 +296,7 @@ sinqController::sinqController(const char *portName, } status = createParam("MOTOR_VMAX_FROM_DRIVER", asynParamFloat64, - &motorVmaxFromDriver_); + &pSinqC_->motorVmaxFromDriver); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -254,7 +307,7 @@ sinqController::sinqController(const char *portName, } status = createParam("MOTOR_ACCL_FROM_DRIVER", asynParamFloat64, - &motorAcclFromDriver_); + &pSinqC_->motorAcclFromDriver); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -264,7 +317,8 @@ sinqController::sinqController(const char *portName, exit(-1); } - status = createParam("ADAPTIVE_POLLING", asynParamInt32, &adaptivePolling_); + status = createParam("ADAPTIVE_POLLING", asynParamInt32, + &pSinqC_->adaptivePolling); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -274,7 +328,7 @@ sinqController::sinqController(const char *portName, exit(-1); } - status = createParam("ENCODER_TYPE", asynParamOctet, &encoderType_); + status = createParam("ENCODER_TYPE", asynParamOctet, &pSinqC_->encoderType); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -284,7 +338,8 @@ sinqController::sinqController(const char *portName, exit(-1); } - status = createParam("MOTOR_FORCE_STOP", asynParamInt32, &motorForceStop_); + status = createParam("MOTOR_FORCE_STOP", asynParamInt32, + &pSinqC_->motorForceStop); if (status != asynSuccess) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\" => %s, line %d:\nFATAL ERROR (creating a " @@ -351,11 +406,11 @@ asynStatus sinqController::writeInt32(asynUser *pasynUser, epicsInt32 value) { } // Handle custom PVs - if (function == motorEnable_) { + if (function == motorEnable()) { return axis->enable(value != 0); - } else if (function == motorReset_) { + } else if (function == motorReset()) { return axis->reset(); - } else if (function == motorForceStop_) { + } else if (function == motorForceStop()) { return axis->stop(0.0); } else { return asynMotorController::writeInt32(pasynUser, value); @@ -373,10 +428,10 @@ asynStatus sinqController::readInt32(asynUser *pasynUser, epicsInt32 *value) { return asynError; } - if (pasynUser->reason == motorEnableRBV_) { + if (pasynUser->reason == motorEnableRBV()) { getAxisParamChecked(axis, motorEnableRBV, value); return asynSuccess; - } else if (pasynUser->reason == motorCanDisable_) { + } else if (pasynUser->reason == motorCanDisable()) { getAxisParamChecked(axis, motorCanDisable, value); return asynSuccess; } else { @@ -389,7 +444,7 @@ asynStatus sinqController::couldNotParseResponse(const char *command, int axisNo, const char *functionName, int line) { - asynPrint(pasynOctetSyncIOipPort_, ASYN_TRACE_ERROR, + asynPrint(pasynOctetSyncIOipPort(), ASYN_TRACE_ERROR, "Controller \"%s\", axis %d => %s, line %d:\nCould not interpret " "response \"%s\" for command \"%s\".\n", portName, axisNo, functionName, line, response, command); @@ -418,7 +473,7 @@ asynStatus sinqController::paramLibAccessFailed(asynStatus status, int line) { if (status != asynSuccess) { - asynPrint(pasynOctetSyncIOipPort_, ASYN_TRACE_ERROR, + asynPrint(pasynOctetSyncIOipPort(), ASYN_TRACE_ERROR, "Controller \"%s\", axis %d => %s, line %d:\n Accessing the " "parameter library failed for parameter %s with error %s.\n", portName, axisNo, functionName, line, parameter, @@ -440,40 +495,42 @@ asynStatus sinqController::checkComTimeoutWatchdog(int axisNo, asynStatus paramLibStatus = asynSuccess; // 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 while (1) { - if (timeoutEvents_.empty()) { + if (pSinqC_->timeoutEvents.empty()) { break; } - if (timeoutEvents_[0] + comTimeoutWindow_ <= time(NULL)) { - timeoutEvents_.pop_front(); + if (pSinqC_->timeoutEvents[0] + pSinqC_->comTimeoutWindow <= + time(NULL)) { + pSinqC_->timeoutEvents.pop_front(); } else { break; } } // 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__, - __LINE__, wantToPrint, - pasynUserSelf)) { + if (pSinqC_->msgPrintC.shouldBePrinted(portName, axisNo, + __PRETTY_FUNCTION__, __LINE__, + wantToPrint, pasynUserSelf)) { asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, "Controller \"%s\", axis %d => %s, line %d:\nMore than %ld " "communication timeouts in %ld " "seconds.%s\n", portName, axisNo, __PRETTY_FUNCTION__, __LINE__, - maxNumberTimeouts_, comTimeoutWindow_, - msgPrintControl_.getSuffix()); + pSinqC_->maxNumberTimeouts, pSinqC_->comTimeoutWindow, + pSinqC_->msgPrintC.getSuffix()); } if (wantToPrint) { snprintf(motorMessage, motorMessageSize, "More than %ld communication timeouts in %ld seconds. Please " "call the support.", - maxNumberTimeouts_, comTimeoutWindow_); + pSinqC_->maxNumberTimeouts, pSinqC_->comTimeoutWindow); paramLibStatus = setIntegerParam(motorStatusCommsError_, 1); if (paramLibStatus != asynSuccess) { @@ -505,8 +562,8 @@ asynStatus sinqController::checkMaxSubsequentTimeouts(int timeoutNo, int axisNo, size_t motorMessageSize) { asynStatus paramLibStatus = asynSuccess; - if (timeoutNo >= maxSubsequentTimeouts_) { - if (!maxSubsequentTimeoutsExceeded_) { + if (timeoutNo >= pSinqC_->maxSubsequentTimeouts) { + if (!pSinqC_->maxSubsequentTimeoutsExceeded) { snprintf(motorMessage, motorMessageSize, "Communication timeout between IOC and motor controller. " "Trying to reconnect ..."); @@ -516,7 +573,7 @@ asynStatus sinqController::checkMaxSubsequentTimeouts(int timeoutNo, int axisNo, "subsequent communication timeouts. Check whether the " "controller is still running and connected to the network.\n", this->portName, axisNo, __PRETTY_FUNCTION__, __LINE__, - maxSubsequentTimeouts_); + pSinqC_->maxSubsequentTimeouts); paramLibStatus = setIntegerParam(motorStatusCommsError_, 1); if (paramLibStatus != asynSuccess) { @@ -524,12 +581,12 @@ asynStatus sinqController::checkMaxSubsequentTimeouts(int timeoutNo, int axisNo, "motorStatusCommsError_", axisNo, __PRETTY_FUNCTION__, __LINE__); } - maxSubsequentTimeoutsExceeded_ = true; + pSinqC_->maxSubsequentTimeoutsExceeded = true; } return asynError; } else { - maxSubsequentTimeoutsExceeded_ = false; + pSinqC_->maxSubsequentTimeoutsExceeded = false; motorMessage[0] = '\0'; return asynSuccess; } @@ -551,8 +608,8 @@ asynStatus sinqController::checkMaxSubsequentTimeouts(int timeoutNo, asynStatus sinqController::poll() { // Decrement the number of outstanding forced fast polls, if they are not // zero - if (outstandingForcedFastPolls_ > 0) { - outstandingForcedFastPolls_--; + if (pSinqC_->outstandingForcedFastPolls > 0) { + pSinqC_->outstandingForcedFastPolls--; } return asynMotorController::poll(); } @@ -560,10 +617,82 @@ asynStatus sinqController::poll() { asynStatus sinqController::wakeupPoller() { // + 1 since outstandingForcedFastPolls_ is reduced once at the start of // a poll cycle - outstandingForcedFastPolls_ = forcedFastPolls_ + 1; + pSinqC_->outstandingForcedFastPolls = forcedFastPolls_ + 1; return asynMotorController::wakeupPoller(); } +asynStatus sinqController::setMaxSubsequentTimeouts(int maxSubsequentTimeouts) { + pSinqC_->maxSubsequentTimeouts = maxSubsequentTimeouts; + return asynSuccess; +} + +/** + * @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 // the strings gives the integer number of each variant (see also method // stringifyAsynStatus) diff --git a/src/sinqController.h b/src/sinqController.h index 6c2aad9..113e28a 100644 --- a/src/sinqController.h +++ b/src/sinqController.h @@ -11,9 +11,8 @@ Stefan Mathis, November 2024 #define sinqController_H #include "asynMotorController.h" #include "msgPrintControl.h" -#include #include -#include +#include #define motorMessageIsFromDriverString "MOTOR_MESSAGE_DRIVER" #define motorMessageTextString "MOTOR_MESSAGE_TEXT" @@ -21,6 +20,8 @@ Stefan Mathis, November 2024 #define AbsoluteEncoder "absolute" #define NoEncoder "none" +struct sinqControllerImpl; + class epicsShareClass sinqController : public asynMotorController { public: /** @@ -175,11 +176,7 @@ class epicsShareClass sinqController : public asynMotorController { * @return asynStatus */ virtual asynStatus setThresholdComTimeout(time_t comTimeoutWindow, - size_t maxNumberTimeouts) { - comTimeoutWindow_ = comTimeoutWindow; - maxNumberTimeouts_ = maxNumberTimeouts; - return asynSuccess; - } + size_t maxNumberTimeouts); /** * @brief Inform the user, if the number of timeouts exceeds the threshold @@ -211,17 +208,14 @@ class epicsShareClass sinqController : public asynMotorController { * @param maxSubsequentTimeouts * @return asynStatus */ - asynStatus setMaxSubsequentTimeouts(int maxSubsequentTimeouts) { - maxSubsequentTimeouts_ = maxSubsequentTimeouts; - return asynSuccess; - } + asynStatus setMaxSubsequentTimeouts(int maxSubsequentTimeouts); /** * @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 &getMsgPrintControl() { return msgPrintControl_; } + msgPrintControl &getMsgPrintControl(); /** * @brief Get the axis object @@ -293,25 +287,26 @@ class epicsShareClass sinqController : public asynMotorController { int motorRecDirection() { return motorRecDirection_; } int motorRecOffset() { return motorRecOffset_; } - // Accessors for additional PVs defined in sinqController - int motorMessageText() { return motorMessageText_; } - int motorReset() { return motorReset_; } - int motorEnable() { return motorEnable_; } - int motorEnableRBV() { return motorEnableRBV_; } - int motorCanDisable() { return motorCanDisable_; } - int motorEnableMovWatchdog() { return motorEnableMovWatchdog_; } - int motorCanSetSpeed() { return motorCanSetSpeed_; } - int motorLimitsOffset() { return motorLimitsOffset_; } - int motorForceStop() { return motorForceStop_; } - int motorConnected() { return motorConnected_; } - int motorVeloFromDriver() { return motorVeloFromDriver_; } - int motorVbasFromDriver() { return motorVbasFromDriver_; } - int motorVmaxFromDriver() { return motorVmaxFromDriver_; } - int motorAcclFromDriver() { return motorAcclFromDriver_; } - int motorHighLimitFromDriver() { return motorHighLimitFromDriver_; } - int motorLowLimitFromDriver() { return motorLowLimitFromDriver_; } - int adaptivePolling() { return adaptivePolling_; } - int encoderType() { return encoderType_; } + // Accessors for additional PVs defined in sinqController (which are hidden + // in pSinqC_) + int motorMessageText(); + int motorReset(); + int motorEnable(); + int motorEnableRBV(); + int motorCanDisable(); + int motorEnableMovWatchdog(); + int motorCanSetSpeed(); + int motorLimitsOffset(); + int motorForceStop(); + int motorConnected(); + int motorVeloFromDriver(); + int motorVbasFromDriver(); + int motorVmaxFromDriver(); + int motorAcclFromDriver(); + int motorHighLimitFromDriver(); + int motorLowLimitFromDriver(); + int adaptivePolling(); + int encoderType(); // Additional members int numAxes() { return numAxes_; } @@ -330,7 +325,7 @@ class epicsShareClass sinqController : public asynMotorController { * * @return asynUser* */ - asynUser *pasynOctetSyncIOipPort() { return pasynOctetSyncIOipPort_; } + asynUser *pasynOctetSyncIOipPort(); /** * @brief Overloaded version of `asynController::poll` which decreases @@ -374,7 +369,7 @@ class epicsShareClass sinqController : public asynMotorController { * specified * */ - int outstandingForcedFastPolls() { return outstandingForcedFastPolls_; } + int outstandingForcedFastPolls(); // Maximum error message buffer size. This is an empirical value which must // be large enough to avoid overflows for all commands to the device / @@ -383,67 +378,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 - 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: + std::unique_ptr pSinqC_; static void epicsInithookFunction(initHookState iState); }; -#define NUM_SINQMOTOR_DRIVER_PARAMS \ - (&LAST_SINQMOTOR_PARAM - &FIRST_SINQMOTOR_PARAM + 1) #endif