Applied various bugfixes to make this driver fully operational
This commit is contained in:
7
Makefile
7
Makefile
@ -10,8 +10,11 @@ ARCH_FILTER=RHEL%
|
||||
REQUIRED+=asynMotor
|
||||
REQUIRED+=sinqMotor
|
||||
|
||||
# Specify the version of asynMotor we want to build against
|
||||
asynMotor_VERSION=7.2.2
|
||||
|
||||
# Specify the version of sinqMotor we want to build against
|
||||
sinqMotor_VERSION=0.6.3
|
||||
sinqMotor_VERSION=0.7.0
|
||||
|
||||
# These headers allow to depend on this library for derived drivers.
|
||||
HEADERS += src/turboPmacAxis.h
|
||||
@ -27,4 +30,4 @@ TEMPLATES += db/turboPmac.db
|
||||
# This file registers the motor-specific functions in the IOC shell.
|
||||
DBDS += src/turboPmac.dbd
|
||||
|
||||
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result # -Werror
|
||||
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Wextra -Werror # -Wpedantic // Does not work because EPICS macros trigger warnings
|
||||
|
@ -1,4 +1,5 @@
|
||||
#---------------------------------------------
|
||||
# SINQ specific DB definitions
|
||||
#---------------------------------------------
|
||||
registrar(turboPmacRegister)
|
||||
registrar(turboPmacControllerRegister)
|
||||
registrar(turboPmacAxisRegister)
|
File diff suppressed because it is too large
Load Diff
@ -69,7 +69,7 @@ class turboPmacAxis : public sinqAxis {
|
||||
double max_velocity, double acceleration);
|
||||
|
||||
/**
|
||||
* @brief Implementation of the `atFirstPoll` function from sinqAxis.
|
||||
* @brief Readout of some values from the controller at IOC startup
|
||||
*
|
||||
* The following steps are performed:
|
||||
* - Read out the motor status, motor position, velocity and acceleration
|
||||
@ -78,7 +78,7 @@ class turboPmacAxis : public sinqAxis {
|
||||
*
|
||||
* @return asynStatus
|
||||
*/
|
||||
asynStatus atFirstPoll();
|
||||
asynStatus init();
|
||||
|
||||
/**
|
||||
* @brief Enable / disable the axis.
|
||||
@ -106,7 +106,6 @@ class turboPmacAxis : public sinqAxis {
|
||||
protected:
|
||||
turboPmacController *pC_;
|
||||
|
||||
bool initial_poll_;
|
||||
bool waitForHandshake_;
|
||||
time_t timeAtHandshake_;
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "turboPmacAxis.h"
|
||||
#include <epicsExport.h>
|
||||
#include <errlog.h>
|
||||
#include <initHooks.h>
|
||||
#include <iocsh.h>
|
||||
#include <netinet/in.h>
|
||||
#include <registryFunction.h>
|
||||
@ -63,6 +64,10 @@ turboPmacController::turboPmacController(const char *portName,
|
||||
lowLevelPortUser_ = nullptr;
|
||||
comTimeout_ = comTimeout;
|
||||
|
||||
// Maximum allowed number of subsequent timeouts before the user is
|
||||
// informed.
|
||||
maxSubsequentTimeouts_ = 10;
|
||||
|
||||
// =========================================================================;
|
||||
|
||||
/*
|
||||
@ -71,10 +76,9 @@ turboPmacController::turboPmacController(const char *portName,
|
||||
*/
|
||||
pasynOctetSyncIO->connect(ipPortConfigName, 0, &lowLevelPortUser_, NULL);
|
||||
if (status != asynSuccess || lowLevelPortUser_ == nullptr) {
|
||||
errlogPrintf(
|
||||
"%s => line %d:\nFATAL ERROR (cannot connect to MCU controller).\n"
|
||||
"Terminating IOC",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
errlogPrintf("Controller \"%s\" => %s, line %d\nFATAL ERROR "
|
||||
"(cannot connect to MCU controller).\nTerminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
@ -85,18 +89,20 @@ turboPmacController::turboPmacController(const char *portName,
|
||||
&rereadEncoderPosition_);
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
|
||||
"with %s).\nTerminating IOC",
|
||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
||||
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
|
||||
"parameter failed with %s).\nTerminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
status = createParam("READ_CONFIG", asynParamInt32, &readConfig_);
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
|
||||
"with %s).\nTerminating IOC",
|
||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
||||
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
|
||||
"parameter failed with %s).\nTerminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status));
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
@ -113,20 +119,22 @@ turboPmacController::turboPmacController(const char *portName,
|
||||
lowLevelPortUser_, message_from_device, strlen(message_from_device));
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nFATAL ERROR (setting input EOS failed "
|
||||
"with %s).\nTerminating IOC",
|
||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
||||
"Controller \"%s\" => %s, line %d\nFATAL ERROR "
|
||||
"(setting input EOS failed with %s).\nTerminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status));
|
||||
pasynOctetSyncIO->disconnect(lowLevelPortUser_);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
status = callParamCallbacks();
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(
|
||||
this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nFATAL ERROR (executing ParamLib callbacks failed "
|
||||
"with %s).\nTerminating IOC",
|
||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\" => %s, line %d\nFATAL ERROR "
|
||||
"(executing ParamLib callbacks failed "
|
||||
"with %s).\nTerminating IOC",
|
||||
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||
stringifyAsynStatus(status));
|
||||
pasynOctetSyncIO->disconnect(lowLevelPortUser_);
|
||||
exit(-1);
|
||||
}
|
||||
@ -165,8 +173,9 @@ turboPmacAxis *turboPmacController::castToAxis(asynMotorAxis *asynAxis) {
|
||||
turboPmacAxis *axis = dynamic_cast<turboPmacAxis *>(asynAxis);
|
||||
if (axis == nullptr) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nAxis %d is not an instance of turboPmacAxis",
|
||||
__PRETTY_FUNCTION__, __LINE__, axis->axisNo_);
|
||||
"Controller \"%s\", axis %d => %s, line %d\nAxis is not "
|
||||
"an instance of turboPmacAxis",
|
||||
portName, axis->axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
return axis;
|
||||
}
|
||||
@ -177,7 +186,8 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
|
||||
// Definition of local variables.
|
||||
asynStatus status = asynSuccess;
|
||||
asynStatus pl_status = asynSuccess;
|
||||
asynStatus paramLibStatus = asynSuccess;
|
||||
asynStatus timeoutStatus = asynSuccess;
|
||||
char fullCommand[MAXBUF_] = {0};
|
||||
char drvMessageText[MAXBUF_] = {0};
|
||||
char modResponse[MAXBUF_] = {0};
|
||||
@ -226,6 +236,11 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
value [Actual message] It is not necessary to append a terminator, since
|
||||
this protocol encodes the message length at the beginning. See Turbo PMAC
|
||||
User Manual, page 418 in VR_PMAC_GETRESPONSE
|
||||
x0D (ASCII value of carriage return) -> The controller needs a carriage
|
||||
return at the end of a "send" command (a command were we transmit data via
|
||||
=). For "request" commands (e.g. read status or position), this is not
|
||||
necessary, but it doesn't hurt either, therefore we always add a carriage
|
||||
return.
|
||||
|
||||
The message has to be build manually into the buffer fullCommand, since it
|
||||
contains NULL terminators in its middle, therefore the string manipulation
|
||||
@ -242,46 +257,25 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
|
||||
// The size of size_t is platform dependant (pointers-sized), while htons
|
||||
// needs an unsigned int. The byte order is then converted from host to
|
||||
// network order.
|
||||
u_int16_t len = htons(static_cast<u_int16_t>(commandLength));
|
||||
// network order. The offset "+1" is for the carriage return.
|
||||
u_int16_t len = htons(static_cast<u_int16_t>(commandLength + 1));
|
||||
|
||||
// Split up into the upper and the lower byte
|
||||
fullCommand[7] = (char)(len >> 8); // Shift the 8 higher bits to the right
|
||||
fullCommand[8] = (char)(len & 0xFF); // Mask the higher bits
|
||||
|
||||
// Write the actual command behind the protocol
|
||||
for (int i = 0; i < commandLength; i++) {
|
||||
for (size_t i = 0; i < commandLength; i++) {
|
||||
fullCommand[i + offset] = command[i];
|
||||
}
|
||||
fullCommand[offset + commandLength] = '\x0D';
|
||||
|
||||
// +1 for the carriage return.
|
||||
const size_t fullComandLength = offset + commandLength + 1;
|
||||
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
|
||||
"%s => line %d:\nSending command %s", __PRETTY_FUNCTION__,
|
||||
__LINE__, fullCommand);
|
||||
|
||||
// TEST CODE
|
||||
/*
|
||||
Check if there is data on the MCU which is waiting to be read
|
||||
TBD: Flush if there is something?
|
||||
*/
|
||||
u_int16_t val = htons(2);
|
||||
char readReadyCommand[MAXBUF_] = {0};
|
||||
char readReadyResponse[MAXBUF_] = {0};
|
||||
readReadyCommand[0] = '\xC0';
|
||||
readReadyCommand[1] = '\xC2';
|
||||
readReadyCommand[4] = (char)(val >> 8);
|
||||
readReadyCommand[5] = (char)(val & 0xFF);
|
||||
status = pasynOctetSyncIO->write(lowLevelPortUser_, readReadyCommand, 6,
|
||||
comTimeout_, &nbytesOut);
|
||||
|
||||
status =
|
||||
pasynOctetSyncIO->read(lowLevelPortUser_, readReadyResponse, MAXBUF_,
|
||||
comTimeout_, &nbytesIn, &eomReason);
|
||||
|
||||
if (readReadyResponse[1] != 0) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nBytes: %x %x\n", __PRETTY_FUNCTION__,
|
||||
__LINE__, readReadyResponse[0], readReadyResponse[1]);
|
||||
}
|
||||
"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
|
||||
@ -291,60 +285,105 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
The flush itself reads repeatedly from the MCU until no messages are there
|
||||
anymore. (The Diamond Light Source driver first send a PMAC flush command
|
||||
and then does the same as the asyn flush). We don't want this behaviour.
|
||||
(https://www.slac.stanford.edu/grp/lcls/controls/global/doc/epics-modules/R3-14-12/asyn/asyn-R4-18-lcls2/asyn/interfaces/asynOctetBase.c):
|
||||
(https://www.slac.stanford.edu/grp/lcls/controls/global/doc/epics-modules/R3-14-12/asyn/asyn-R4-18-lcls2/asyn/interfaces/asynOctetBase.c)
|
||||
|
||||
If a timeout occurs during writing or reading, inform the user that we're
|
||||
trying to reconnect. If the problem persists, ask them to call the support
|
||||
*/
|
||||
|
||||
// Send out the command
|
||||
status = pasynOctetSyncIO->write(lowLevelPortUser_, fullCommand,
|
||||
commandLength + offset, comTimeout_,
|
||||
&nbytesOut);
|
||||
if (status != asynSuccess) {
|
||||
fullComandLength, comTimeout_, &nbytesOut);
|
||||
|
||||
if (status == asynTimeout) {
|
||||
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nError %s while writing to the controller\n",
|
||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
||||
pl_status = setStringParam(
|
||||
motorMessageText_, "Communication timeout between IOC and "
|
||||
"motor controller. Please call the support.");
|
||||
if (pl_status != asynSuccess) {
|
||||
return paramLibAccessFailed(pl_status, "motorMessageText_",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
"Controller \"%s\", axis %d => %s, line %d\nTimeout while "
|
||||
"writing to the MCU\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
|
||||
timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText,
|
||||
sizeof(drvMessageText));
|
||||
|
||||
int timeoutCounter = 0;
|
||||
while (1) {
|
||||
checkMaxSubsequentTimeouts(timeoutCounter, axis);
|
||||
timeoutCounter += 1;
|
||||
|
||||
status = pasynOctetSyncIO->write(lowLevelPortUser_, fullCommand,
|
||||
fullComandLength, comTimeout_,
|
||||
&nbytesOut);
|
||||
if (status != asynTimeout) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
"%d\nReconnected after write timeout\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} 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));
|
||||
}
|
||||
|
||||
// Read the response from the MCU buffer
|
||||
status = pasynOctetSyncIO->read(lowLevelPortUser_, response, MAXBUF_,
|
||||
comTimeout_, &nbytesIn, &eomReason);
|
||||
|
||||
if (status != asynSuccess) {
|
||||
asynPrint(
|
||||
this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nError %s while reading from the controller\n ",
|
||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
||||
pl_status = setStringParam(
|
||||
motorMessageText_, "Communication timeout between IOC and "
|
||||
"motor controller. Please call the support.");
|
||||
if (pl_status != asynSuccess) {
|
||||
return paramLibAccessFailed(pl_status, "motorMessageText_",
|
||||
__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__);
|
||||
|
||||
// Add this event to the back of the timeout event counter
|
||||
timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText,
|
||||
sizeof(drvMessageText));
|
||||
|
||||
int timeoutCounter = 0;
|
||||
while (1) {
|
||||
checkMaxSubsequentTimeouts(timeoutCounter, axis);
|
||||
timeoutCounter += 1;
|
||||
|
||||
status =
|
||||
pasynOctetSyncIO->read(lowLevelPortUser_, response, MAXBUF_,
|
||||
comTimeout_, &nbytesIn, &eomReason);
|
||||
if (status != asynTimeout) {
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"Controller \"%s\", axis %d => %s, line "
|
||||
"%d\nReconnected after read timeout\n",
|
||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} 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));
|
||||
}
|
||||
|
||||
// // TEST CODE
|
||||
// if (response == lastResponse) {
|
||||
// asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
// "%s => line %d:\nBytes: %x %x\n", __PRETTY_FUNCTION__,
|
||||
// __LINE__, readReadyResponse[0], readReadyResponse[1]);
|
||||
// } else {
|
||||
// for (int i = 0; i < MAXBUF_; i++) {
|
||||
// lastResponse[i] = response[i];
|
||||
// }
|
||||
// }
|
||||
if (timeoutStatus == asynError) {
|
||||
status = asynError;
|
||||
}
|
||||
|
||||
// TBD: The message should only ever terminate due to reason 2 -> Should we
|
||||
// indicate an error otherwise?
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
|
||||
"%s => line %d:\nMessage terminated due to reason %i\n",
|
||||
__PRETTY_FUNCTION__, __LINE__, eomReason);
|
||||
// The message should only ever terminate due to reason 2
|
||||
if (eomReason != 2) {
|
||||
status = asynError;
|
||||
|
||||
snprintf(drvMessageText, sizeof(drvMessageText),
|
||||
"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);
|
||||
}
|
||||
|
||||
/*
|
||||
Calculate the number of received responses by counting the number of
|
||||
@ -358,23 +397,21 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
if (numExpectedResponses != numReceivedResponses) {
|
||||
adjustResponseForPrint(modResponse, response, MAXBUF_);
|
||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||
"%s => line %d:\nUnexpected response '%s' (carriage "
|
||||
"returns are replaced with spaces) for command %s\n",
|
||||
__PRETTY_FUNCTION__, __LINE__, modResponse, command);
|
||||
"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);
|
||||
snprintf(drvMessageText, sizeof(drvMessageText),
|
||||
"Received unexpected response '%s' (carriage returns "
|
||||
"are replaced with spaces) for command %s. "
|
||||
"Please call the support",
|
||||
modResponse, command);
|
||||
pl_status = setStringParam(motorMessageText_, drvMessageText);
|
||||
if (pl_status != asynSuccess) {
|
||||
return paramLibAccessFailed(pl_status, "motorMessageText_",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
status = asynError;
|
||||
}
|
||||
|
||||
// Create custom error messages for different failure modes
|
||||
// Create custom error messages for different failure modes, if no error
|
||||
// message has been set yet
|
||||
if (strlen(drvMessageText) == 0) {
|
||||
switch (status) {
|
||||
case asynSuccess:
|
||||
@ -402,43 +439,48 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
||||
if (status == asynSuccess) {
|
||||
|
||||
asynPrint(lowLevelPortUser_, ASYN_TRACEIO_DRIVER,
|
||||
"%s => line %d:\nDevice response: %s (_ are "
|
||||
"carriage returns)\n",
|
||||
__PRETTY_FUNCTION__, __LINE__, modResponse);
|
||||
pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 0);
|
||||
"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,
|
||||
"%s => line %d:\nCommunication failed for command %s (%s)\n",
|
||||
__PRETTY_FUNCTION__, __LINE__, fullCommand,
|
||||
"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
|
||||
pl_status =
|
||||
paramLibStatus =
|
||||
getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem);
|
||||
if (pl_status != asynSuccess) {
|
||||
return paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
if (paramLibStatus != asynSuccess) {
|
||||
return paramLibAccessFailed(paramLibStatus, "motorStatusProblem_",
|
||||
axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
if (motorStatusProblem == 0) {
|
||||
pl_status = axis->setStringParam(motorMessageText_, drvMessageText);
|
||||
if (pl_status != asynSuccess) {
|
||||
return paramLibAccessFailed(pl_status, "motorMessageText_",
|
||||
paramLibStatus =
|
||||
axis->setStringParam(motorMessageText_, drvMessageText);
|
||||
if (paramLibStatus != asynSuccess) {
|
||||
return paramLibAccessFailed(paramLibStatus, "motorMessageText_",
|
||||
axisNo, __PRETTY_FUNCTION__,
|
||||
__LINE__);
|
||||
}
|
||||
|
||||
paramLibStatus = axis->setIntegerParam(motorStatusProblem_, 1);
|
||||
if (paramLibStatus != asynSuccess) {
|
||||
return paramLibAccessFailed(paramLibStatus,
|
||||
"motorStatusProblem", axisNo,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
pl_status = axis->setIntegerParam(motorStatusProblem_, 1);
|
||||
if (pl_status != asynSuccess) {
|
||||
return paramLibAccessFailed(pl_status, "motorStatusProblem",
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
|
||||
pl_status = axis->setIntegerParam(motorStatusProblem_, 1);
|
||||
if (pl_status != asynSuccess) {
|
||||
return paramLibAccessFailed(pl_status, "motorStatusCommsError_",
|
||||
paramLibStatus = axis->setIntegerParam(motorStatusProblem_, 1);
|
||||
if (paramLibStatus != asynSuccess) {
|
||||
return paramLibAccessFailed(paramLibStatus,
|
||||
"motorStatusCommsError_", axisNo,
|
||||
__PRETTY_FUNCTION__, __LINE__);
|
||||
}
|
||||
}
|
||||
@ -462,7 +504,7 @@ asynStatus turboPmacController::writeInt32(asynUser *pasynUser,
|
||||
if (function == rereadEncoderPosition_) {
|
||||
return axis->rereadEncoder();
|
||||
} else if (function == readConfig_) {
|
||||
return axis->atFirstPoll();
|
||||
return axis->init();
|
||||
} else {
|
||||
return sinqController::writeInt32(pasynUser, value);
|
||||
}
|
||||
@ -519,80 +561,6 @@ asynStatus turboPmacCreateController(const char *portName,
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
C wrapper for the axis constructor. Please refer to the turboPmacAxis
|
||||
constructor documentation. The controller is read from the portName.
|
||||
*/
|
||||
asynStatus turboPmacCreateAxis(const char *portName, int axis) {
|
||||
turboPmacAxis *pAxis;
|
||||
|
||||
/*
|
||||
findAsynPortDriver is a asyn library FFI function which uses the C ABI.
|
||||
Therefore it returns a void pointer instead of e.g. a pointer to a
|
||||
superclass of the controller such as asynPortDriver. Type-safe upcasting
|
||||
via dynamic_cast is therefore not possible directly. However, we do know
|
||||
that the void pointer is either a pointer to asynPortDriver (if a driver
|
||||
with the specified name exists) or a nullptr. Therefore, we first do a
|
||||
nullptr check, then a cast to asynPortDriver and lastly a (typesafe)
|
||||
dynamic_upcast to Controller
|
||||
https://stackoverflow.com/questions/70906749/is-there-a-safe-way-to-cast-void-to-class-pointer-in-c
|
||||
*/
|
||||
void *ptr = findAsynPortDriver(portName);
|
||||
if (ptr == nullptr) {
|
||||
/*
|
||||
We can't use asynPrint here since this macro would require us
|
||||
to get a lowLevelPortUser_ from a pointer to an asynPortDriver.
|
||||
However, the given pointer is a nullptr and therefore doesn't
|
||||
have a lowLevelPortUser_! printf is an EPICS alternative which
|
||||
works w/o that, but doesn't offer the comfort provided
|
||||
by the asynTrace-facility
|
||||
*/
|
||||
errlogPrintf("%s => line %d:\nPort %s not found.", __PRETTY_FUNCTION__,
|
||||
__LINE__, portName);
|
||||
return asynError;
|
||||
}
|
||||
// Unsafe cast of the pointer to an asynPortDriver
|
||||
asynPortDriver *apd = (asynPortDriver *)(ptr);
|
||||
|
||||
// Safe downcast
|
||||
turboPmacController *pC = dynamic_cast<turboPmacController *>(apd);
|
||||
if (pC == nullptr) {
|
||||
errlogPrintf("%s => line %d:\ncontroller on port %s is not a "
|
||||
"turboPmacController.",
|
||||
__PRETTY_FUNCTION__, __LINE__, portName);
|
||||
return asynError;
|
||||
}
|
||||
|
||||
// Prevent manipulation of the controller from other threads while we
|
||||
// create the new axis.
|
||||
pC->lock();
|
||||
|
||||
/*
|
||||
We create a new instance of the axis, using the "new" keyword to
|
||||
allocate it on the heap while avoiding RAII.
|
||||
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.
|
||||
*/
|
||||
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
|
||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
||||
pAxis = new turboPmacAxis(pC, axis);
|
||||
|
||||
// Allow manipulation of the controller again
|
||||
pC->unlock();
|
||||
return asynSuccess;
|
||||
}
|
||||
|
||||
/*
|
||||
This is boilerplate code which is used to make the FFI functions
|
||||
CreateController and CreateAxis "known" to the IOC shell (iocsh).
|
||||
*/
|
||||
|
||||
#ifdef vxWorks
|
||||
#else
|
||||
|
||||
/*
|
||||
Define name and type of the arguments for the CreateController function
|
||||
in the iocsh. This is done by creating structs with the argument names and
|
||||
@ -616,36 +584,17 @@ static const iocshArg *const CreateControllerArgs[] = {
|
||||
&CreateControllerArg3, &CreateControllerArg4, &CreateControllerArg5};
|
||||
static const iocshFuncDef configturboPmacCreateController = {
|
||||
"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,
|
||||
args[3].dval, args[4].dval, args[5].dval);
|
||||
}
|
||||
|
||||
/*
|
||||
Same procedure as for the CreateController function, but for the axis
|
||||
itself.
|
||||
*/
|
||||
static const iocshArg CreateAxisArg0 = {"Controller name (e.g. mcu1)",
|
||||
iocshArgString};
|
||||
static const iocshArg CreateAxisArg1 = {"Axis number", iocshArgInt};
|
||||
static const iocshArg *const CreateAxisArgs[] = {&CreateAxisArg0,
|
||||
&CreateAxisArg1};
|
||||
static const iocshFuncDef configturboPmacCreateAxis = {"turboPmacAxis", 2,
|
||||
CreateAxisArgs};
|
||||
static void configturboPmacCreateAxisCallFunc(const iocshArgBuf *args) {
|
||||
turboPmacCreateAxis(args[0].sval, args[1].ival);
|
||||
}
|
||||
|
||||
// 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 turboPmacRegister(void) {
|
||||
static void turboPmacControllerRegister(void) {
|
||||
iocshRegister(&configturboPmacCreateController,
|
||||
configturboPmacCreateControllerCallFunc);
|
||||
iocshRegister(&configturboPmacCreateAxis,
|
||||
configturboPmacCreateAxisCallFunc);
|
||||
configTurboPmacCreateControllerCallFunc);
|
||||
}
|
||||
epicsExportRegistrar(turboPmacRegister);
|
||||
|
||||
#endif
|
||||
epicsExportRegistrar(turboPmacControllerRegister);
|
||||
|
||||
} // extern "C"
|
||||
|
@ -1,16 +1,16 @@
|
||||
/********************************************
|
||||
* turboPmacController.h
|
||||
*
|
||||
* PMAC V3 controller driver based on the asynMotorController class
|
||||
* Turbo PMAC controller driver based on the asynMotorController class
|
||||
*
|
||||
* Stefan Mathis, September 2024
|
||||
********************************************/
|
||||
|
||||
#ifndef turboPmacController_H
|
||||
#define turboPmacController_H
|
||||
#include "turboPmacAxis.h"
|
||||
#include "sinqAxis.h"
|
||||
#include "sinqController.h"
|
||||
#include "turboPmacAxis.h"
|
||||
|
||||
class turboPmacController : public sinqController {
|
||||
|
||||
@ -28,14 +28,15 @@ class turboPmacController : public sinqController {
|
||||
time (in seconds) has passed, then it declares a timeout.
|
||||
*/
|
||||
turboPmacController(const char *portName, const char *ipPortConfigName,
|
||||
int numAxes, double movingPollPeriod,
|
||||
double idlePollPeriod, double comTimeout);
|
||||
int numAxes, double movingPollPeriod,
|
||||
double idlePollPeriod, double comTimeout);
|
||||
|
||||
/**
|
||||
* @brief Get the axis object
|
||||
*
|
||||
* @param pasynUser Specify the axis via the asynUser
|
||||
* @return turboPmacAxis* If no axis could be found, this is a nullptr
|
||||
* @return turboPmacAxis* If no axis could be found, this is a
|
||||
* nullptr
|
||||
*/
|
||||
turboPmacAxis *getAxis(asynUser *pasynUser);
|
||||
|
||||
@ -43,7 +44,8 @@ class turboPmacController : public sinqController {
|
||||
* @brief Get the axis object
|
||||
*
|
||||
* @param axisNo Specify the axis via its index
|
||||
* @return turboPmacAxis* If no axis could be found, this is a nullptr
|
||||
* @return turboPmacAxis* If no axis could be found, this is a
|
||||
* nullptr
|
||||
*/
|
||||
turboPmacAxis *getAxis(int axisNo);
|
||||
|
||||
@ -81,8 +83,8 @@ class turboPmacController : public sinqController {
|
||||
int numExpectedResponses);
|
||||
|
||||
/**
|
||||
* @brief Save cast of the given asynAxis pointer to a turboPmacAxis pointer.
|
||||
* If the cast fails, this function returns a nullptr.
|
||||
* @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*
|
||||
@ -119,7 +121,7 @@ class turboPmacController : public sinqController {
|
||||
static const uint32_t MAXBUF_ = 200;
|
||||
|
||||
/*
|
||||
Stores the constructor input comTimeout
|
||||
Timeout for the communication process in seconds
|
||||
*/
|
||||
double comTimeout_;
|
||||
|
||||
@ -133,6 +135,7 @@ class turboPmacController : public sinqController {
|
||||
|
||||
friend class turboPmacAxis;
|
||||
};
|
||||
#define NUM_turboPmac_DRIVER_PARAMS (&LAST_turboPmac_PARAM - &FIRST_turboPmac_PARAM + 1)
|
||||
#define NUM_turboPmac_DRIVER_PARAMS \
|
||||
(&LAST_turboPmac_PARAM - &FIRST_turboPmac_PARAM + 1)
|
||||
|
||||
#endif /* turboPmacController_H */
|
||||
|
Reference in New Issue
Block a user