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 motorBase_VERSION=7.2.2
# Specify the version of sinqMotor we want to build against # 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. # These headers allow to depend on this library for derived drivers.
HEADERS += src/turboPmacAxis.h 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 ### 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. - `turboPmacController`: Create a new controller object.
- `turboPmacAxis`: Create a new axis object. - `turboPmacAxis`: Create a new axis object.
These functions are parametrized as follows:
The full mcu.cmd file looks like this:
``` ```
turboPmacController( # Define the name of the controller and the corresponding port
"$(NAME)", # Name of the MCU, e.g. mcu1. This parameter should be provided by an environment variable. epicsEnvSet("NAME","mcu")
"$(ASYN_PORT)", # IP-Port of the MCU. This parameter should be provided by an environment variable. epicsEnvSet("ASYN_PORT","p$(NAME)")
8, # Maximum number of axes
0.05, # Busy poll period in seconds # 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
1, # Idle poll period in seconds drvAsynIPPortConfigure("$(ASYN_PORT)","172.28.101.24:1025")
0.05 # Communication timeout in seconds
); # 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
turboPmacAxis( # 0.05: Busy poll period in seconds
"$(NAME)", # Name of the associated MCU, e.g. mcu1. This parameter should be provided by an environment variable. # 1: Idle poll period in seconds
1 # Index of the axis. # 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 ### 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) { : sinqAxis(pC, axisNo), pC_(pC) {
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
@ -63,13 +64,17 @@ turboPmacAxis::turboPmacAxis(turboPmacController *pC, int axisNo)
exit(-1); exit(-1);
} }
// Register the hook function during construction of the first axis object if (initialize) {
if (axes.empty()) { // Register the hook function during construction of the first axis
initHookRegister(&epicsInithookFunction); // object
} if (axes.empty()) {
initHookRegister(&epicsInithookFunction);
}
// Collect all axes into this list which will be used in the hook function // Collect all axes into this list which will be used in the hook
axes.push_back(this); // function
axes.push_back(this);
}
// Initialize all member variables // Initialize all member variables
waitForHandshake_ = false; waitForHandshake_ = false;
@ -109,9 +114,6 @@ turboPmacAxis::~turboPmacAxis(void) {
// clean up the pointer pC here. // clean up the pointer pC here.
} }
/**
Read the configuration at the first poll
*/
asynStatus turboPmacAxis::init() { asynStatus turboPmacAxis::init() {
// Local variable declaration // Local variable declaration
@ -363,39 +365,43 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
__PRETTY_FUNCTION__, __LINE__); __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) { switch (axStatus) {
case -6: case -6:
// Axis is stopping
*moving = true; *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; break;
case -5: case -5:
// Axis is deactivated
*moving = false; *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"); pl_status = setStringParam(pC_->motorMessageText_, "Deactivated");
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__, axisNo_, __PRETTY_FUNCTION__,
__LINE__); __LINE__);
} }
break; break;
case -4: case -4:
// Emergency stop
*moving = false; *moving = false;
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, if (pC_->msgPrintControl_.shouldBePrinted(keyStatus, true,
"Controller \"%s\", axis %d => %s, line %d\nEmergency stop " pC_->pasynUserSelf)) {
"activated\n", asynPrint(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); 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"); pl_status = setStringParam(pC_->motorMessageText_, "Emergency stop");
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
@ -406,13 +412,9 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
break; break;
case -3: case -3:
// Disabled
*moving = false; *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"); pl_status = setStringParam(pC_->motorMessageText_, "Disabled");
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
@ -422,43 +424,24 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
break; break;
case 0: case 0:
// Idle
*moving = false; *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; break;
case 1: case 1:
// Move order acknowledged
*moving = true; *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; break;
case 2: case 2:
// Move order confirmed possible
*moving = true; *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; break;
case 3: case 3:
// Axis in Air Cushion Output status
*moving = true; *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; break;
case 4: case 4:
// Axis in Air Cushion Input status
*moving = true; *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; break;
case 5: case 5:
*moving = true; *moving = true;
@ -472,15 +455,22 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
default: default:
*moving = false; *moving = false;
asynPrint( if (pC_->msgPrintControl_.shouldBePrinted(keyStatus, true,
pC_->pasynUserSelf, ASYN_TRACE_ERROR, pC_->pasynUserSelf)) {
"Controller \"%s\", axis %d => %s, line %d\nReached unreachable "
"state P%2.2d00 = %d\n", asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axisNo_, "Controller \"%s\", axis %d => %s, line %d\nReached "
axStatus); "unreachable state P%2.2d00 = %d.%s\n",
pl_status = setStringParam( pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->motorMessageText_, axisNo_, axStatus, pC_->msgPrintControl_.getSuffix());
"Unreachable state has been reached. Please call the support."); }
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) { if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__, axisNo_, __PRETTY_FUNCTION__,
@ -488,6 +478,10 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
} }
} }
if (resetCountStatus) {
pC_->msgPrintControl_.resetCount(keyStatus);
}
if (*moving) { if (*moving) {
// If the axis is moving, evaluate the movement direction // If the axis is moving, evaluate the movement direction
if ((currentPosition - previousPosition) > 0) { 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) { switch (error) {
case 0: case 0:
// No error // No error -> Reset the message repetition watchdog
break; break;
case 1: case 1:
// EPICS should already prevent this issue in the first place, // EPICS should already prevent this issue in the first place,
// since it contains the user limits // since it contains the user limits
asynPrint( if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf, ASYN_TRACE_ERROR, pC_->pasynUserSelf)) {
"Controller \"%s\", axis %d => %s, line %d\nTarget position would " asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"exceed user limits.\n", "Controller \"%s\", axis %d => %s, line %d\nTarget "
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); "position would exceed user limits.%s\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->msgPrintControl_.getSuffix());
}
resetError = false;
pl_status = setStringParam(pC_->motorMessageText_, pl_status = setStringParam(pC_->motorMessageText_,
"Target position would exceed software " "Target position would exceed software "
@ -524,11 +527,17 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
break; break;
case 5: case 5:
// Command not possible // Command not possible
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, if (pC_->msgPrintControl_.shouldBePrinted(keyStatus, true,
"Controller \"%s\", axis %d => %s, line %d\nAxis is " pC_->pasynUserSelf)) {
"still moving, but received another move command. EPICS " asynPrint(
"should prevent this, check if *moving is set correctly.\n", pC_->pasynUserSelf, ASYN_TRACE_ERROR,
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); "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_, pl_status = setStringParam(pC_->motorMessageText_,
"Axis received move command while it is " "Axis received move command while it is "
@ -542,12 +551,15 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
poll_status = asynError; poll_status = asynError;
break; break;
case 8: case 8:
asynPrint( if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->pasynUserSelf, ASYN_TRACE_ERROR, pC_->pasynUserSelf)) {
"Controller \"%s\", axis %d => %s, line %d\nAir cushion feedback " asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"stopped during movement (P%2.2d01 = %d).\n", "Controller \"%s\", axis %d => %s, line %d\nAir cushion "
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, axisNo_, "feedback stopped during movement (P%2.2d01 = %d).%s\n",
error); pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
axisNo_, error, pC_->msgPrintControl_.getSuffix());
}
resetError = false;
snprintf(userMessage, sizeof(userMessage), snprintf(userMessage, sizeof(userMessage),
"Air cushion feedback stopped during movement (P%2.2d01 = " "Air cushion feedback stopped during movement (P%2.2d01 = "
@ -562,11 +574,16 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
} }
break; break;
case 9: case 9:
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR, if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
"Controller \"%s\", axis %d => %s, line %d\nNo air cushion " pC_->pasynUserSelf)) {
"feedback before movement start (P%2.2d01 = %d).\n", asynPrint(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, pC_->pasynUserSelf, ASYN_TRACE_ERROR,
axisNo_, 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), snprintf(userMessage, sizeof(userMessage),
"No air cushion feedback before movement start (P%2.2d01 = " "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 error case can only happen if either the axis has an incremental encoder
which is not properly homed or if a bug occured. 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 " if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
"controller limits.\n", pC_->pasynUserSelf)) {
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); 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), snprintf(userMessage, sizeof(userMessage),
"Software limits or end switch hit (P%2.2d01 = %d). Try " "Software limits or end switch hit (P%2.2d01 = %d). Try "
@ -609,10 +633,17 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
break; break;
case 11: case 11:
// Following error // Following error
asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nMaximum allowed " if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
"following error exceeded.\n", pC_->pasynUserSelf)) {
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); 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), snprintf(command, sizeof(command),
"Maximum allowed following error exceeded (P%2.2d01 = %d). " "Maximum allowed following error exceeded (P%2.2d01 = %d). "
@ -630,6 +661,16 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
break; break;
case 12: 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), snprintf(command, sizeof(command),
"Security input is triggered (P%2.2d01 = %d). Check the SPS " "Security input is triggered (P%2.2d01 = %d). Check the SPS "
"for errors (if available). Otherwise please call " "for errors (if available). Otherwise please call "
@ -647,11 +688,17 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
case 13: case 13:
// Driver hardware error triggered // Driver hardware error triggered
asynPrint(
pC_->pasynUserSelf, ASYN_TRACE_ERROR, if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
"Controller \"%s\", axis %d => %s, line %d\nDriver hardware error " pC_->pasynUserSelf)) {
"triggered.\n", asynPrint(pC_->pasynUserSelf, ASYN_TRACE_ERROR,
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__); "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), snprintf(command, sizeof(command),
"Driver hardware error (P%2.2d01 = 13). " "Driver hardware error (P%2.2d01 = 13). "
"Please call the support.", "Please call the support.",
@ -668,18 +715,22 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
case 14: case 14:
// EPICS should already prevent this issue in the first place, // EPICS should already prevent this issue in the first place,
// since it contains the user limits // 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 " "Move command exceeds hardware limits (P%2.2d01 = %d). Please "
"call the support.", "call the support.",
axisNo_, error); axisNo_, error);
pl_status = setStringParam(pC_->motorMessageText_, command); pl_status = setStringParam(pC_->motorMessageText_, userMessage);
if (pl_status != asynSuccess) { if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__, axisNo_, __PRETTY_FUNCTION__,
@ -689,15 +740,22 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
poll_status = asynError; poll_status = asynError;
break; break;
default: 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( if (pC_->msgPrintControl_.shouldBePrinted(keyError, true,
pC_->motorMessageText_, pC_->pasynUserSelf)) {
"Unknown error P%2.2d01 = %d. Please call the support."); 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) { if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_", return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__, axisNo_, __PRETTY_FUNCTION__,
@ -708,6 +766,10 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
break; break;
} }
if (resetError) {
pC_->msgPrintControl_.resetCount(keyError);
}
// Update the parameter library // Update the parameter library
if (error != 0) { if (error != 0) {
pl_status = setIntegerParam(pC_->motorStatusProblem_, true); pl_status = setIntegerParam(pC_->motorStatusProblem_, true);

View File

@ -3,7 +3,7 @@
#include "sinqAxis.h" #include "sinqAxis.h"
// Forward declaration of the controller class to resolve the cyclic dependency // 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. // https://en.cppreference.com/w/cpp/language/class.
class turboPmacController; class turboPmacController;
@ -15,7 +15,8 @@ class turboPmacAxis : public sinqAxis {
* @param pController Pointer to the associated controller * @param pController Pointer to the associated controller
* @param axisNo Index of the axis * @param axisNo Index of the axis
*/ */
turboPmacAxis(turboPmacController *pController, int axisNo); turboPmacAxis(turboPmacController *pController, int axisNo,
bool initialize = true);
/** /**
* @brief Destroy the turboPmacAxis * @brief Destroy the turboPmacAxis
@ -74,7 +75,7 @@ class turboPmacAxis : public sinqAxis {
* The following steps are performed: * The following steps are performed:
* - Read out the motor status, motor position, velocity and acceleration * - Read out the motor status, motor position, velocity and acceleration
* from the MCU and store this information in the parameter library. * 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 * @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, turboPmacController::turboPmacController(const char *portName,
const char *ipPortConfigName, const char *ipPortConfigName,
int numAxes, double movingPollPeriod, int numAxes, double movingPollPeriod,
double idlePollPeriod, double idlePollPeriod,
double comTimeout) double comTimeout, int numExtraParams)
: sinqController( : sinqController(
portName, ipPortConfigName, numAxes, movingPollPeriod, idlePollPeriod, portName, ipPortConfigName, numAxes, movingPollPeriod, idlePollPeriod,
/* /*
@ -53,7 +41,7 @@ turboPmacController::turboPmacController(const char *portName,
- REREAD_ENCODER_POSITION - REREAD_ENCODER_POSITION
- READ_CONFIG - 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 If the axis does not exist or is not a Axis, a nullptr is returned and an
error is emitted. error is emitted.
*/ */
turboPmacAxis *turboPmacController::getAxis(asynUser *pasynUser) { turboPmacAxis *turboPmacController::getTurboPmacAxis(asynUser *pasynUser) {
asynMotorAxis *asynAxis = asynMotorController::getAxis(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. 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 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); asynMotorAxis *asynAxis = asynMotorController::getAxis(axisNo);
return turboPmacController::castToAxis(asynAxis); return dynamic_cast<turboPmacAxis *>(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;
} }
asynStatus turboPmacController::writeRead(int axisNo, const char *command, 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) { if (axis == nullptr) {
// We already did the error logging directly in getAxis // We already did the error logging directly in getAxis
return asynError; return asynError;
@ -273,10 +240,6 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
// +1 for the carriage return. // +1 for the carriage return.
const size_t fullComandLength = offset + commandLength + 1; 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 We use separated write and read commands here, not the combined writeRead
method, because the latter is actually a flushWriteRead (see 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, status = pasynOctetSyncIO->write(lowLevelPortUser_, fullCommand,
fullComandLength, comTimeout_, &nbytesOut); fullComandLength, comTimeout_, &nbytesOut);
msgPrintControlKey writeKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (status == asynTimeout) { if (status == asynTimeout) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, if (msgPrintControl_.shouldBePrinted(writeKey, true, pasynUserSelf)) {
"Controller \"%s\", axis %d => %s, line %d\nTimeout while " asynPrint(
"writing to the MCU\n", this->pasynUserSelf, ASYN_TRACE_ERROR,
portName, axisNo, __PRETTY_FUNCTION__, __LINE__); "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, timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText,
sizeof(drvMessageText)); sizeof(drvMessageText));
@ -321,23 +291,35 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
} }
} }
} else if (status != asynSuccess) { } else if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, if (msgPrintControl_.shouldBePrinted(writeKey, true, pasynUserSelf)) {
"Controller \"%s\", axis %d => %s, line %d\nError %s while " asynPrint(
"writing to the controller\n", this->pasynUserSelf, ASYN_TRACE_ERROR,
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, "Controller \"%s\", axis %d => %s, line %d\nError %s while "
stringifyAsynStatus(status)); "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 // Read the response from the MCU buffer
status = pasynOctetSyncIO->read(lowLevelPortUser_, response, MAXBUF_, status = pasynOctetSyncIO->read(lowLevelPortUser_, response, MAXBUF_,
comTimeout_, &nbytesIn, &eomReason); comTimeout_, &nbytesIn, &eomReason);
msgPrintControlKey readKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (status == asynTimeout) { if (status == asynTimeout) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, if (msgPrintControl_.shouldBePrinted(readKey, true, pasynUserSelf)) {
"Controller \"%s\", axis %d => %s, line %d\nTimeout while " asynPrint(
"reading from the MCU\n", this->pasynUserSelf, ASYN_TRACE_ERROR,
portName, axisNo, __PRETTY_FUNCTION__, __LINE__); "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 // Add this event to the back of the timeout event counter
timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText, timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText,
@ -360,11 +342,16 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
} }
} }
} else if (status != asynSuccess) { } else if (status != asynSuccess) {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR, if (msgPrintControl_.shouldBePrinted(readKey, true, pasynUserSelf)) {
"Controller \"%s\", axis %d => %s, line %d\nError %s while " asynPrint(
"reading from the controller\n", this->pasynUserSelf, ASYN_TRACE_ERROR,
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, "Controller \"%s\", axis %d => %s, line %d\nError %s while "
stringifyAsynStatus(status)); "reading from the controller.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status), msgPrintControl_.getSuffix());
}
} else {
msgPrintControl_.resetCount(readKey);
} }
if (timeoutStatus == asynError) { 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 // The message should only ever terminate due to reason 2
msgPrintControlKey terminateKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (eomReason != 2) { if (eomReason != 2) {
status = asynError; status = asynError;
@ -379,10 +368,16 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
"Terminated message due to reason %d (should be 2).", "Terminated message due to reason %d (should be 2).",
eomReason); eomReason);
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER, if (msgPrintControl_.shouldBePrinted(terminateKey, true,
"Controller \"%s\", axis %d => %s, line %d\nMessage " pasynUserSelf)) {
"terminated due to reason %i\n", asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, eomReason); "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++; numReceivedResponses++;
} }
} }
msgPrintControlKey numResponsesKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (numExpectedResponses != numReceivedResponses) { if (numExpectedResponses != numReceivedResponses) {
adjustResponseForPrint(modResponse, response, MAXBUF_); adjustResponseForPrint(modResponse, response, MAXBUF_);
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nUnexpected " if (msgPrintControl_.shouldBePrinted(numResponsesKey, true,
"response '%s' (carriage returns are replaced with spaces) " pasynUserSelf)) {
"for command %s\n", asynPrint(
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, modResponse, this->pasynUserSelf, ASYN_TRACE_ERROR,
command); "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), snprintf(drvMessageText, sizeof(drvMessageText),
"Received unexpected response '%s' (carriage returns " "Received unexpected response '%s' (carriage returns "
"are replaced with spaces) for command %s. " "are replaced with spaces) for command %s. "
"Please call the support", "Please call the support",
modResponse, command); modResponse, command);
status = asynError; status = asynError;
} else {
msgPrintControl_.resetCount(numResponsesKey);
} }
// Create custom error messages for different failure modes, if no error // 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) // Log the overall status (communication successfull or not)
if (status == asynSuccess) { 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); paramLibStatus = axis->setIntegerParam(this->motorStatusCommsError_, 0);
} else { } else {
asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR, // Check if the axis already is in an error communication mode. If
"Controller \"%s\", axis %d => %s, line %d\nCommunication " // it is not, upstream the error. This is done to avoid "flooding"
"failed for command %s (%s)\n", // the user with different error messages if more than one error
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, fullCommand, // ocurred before an error-free communication
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
paramLibStatus = paramLibStatus =
getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem); getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem);
if (paramLibStatus != asynSuccess) { if (paramLibStatus != asynSuccess) {
@ -494,11 +488,7 @@ asynStatus turboPmacController::writeInt32(asynUser *pasynUser,
// ===================================================================== // =====================================================================
turboPmacAxis *axis = getAxis(pasynUser); turboPmacAxis *axis = getTurboPmacAxis(pasynUser);
if (axis == nullptr) {
// We already did the error logging directly in getAxis
return asynError;
}
// Handle custom PVs // Handle custom PVs
if (function == rereadEncoderPosition_) { 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 // PMACs can be disabled
if (pasynUser->reason == motorCanDisable_) { if (pasynUser->reason == motorCanDisable_) {
*value = 1; *value = 1;
return asynSuccess; return asynSuccess;
} else { } 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/motor/blob/master/motorApp/MotorSrc/asynMotorController.cpp
https://github.com/epics-modules/asyn/blob/master/asyn/asynPortDriver/asynPortDriver.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 The created object is registered in EPICS in its constructor and can
be "leaked" here. safely be "leaked" here.
*/ */
#pragma GCC diagnostic ignored "-Wunused-but-set-variable" #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-variable" #pragma GCC diagnostic ignored "-Wunused-variable"
@ -582,17 +573,17 @@ static const iocshArg CreateControllerArg5 = {"Communication timeout (s)",
static const iocshArg *const CreateControllerArgs[] = { static const iocshArg *const CreateControllerArgs[] = {
&CreateControllerArg0, &CreateControllerArg1, &CreateControllerArg2, &CreateControllerArg0, &CreateControllerArg1, &CreateControllerArg2,
&CreateControllerArg3, &CreateControllerArg4, &CreateControllerArg5}; &CreateControllerArg3, &CreateControllerArg4, &CreateControllerArg5};
static const iocshFuncDef configturboPmacCreateController = { static const iocshFuncDef configTurboPmacCreateController = {
"turboPmacController", 6, CreateControllerArgs}; "turboPmacController", 6, CreateControllerArgs};
static void configTurboPmacCreateControllerCallFunc(const iocshArgBuf *args) { static void configTurboPmacCreateControllerCallFunc(const iocshArgBuf *args) {
turboPmacCreateController(args[0].sval, args[1].sval, args[2].ival, turboPmacCreateController(args[0].sval, args[1].sval, args[2].ival,
args[3].dval, args[4].dval, args[5].dval); args[3].dval, args[4].dval, args[5].dval);
} }
// This function is made known to EPICS in turboPmac.dbd and is called by EPICS // This function is made known to EPICS in turboPmac.dbd and is called by
// in order to register both functions in the IOC shell // EPICS in order to register both functions in the IOC shell
static void turboPmacControllerRegister(void) { static void turboPmacControllerRegister(void) {
iocshRegister(&configturboPmacCreateController, iocshRegister(&configTurboPmacCreateController,
configTurboPmacCreateControllerCallFunc); configTurboPmacCreateControllerCallFunc);
} }
epicsExportRegistrar(turboPmacControllerRegister); epicsExportRegistrar(turboPmacControllerRegister);

View File

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