Compare commits

...

3 Commits
0.6.0 ... 0.7.0

Author SHA1 Message Date
d6adf1ad2a Fixed a compilation bug related to sinqMotor 0.8.0 2025-03-04 09:39:32 +01:00
dfb55a1b76 Added new feature msgPrintControl from sinqMotor 0.8.0. Correspondingly,
the minimum version requirement for sinqMotor has been bumped to 0.8.0.
2025-03-04 09:29:19 +01:00
8f597550fa Fixed a bug in readInt32 and prepared the turboPmac library to serve the
detectorTower library
2025-02-26 14:08:37 +01:00
6 changed files with 330 additions and 252 deletions

View File

@ -14,7 +14,7 @@ REQUIRED+=sinqMotor
motorBase_VERSION=7.2.2
# Specify the version of sinqMotor we want to build against
sinqMotor_VERSION=0.7.0
sinqMotor_VERSION=0.8.0
# These headers allow to depend on this library for derived drivers.
HEADERS += src/turboPmacAxis.h

View File

@ -17,25 +17,45 @@ The folder "utils" contains utility scripts for working with pmac motor controll
### Usage in IOC shell
turboPmac exposes the following IOC shell functions (all in turboPmacController.cpp):
turboPmac exports the following IOC shell functions:
- `turboPmacController`: Create a new controller object.
- `turboPmacAxis`: Create a new axis object.
These functions are parametrized as follows:
The full mcu.cmd file looks like this:
```
turboPmacController(
"$(NAME)", # Name of the MCU, e.g. mcu1. This parameter should be provided by an environment variable.
"$(ASYN_PORT)", # IP-Port of the MCU. This parameter should be provided by an environment variable.
8, # Maximum number of axes
0.05, # Busy poll period in seconds
1, # Idle poll period in seconds
0.05 # Communication timeout in seconds
);
```
```
turboPmacAxis(
"$(NAME)", # Name of the associated MCU, e.g. mcu1. This parameter should be provided by an environment variable.
1 # Index of the axis.
);
# Define the name of the controller and the corresponding port
epicsEnvSet("NAME","mcu")
epicsEnvSet("ASYN_PORT","p$(NAME)")
# Create the TCP/IP socket used to talk with the controller. The socket can be adressed from within the IOC shell via the port name
drvAsynIPPortConfigure("$(ASYN_PORT)","172.28.101.24:1025")
# Create the controller object with the defined name and connect it to the socket via the port name.
# The other parameters are as follows:
# 8: Maximum number of axes
# 0.05: Busy poll period in seconds
# 1: Idle poll period in seconds
# 1: Socket communication timeout in seconds
turboPmacController("$(NAME)", "$(ASYN_PORT)", 8, 0.05, 1, 1);
# Define some axes for the specified MCU at the given slot (1, 2 and 5). No slot may be used twice!
turboPmacAxis("$(NAME)",1);
turboPmacAxis("$(NAME)",2);
turboPmacAxis("$(NAME)",5);
# Set the number of subsequent timeouts
setMaxSubsequentTimeouts("$(NAME)", 20);
# Configure the timeout frequency watchdog:
setThresholdComTimeout("$(NAME)", 100, 1);
# Parametrize the EPICS record database with the substitution file named after the MCU.
epicsEnvSet("SINQDBPATH","$(sinqMotor_DB)/sinqMotor.db")
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
epicsEnvSet("SINQDBPATH","$(turboPmac_DB)/turboPmac.db")
dbLoadTemplate("$(TOP)/$(NAME).substitutions", "INSTR=$(INSTR)$(NAME):,CONTROLLER=$(NAME)")
dbLoadRecords("$(sinqMotor_DB)/asynRecord.db","P=$(INSTR)$(NAME),PORT=$(ASYN_PORT)")
```
### Versioning

View File

@ -35,7 +35,8 @@ static void epicsInithookFunction(initHookState iState) {
}
}
turboPmacAxis::turboPmacAxis(turboPmacController *pC, int axisNo)
turboPmacAxis::turboPmacAxis(turboPmacController *pC, int axisNo,
bool initialize)
: sinqAxis(pC, axisNo), pC_(pC) {
asynStatus status = asynSuccess;
@ -63,13 +64,17 @@ turboPmacAxis::turboPmacAxis(turboPmacController *pC, int axisNo)
exit(-1);
}
// Register the hook function during construction of the first axis object
if (axes.empty()) {
initHookRegister(&epicsInithookFunction);
}
if (initialize) {
// Register the hook function during construction of the first axis
// object
if (axes.empty()) {
initHookRegister(&epicsInithookFunction);
}
// Collect all axes into this list which will be used in the hook function
axes.push_back(this);
// Collect all axes into this list which will be used in the hook
// function
axes.push_back(this);
}
// Initialize all member variables
waitForHandshake_ = false;
@ -109,9 +114,6 @@ turboPmacAxis::~turboPmacAxis(void) {
// clean up the pointer pC here.
}
/**
Read the configuration at the first poll
*/
asynStatus turboPmacAxis::init() {
// Local variable declaration
@ -363,39 +365,43 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
__PRETTY_FUNCTION__, __LINE__);
}
// Intepret the status
// Create the unique callsite identifier manually so it can be used later in
// the shouldBePrinted calls.
msgPrintControlKey keyStatus = msgPrintControlKey(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
bool resetCountStatus = true;
// Interpret the status
switch (axStatus) {
case -6:
// Axis is stopping
*moving = true;
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d\nAxis is stopping\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
break;
case -5:
// Axis is deactivated
*moving = false;
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d\nAxis is "
"deactivated\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
pl_status = setStringParam(pC_->motorMessageText_, "Deactivated");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
break;
case -4:
// Emergency stop
*moving = false;
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nEmergency stop "
"activated\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
if (pC_->msgPrintControl_.shouldBePrinted(keyStatus, true,
pC_->pasynUserSelf)) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nEmergency stop "
"activated.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetCountStatus = false;
pl_status = setStringParam(pC_->motorMessageText_, "Emergency stop");
if (pl_status != asynSuccess) {
@ -406,13 +412,9 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
break;
case -3:
// Disabled
*moving = false;
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d\nAxis %d is disabled\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axisNo_);
pl_status = setStringParam(pC_->motorMessageText_, "Disabled");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
@ -422,43 +424,24 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
break;
case 0:
// Idle
*moving = false;
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d\nAxis is idle\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
break;
case 1:
// Move order acknowledged
*moving = true;
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d\nMove order "
"acknowledged\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
break;
case 2:
// Move order confirmed possible
*moving = true;
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d\nMove order is "
"possible\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
break;
case 3:
// Axis in Air Cushion Output status
*moving = true;
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d\nAxis in Air "
"Cushion Output status\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
break;
case 4:
// Axis in Air Cushion Input status
*moving = true;
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d\nAxis in Air "
"Cushion Input status\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
break;
case 5:
*moving = true;
@ -472,15 +455,22 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
default:
*moving = false;
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nReached unreachable "
"state P%2.2d00 = %d\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axisNo_,
axStatus);
pl_status = setStringParam(
pC_->motorMessageText_,
"Unreachable state has been reached. Please call the support.");
if (pC_->msgPrintControl_.shouldBePrinted(keyStatus, true,
pC_->pasynUserSelf)) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nReached "
"unreachable state P%2.2d00 = %d.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
axisNo_, axStatus, pC_->msgPrintControl_.getSuffix());
}
resetCountStatus = false;
snprintf(userMessage, sizeof(userMessage),
"Unknown state P%2.2d00 = %d has been reached. Please call "
"the support.",
axisNo_, error);
pl_status = setStringParam(pC_->motorMessageText_, userMessage);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
@ -488,6 +478,10 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
}
}
if (resetCountStatus) {
pC_->msgPrintControl_.resetCount(keyStatus);
}
if (*moving) {
// If the axis is moving, evaluate the movement direction
if ((currentPosition - previousPosition) > 0) {
@ -497,19 +491,28 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
}
}
// Error handling
// Create the unique callsite identifier manually so it can be used later in
// the shouldBePrinted calls.
msgPrintControlKey keyError = msgPrintControlKey(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
bool resetError = true;
switch (error) {
case 0:
// No error
// No error -> Reset the message repetition watchdog
break;
case 1:
// EPICS should already prevent this issue in the first place,
// since it contains the user limits
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nTarget position would "
"exceed user limits.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf)) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nTarget "
"position would exceed user limits.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
pl_status = setStringParam(pC_->motorMessageText_,
"Target position would exceed software "
@ -524,11 +527,17 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
break;
case 5:
// Command not possible
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAxis is "
"still moving, but received another move command. EPICS "
"should prevent this, check if *moving is set correctly.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
if (pC_->msgPrintControl_.shouldBePrinted(keyStatus, true,
pC_->pasynUserSelf)) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAxis is "
"still moving, but received another move command. EPICS "
"should prevent this, check if *moving is set correctly.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
pl_status = setStringParam(pC_->motorMessageText_,
"Axis received move command while it is "
@ -542,12 +551,15 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
poll_status = asynError;
break;
case 8:
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAir cushion feedback "
"stopped during movement (P%2.2d01 = %d).\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axisNo_,
error);
if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf)) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAir cushion "
"feedback stopped during movement (P%2.2d01 = %d).%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
axisNo_, error, pC_->msgPrintControl_.getSuffix());
}
resetError = false;
snprintf(userMessage, sizeof(userMessage),
"Air cushion feedback stopped during movement (P%2.2d01 = "
@ -562,11 +574,16 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
}
break;
case 9:
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nNo air cushion "
"feedback before movement start (P%2.2d01 = %d).\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
axisNo_, error);
if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf)) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nNo air cushion "
"feedback before movement start (P%2.2d01 = %d).%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axisNo_,
error, pC_->msgPrintControl_.getSuffix());
}
resetError = false;
snprintf(userMessage, sizeof(userMessage),
"No air cushion feedback before movement start (P%2.2d01 = "
@ -586,10 +603,17 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
error case can only happen if either the axis has an incremental encoder
which is not properly homed or if a bug occured.
*/
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAxis hit the "
"controller limits.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf)) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAxis hit the "
"controller limits.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
snprintf(userMessage, sizeof(userMessage),
"Software limits or end switch hit (P%2.2d01 = %d). Try "
@ -609,10 +633,17 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
break;
case 11:
// Following error
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nMaximum allowed "
"following error exceeded.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf)) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nMaximum allowed "
"following error exceeded.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
snprintf(command, sizeof(command),
"Maximum allowed following error exceeded (P%2.2d01 = %d). "
@ -630,6 +661,16 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
break;
case 12:
if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf)) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nSecurity "
"input is triggered (P%2.2d01 = %d).%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
axisNo_, error, pC_->msgPrintControl_.getSuffix());
}
resetError = false;
snprintf(command, sizeof(command),
"Security input is triggered (P%2.2d01 = %d). Check the SPS "
"for errors (if available). Otherwise please call "
@ -647,11 +688,17 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
case 13:
// Driver hardware error triggered
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nDriver hardware error "
"triggered.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf)) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nDriver "
"hardware error triggered.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
snprintf(command, sizeof(command),
"Driver hardware error (P%2.2d01 = 13). "
"Please call the support.",
@ -668,18 +715,22 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
case 14:
// EPICS should already prevent this issue in the first place,
// since it contains the user limits
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nMove command exceeds "
"hardware limits (P%2.2d01 = %d).\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axisNo_,
error);
snprintf(command, sizeof(command),
if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf)) {
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nMove "
"command exceeds hardware limits (P%2.2d01 = %d).%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
axisNo_, error, pC_->msgPrintControl_.getSuffix());
}
resetError = false;
snprintf(userMessage, sizeof(userMessage),
"Move command exceeds hardware limits (P%2.2d01 = %d). Please "
"call the support.",
axisNo_, error);
pl_status = setStringParam(pC_->motorMessageText_, command);
pl_status = setStringParam(pC_->motorMessageText_, userMessage);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
@ -689,15 +740,22 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
poll_status = asynError;
break;
default:
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nUnknown error "
"P%2.2d01 = %d.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
axisNo_, error);
pl_status = setStringParam(
pC_->motorMessageText_,
"Unknown error P%2.2d01 = %d. Please call the support.");
if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf)) {
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nUnknown error "
"P%2.2d01 = %d.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axisNo_,
error, pC_->msgPrintControl_.getSuffix());
}
resetError = false;
snprintf(userMessage, sizeof(userMessage),
"Unknown error P%2.2d01 = %d. Please call the support.",
axisNo_, error);
pl_status = setStringParam(pC_->motorMessageText_, userMessage);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
@ -708,6 +766,10 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
break;
}
if (resetError) {
pC_->msgPrintControl_.resetCount(keyError);
}
// Update the parameter library
if (error != 0) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true);

View File

@ -3,7 +3,7 @@
#include "sinqAxis.h"
// Forward declaration of the controller class to resolve the cyclic dependency
// between C804Controller.h and C804Axis.h. See
// between the controller and the axis .h-file. See
// https://en.cppreference.com/w/cpp/language/class.
class turboPmacController;
@ -15,7 +15,8 @@ class turboPmacAxis : public sinqAxis {
* @param pController Pointer to the associated controller
* @param axisNo Index of the axis
*/
turboPmacAxis(turboPmacController *pController, int axisNo);
turboPmacAxis(turboPmacController *pController, int axisNo,
bool initialize = true);
/**
* @brief Destroy the turboPmacAxis
@ -74,7 +75,7 @@ class turboPmacAxis : public sinqAxis {
* The following steps are performed:
* - Read out the motor status, motor position, velocity and acceleration
* from the MCU and store this information in the parameter library.
* - Set the enable PV accordint to the initial status of the axis.
* - Set the enable PV according to the initial status of the axis.
*
* @return asynStatus
*/

View File

@ -29,23 +29,11 @@ void adjustResponseForPrint(char *dst, const char *src, size_t buf_length) {
}
}
/**
* @brief Construct a new turboPmacController::turboPmacController object
*
* @param portName See documentation of sinqController
* @param ipPortConfigName See documentation of sinqController
* @param numAxes See documentation of sinqController
* @param movingPollPeriod See documentation of sinqController
* @param idlePollPeriod See documentation of sinqController
* @param comTimeout Time after which a communication timeout error
* is declared in writeRead (in seconds)
* @param extraParams See documentation of sinqController
*/
turboPmacController::turboPmacController(const char *portName,
const char *ipPortConfigName,
int numAxes, double movingPollPeriod,
double idlePollPeriod,
double comTimeout)
double comTimeout, int numExtraParams)
: sinqController(
portName, ipPortConfigName, numAxes, movingPollPeriod, idlePollPeriod,
/*
@ -53,7 +41,7 @@ turboPmacController::turboPmacController(const char *portName,
- REREAD_ENCODER_POSITION
- READ_CONFIG
*/
NUM_turboPmac_DRIVER_PARAMS)
numExtraParams + NUM_turboPmac_DRIVER_PARAMS)
{
@ -145,39 +133,18 @@ Access one of the axes of the controller via the axis adress stored in asynUser.
If the axis does not exist or is not a Axis, a nullptr is returned and an
error is emitted.
*/
turboPmacAxis *turboPmacController::getAxis(asynUser *pasynUser) {
turboPmacAxis *turboPmacController::getTurboPmacAxis(asynUser *pasynUser) {
asynMotorAxis *asynAxis = asynMotorController::getAxis(pasynUser);
return turboPmacController::castToAxis(asynAxis);
return dynamic_cast<turboPmacAxis *>(asynAxis);
}
/*
Access one of the axes of the controller via the axis index.
If the axis does not exist or is not a Axis, the function must return Null
*/
turboPmacAxis *turboPmacController::getAxis(int axisNo) {
turboPmacAxis *turboPmacController::getTurboPmacAxis(int axisNo) {
asynMotorAxis *asynAxis = asynMotorController::getAxis(axisNo);
return turboPmacController::castToAxis(asynAxis);
}
turboPmacAxis *turboPmacController::castToAxis(asynMotorAxis *asynAxis) {
// =========================================================================
// If the axis slot of the pAxes_ array is empty, a nullptr must be returned
if (asynAxis == nullptr) {
return nullptr;
}
// Here, an error is emitted since asyn_axis is not a nullptr but also not
// an instance of Axis
turboPmacAxis *axis = dynamic_cast<turboPmacAxis *>(asynAxis);
if (axis == nullptr) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAxis is not "
"an instance of turboPmacAxis",
portName, axis->axisNo_, __PRETTY_FUNCTION__, __LINE__);
}
return axis;
return dynamic_cast<turboPmacAxis *>(asynAxis);
}
asynStatus turboPmacController::writeRead(int axisNo, const char *command,
@ -216,7 +183,7 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
// =========================================================================
turboPmacAxis *axis = getAxis(axisNo);
turboPmacAxis *axis = getTurboPmacAxis(axisNo);
if (axis == nullptr) {
// We already did the error logging directly in getAxis
return asynError;
@ -273,10 +240,6 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
// +1 for the carriage return.
const size_t fullComandLength = offset + commandLength + 1;
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
"Controller \"%s\", axis %d => %s, line %d\nSending command %s",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, fullCommand);
/*
We use separated write and read commands here, not the combined writeRead
method, because the latter is actually a flushWriteRead (see
@ -294,12 +257,19 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
status = pasynOctetSyncIO->write(lowLevelPortUser_, fullCommand,
fullComandLength, comTimeout_, &nbytesOut);
msgPrintControlKey writeKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (status == asynTimeout) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nTimeout while "
"writing to the MCU\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (msgPrintControl_.shouldBePrinted(writeKey, true, pasynUserSelf)) {
asynPrint(
this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nTimeout while "
"writing to the MCU.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
msgPrintControl_.getSuffix());
}
timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText,
sizeof(drvMessageText));
@ -321,23 +291,35 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
}
}
} else if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nError %s while "
"writing to the controller\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
if (msgPrintControl_.shouldBePrinted(writeKey, true, pasynUserSelf)) {
asynPrint(
this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nError %s while "
"writing to the controller.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status), msgPrintControl_.getSuffix());
}
} else {
msgPrintControl_.resetCount(writeKey);
}
// Read the response from the MCU buffer
status = pasynOctetSyncIO->read(lowLevelPortUser_, response, MAXBUF_,
comTimeout_, &nbytesIn, &eomReason);
msgPrintControlKey readKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (status == asynTimeout) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nTimeout while "
"reading from the MCU\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (msgPrintControl_.shouldBePrinted(readKey, true, pasynUserSelf)) {
asynPrint(
this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nTimeout while "
"reading from the MCU.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
msgPrintControl_.getSuffix());
}
// Add this event to the back of the timeout event counter
timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText,
@ -360,11 +342,16 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
}
}
} else if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nError %s while "
"reading from the controller\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
if (msgPrintControl_.shouldBePrinted(readKey, true, pasynUserSelf)) {
asynPrint(
this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nError %s while "
"reading from the controller.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status), msgPrintControl_.getSuffix());
}
} else {
msgPrintControl_.resetCount(readKey);
}
if (timeoutStatus == asynError) {
@ -372,6 +359,8 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
}
// The message should only ever terminate due to reason 2
msgPrintControlKey terminateKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (eomReason != 2) {
status = asynError;
@ -379,10 +368,16 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
"Terminated message due to reason %d (should be 2).",
eomReason);
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
"Controller \"%s\", axis %d => %s, line %d\nMessage "
"terminated due to reason %i\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, eomReason);
if (msgPrintControl_.shouldBePrinted(terminateKey, true,
pasynUserSelf)) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nMessage "
"terminated due to reason %i.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
eomReason, msgPrintControl_.getSuffix());
}
} else {
msgPrintControl_.resetCount(terminateKey);
}
/*
@ -394,20 +389,30 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
numReceivedResponses++;
}
}
msgPrintControlKey numResponsesKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (numExpectedResponses != numReceivedResponses) {
adjustResponseForPrint(modResponse, response, MAXBUF_);
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nUnexpected "
"response '%s' (carriage returns are replaced with spaces) "
"for command %s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, modResponse,
command);
if (msgPrintControl_.shouldBePrinted(numResponsesKey, true,
pasynUserSelf)) {
asynPrint(
this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nUnexpected "
"response '%s' (carriage returns are replaced with spaces) "
"for command %s.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, modResponse,
command, msgPrintControl_.getSuffix());
}
snprintf(drvMessageText, sizeof(drvMessageText),
"Received unexpected response '%s' (carriage returns "
"are replaced with spaces) for command %s. "
"Please call the support",
modResponse, command);
status = asynError;
} else {
msgPrintControl_.resetCount(numResponsesKey);
}
// Create custom error messages for different failure modes, if no error
@ -437,23 +442,12 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
// Log the overall status (communication successfull or not)
if (status == asynSuccess) {
asynPrint(lowLevelPortUser_, ASYN_TRACEIO_DRIVER,
"Controller \"%s\", axis %d => %s, line %d\nDevice "
"response: %s (carriage returns are replaced with spaces)\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, modResponse);
paramLibStatus = axis->setIntegerParam(this->motorStatusCommsError_, 0);
} else {
asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nCommunication "
"failed for command %s (%s)\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, fullCommand,
stringifyAsynStatus(status));
// Check if the axis already is in an error communication mode. If it is
// not, upstream the error. This is done to avoid "flooding" the user
// with different error messages if more than one error ocurred before
// an error-free communication
// Check if the axis already is in an error communication mode. If
// it is not, upstream the error. This is done to avoid "flooding"
// the user with different error messages if more than one error
// ocurred before an error-free communication
paramLibStatus =
getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem);
if (paramLibStatus != asynSuccess) {
@ -494,11 +488,7 @@ asynStatus turboPmacController::writeInt32(asynUser *pasynUser,
// =====================================================================
turboPmacAxis *axis = getAxis(pasynUser);
if (axis == nullptr) {
// We already did the error logging directly in getAxis
return asynError;
}
turboPmacAxis *axis = getTurboPmacAxis(pasynUser);
// Handle custom PVs
if (function == rereadEncoderPosition_) {
@ -510,13 +500,14 @@ asynStatus turboPmacController::writeInt32(asynUser *pasynUser,
}
}
asynStatus sinqController::readInt32(asynUser *pasynUser, epicsInt32 *value) {
asynStatus turboPmacController::readInt32(asynUser *pasynUser,
epicsInt32 *value) {
// PMACs can be disabled
if (pasynUser->reason == motorCanDisable_) {
*value = 1;
return asynSuccess;
} else {
return asynMotorController::readInt32(pasynUser, value);
return sinqController::readInt32(pasynUser, value);
}
}
@ -549,8 +540,8 @@ asynStatus turboPmacCreateController(const char *portName,
https://github.com/epics-modules/motor/blob/master/motorApp/MotorSrc/asynMotorController.cpp
https://github.com/epics-modules/asyn/blob/master/asyn/asynPortDriver/asynPortDriver.cpp
The created object is registered in EPICS in its constructor and can safely
be "leaked" here.
The created object is registered in EPICS in its constructor and can
safely be "leaked" here.
*/
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-variable"
@ -582,17 +573,17 @@ static const iocshArg CreateControllerArg5 = {"Communication timeout (s)",
static const iocshArg *const CreateControllerArgs[] = {
&CreateControllerArg0, &CreateControllerArg1, &CreateControllerArg2,
&CreateControllerArg3, &CreateControllerArg4, &CreateControllerArg5};
static const iocshFuncDef configturboPmacCreateController = {
static const iocshFuncDef configTurboPmacCreateController = {
"turboPmacController", 6, CreateControllerArgs};
static void configTurboPmacCreateControllerCallFunc(const iocshArgBuf *args) {
turboPmacCreateController(args[0].sval, args[1].sval, args[2].ival,
args[3].dval, args[4].dval, args[5].dval);
}
// This function is made known to EPICS in turboPmac.dbd and is called by EPICS
// in order to register both functions in the IOC shell
// This function is made known to EPICS in turboPmac.dbd and is called by
// EPICS in order to register both functions in the IOC shell
static void turboPmacControllerRegister(void) {
iocshRegister(&configturboPmacCreateController,
iocshRegister(&configTurboPmacCreateController,
configTurboPmacCreateControllerCallFunc);
}
epicsExportRegistrar(turboPmacControllerRegister);

View File

@ -13,10 +13,10 @@
#include "turboPmacAxis.h"
class turboPmacController : public sinqController {
public:
/**
* @brief Construct a new turboPmacController object
* @brief Construct a new turboPmacController object. This function is meant
to be called from a child class constructor.
*
* @param portName See sinqController constructor
* @param ipPortConfigName See sinqController constructor
@ -26,10 +26,12 @@ class turboPmacController : public sinqController {
* @param comTimeout When trying to communicate with the device,
the underlying asynOctetSyncIO interface waits for a response until this
time (in seconds) has passed, then it declares a timeout.
* @param numExtraParams Number of extra parameters from a child class
*/
turboPmacController(const char *portName, const char *ipPortConfigName,
int numAxes, double movingPollPeriod,
double idlePollPeriod, double comTimeout);
double idlePollPeriod, double comTimeout,
int numExtraParams = 0);
/**
* @brief Get the axis object
@ -38,7 +40,7 @@ class turboPmacController : public sinqController {
* @return turboPmacAxis* If no axis could be found, this is a
* nullptr
*/
turboPmacAxis *getAxis(asynUser *pasynUser);
turboPmacAxis *getTurboPmacAxis(asynUser *pasynUser);
/**
* @brief Get the axis object
@ -47,7 +49,18 @@ class turboPmacController : public sinqController {
* @return turboPmacAxis* If no axis could be found, this is a
* nullptr
*/
turboPmacAxis *getAxis(int axisNo);
turboPmacAxis *getTurboPmacAxis(int axisNo);
/**
* @brief Overloaded function of sinqController
*
* The function is overloaded in order to read motorCanDisable_.
*
* @param pasynUser
* @param value
* @return asynStatus
*/
virtual asynStatus readInt32(asynUser *pasynUser, epicsInt32 *value);
/**
* @brief Overloaded function of sinqController
@ -58,7 +71,7 @@ class turboPmacController : public sinqController {
* @param value New value
* @return asynStatus
*/
asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
virtual asynStatus writeInt32(asynUser *pasynUser, epicsInt32 value);
protected:
asynUser *lowLevelPortUser_;
@ -82,15 +95,6 @@ class turboPmacController : public sinqController {
asynStatus writeRead(int axisNo, const char *command, char *response,
int numExpectedResponses);
/**
* @brief Save cast of the given asynAxis pointer to a turboPmacAxis
* pointer. If the cast fails, this function returns a nullptr.
*
* @param asynAxis
* @return turboPmacAxis*
*/
turboPmacAxis *castToAxis(asynMotorAxis *asynAxis);
/**
* @brief Specialized version of sinqController::errMsgCouldNotParseResponse
* for turboPmac
@ -114,7 +118,7 @@ class turboPmacController : public sinqController {
const char *functionName,
int lineNumber);
private:
protected:
// Set the maximum buffer size. This is an empirical value which must be
// large enough to avoid overflows for all commands to the device /
// responses from it.