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+=asynMotor
|
||||||
REQUIRED+=sinqMotor
|
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
|
# 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.
|
# These headers allow to depend on this library for derived drivers.
|
||||||
HEADERS += src/turboPmacAxis.h
|
HEADERS += src/turboPmacAxis.h
|
||||||
@ -27,4 +30,4 @@ TEMPLATES += db/turboPmac.db
|
|||||||
# This file registers the motor-specific functions in the IOC shell.
|
# This file registers the motor-specific functions in the IOC shell.
|
||||||
DBDS += src/turboPmac.dbd
|
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
|
# 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);
|
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:
|
* 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
|
||||||
@ -78,7 +78,7 @@ class turboPmacAxis : public sinqAxis {
|
|||||||
*
|
*
|
||||||
* @return asynStatus
|
* @return asynStatus
|
||||||
*/
|
*/
|
||||||
asynStatus atFirstPoll();
|
asynStatus init();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Enable / disable the axis.
|
* @brief Enable / disable the axis.
|
||||||
@ -106,7 +106,6 @@ class turboPmacAxis : public sinqAxis {
|
|||||||
protected:
|
protected:
|
||||||
turboPmacController *pC_;
|
turboPmacController *pC_;
|
||||||
|
|
||||||
bool initial_poll_;
|
|
||||||
bool waitForHandshake_;
|
bool waitForHandshake_;
|
||||||
time_t timeAtHandshake_;
|
time_t timeAtHandshake_;
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "turboPmacAxis.h"
|
#include "turboPmacAxis.h"
|
||||||
#include <epicsExport.h>
|
#include <epicsExport.h>
|
||||||
#include <errlog.h>
|
#include <errlog.h>
|
||||||
|
#include <initHooks.h>
|
||||||
#include <iocsh.h>
|
#include <iocsh.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <registryFunction.h>
|
#include <registryFunction.h>
|
||||||
@ -63,6 +64,10 @@ turboPmacController::turboPmacController(const char *portName,
|
|||||||
lowLevelPortUser_ = nullptr;
|
lowLevelPortUser_ = nullptr;
|
||||||
comTimeout_ = comTimeout;
|
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);
|
pasynOctetSyncIO->connect(ipPortConfigName, 0, &lowLevelPortUser_, NULL);
|
||||||
if (status != asynSuccess || lowLevelPortUser_ == nullptr) {
|
if (status != asynSuccess || lowLevelPortUser_ == nullptr) {
|
||||||
errlogPrintf(
|
errlogPrintf("Controller \"%s\" => %s, line %d\nFATAL ERROR "
|
||||||
"%s => line %d:\nFATAL ERROR (cannot connect to MCU controller).\n"
|
"(cannot connect to MCU controller).\nTerminating IOC",
|
||||||
"Terminating IOC",
|
portName, __PRETTY_FUNCTION__, __LINE__);
|
||||||
__PRETTY_FUNCTION__, __LINE__);
|
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,18 +89,20 @@ turboPmacController::turboPmacController(const char *portName,
|
|||||||
&rereadEncoderPosition_);
|
&rereadEncoderPosition_);
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||||
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
|
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
|
||||||
"with %s).\nTerminating IOC",
|
"parameter failed with %s).\nTerminating IOC",
|
||||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
stringifyAsynStatus(status));
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
status = createParam("READ_CONFIG", asynParamInt32, &readConfig_);
|
status = createParam("READ_CONFIG", asynParamInt32, &readConfig_);
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||||
"%s => line %d:\nFATAL ERROR (creating a parameter failed "
|
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
|
||||||
"with %s).\nTerminating IOC",
|
"parameter failed with %s).\nTerminating IOC",
|
||||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
stringifyAsynStatus(status));
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,20 +119,22 @@ turboPmacController::turboPmacController(const char *portName,
|
|||||||
lowLevelPortUser_, message_from_device, strlen(message_from_device));
|
lowLevelPortUser_, message_from_device, strlen(message_from_device));
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||||
"%s => line %d:\nFATAL ERROR (setting input EOS failed "
|
"Controller \"%s\" => %s, line %d\nFATAL ERROR "
|
||||||
"with %s).\nTerminating IOC",
|
"(setting input EOS failed with %s).\nTerminating IOC",
|
||||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
stringifyAsynStatus(status));
|
||||||
pasynOctetSyncIO->disconnect(lowLevelPortUser_);
|
pasynOctetSyncIO->disconnect(lowLevelPortUser_);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
status = callParamCallbacks();
|
status = callParamCallbacks();
|
||||||
if (status != asynSuccess) {
|
if (status != asynSuccess) {
|
||||||
asynPrint(
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||||
this->pasynUserSelf, ASYN_TRACE_ERROR,
|
"Controller \"%s\" => %s, line %d\nFATAL ERROR "
|
||||||
"%s => line %d:\nFATAL ERROR (executing ParamLib callbacks failed "
|
"(executing ParamLib callbacks failed "
|
||||||
"with %s).\nTerminating IOC",
|
"with %s).\nTerminating IOC",
|
||||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
portName, __PRETTY_FUNCTION__, __LINE__,
|
||||||
|
stringifyAsynStatus(status));
|
||||||
pasynOctetSyncIO->disconnect(lowLevelPortUser_);
|
pasynOctetSyncIO->disconnect(lowLevelPortUser_);
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
@ -165,8 +173,9 @@ turboPmacAxis *turboPmacController::castToAxis(asynMotorAxis *asynAxis) {
|
|||||||
turboPmacAxis *axis = dynamic_cast<turboPmacAxis *>(asynAxis);
|
turboPmacAxis *axis = dynamic_cast<turboPmacAxis *>(asynAxis);
|
||||||
if (axis == nullptr) {
|
if (axis == nullptr) {
|
||||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||||
"%s => line %d:\nAxis %d is not an instance of turboPmacAxis",
|
"Controller \"%s\", axis %d => %s, line %d\nAxis is not "
|
||||||
__PRETTY_FUNCTION__, __LINE__, axis->axisNo_);
|
"an instance of turboPmacAxis",
|
||||||
|
portName, axis->axisNo_, __PRETTY_FUNCTION__, __LINE__);
|
||||||
}
|
}
|
||||||
return axis;
|
return axis;
|
||||||
}
|
}
|
||||||
@ -177,7 +186,8 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
|||||||
|
|
||||||
// Definition of local variables.
|
// Definition of local variables.
|
||||||
asynStatus status = asynSuccess;
|
asynStatus status = asynSuccess;
|
||||||
asynStatus pl_status = asynSuccess;
|
asynStatus paramLibStatus = asynSuccess;
|
||||||
|
asynStatus timeoutStatus = asynSuccess;
|
||||||
char fullCommand[MAXBUF_] = {0};
|
char fullCommand[MAXBUF_] = {0};
|
||||||
char drvMessageText[MAXBUF_] = {0};
|
char drvMessageText[MAXBUF_] = {0};
|
||||||
char modResponse[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
|
value [Actual message] It is not necessary to append a terminator, since
|
||||||
this protocol encodes the message length at the beginning. See Turbo PMAC
|
this protocol encodes the message length at the beginning. See Turbo PMAC
|
||||||
User Manual, page 418 in VR_PMAC_GETRESPONSE
|
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
|
The message has to be build manually into the buffer fullCommand, since it
|
||||||
contains NULL terminators in its middle, therefore the string manipulation
|
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
|
// 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
|
// needs an unsigned int. The byte order is then converted from host to
|
||||||
// network order.
|
// network order. The offset "+1" is for the carriage return.
|
||||||
u_int16_t len = htons(static_cast<u_int16_t>(commandLength));
|
u_int16_t len = htons(static_cast<u_int16_t>(commandLength + 1));
|
||||||
|
|
||||||
// Split up into the upper and the lower byte
|
// Split up into the upper and the lower byte
|
||||||
fullCommand[7] = (char)(len >> 8); // Shift the 8 higher bits to the right
|
fullCommand[7] = (char)(len >> 8); // Shift the 8 higher bits to the right
|
||||||
fullCommand[8] = (char)(len & 0xFF); // Mask the higher bits
|
fullCommand[8] = (char)(len & 0xFF); // Mask the higher bits
|
||||||
|
|
||||||
// Write the actual command behind the protocol
|
// 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[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,
|
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
|
||||||
"%s => line %d:\nSending command %s", __PRETTY_FUNCTION__,
|
"Controller \"%s\", axis %d => %s, line %d\nSending command %s",
|
||||||
__LINE__, fullCommand);
|
portName, axisNo, __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]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We use separated write and read commands here, not the combined writeRead
|
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
|
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
|
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.
|
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,
|
status = pasynOctetSyncIO->write(lowLevelPortUser_, fullCommand,
|
||||||
commandLength + offset, comTimeout_,
|
fullComandLength, comTimeout_, &nbytesOut);
|
||||||
&nbytesOut);
|
|
||||||
if (status != asynSuccess) {
|
if (status == asynTimeout) {
|
||||||
|
|
||||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||||
"%s => line %d:\nError %s while writing to the controller\n",
|
"Controller \"%s\", axis %d => %s, line %d\nTimeout while "
|
||||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
"writing to the MCU\n",
|
||||||
pl_status = setStringParam(
|
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||||
motorMessageText_, "Communication timeout between IOC and "
|
|
||||||
"motor controller. Please call the support.");
|
timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText,
|
||||||
if (pl_status != asynSuccess) {
|
sizeof(drvMessageText));
|
||||||
return paramLibAccessFailed(pl_status, "motorMessageText_",
|
|
||||||
__PRETTY_FUNCTION__, __LINE__);
|
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
|
// 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);
|
||||||
|
|
||||||
if (status != asynSuccess) {
|
if (status == asynTimeout) {
|
||||||
asynPrint(
|
|
||||||
this->pasynUserSelf, ASYN_TRACE_ERROR,
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||||
"%s => line %d:\nError %s while reading from the controller\n ",
|
"Controller \"%s\", axis %d => %s, line %d\nTimeout while "
|
||||||
__PRETTY_FUNCTION__, __LINE__, stringifyAsynStatus(status));
|
"reading from the MCU\n",
|
||||||
pl_status = setStringParam(
|
portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||||
motorMessageText_, "Communication timeout between IOC and "
|
|
||||||
"motor controller. Please call the support.");
|
// Add this event to the back of the timeout event counter
|
||||||
if (pl_status != asynSuccess) {
|
timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText,
|
||||||
return paramLibAccessFailed(pl_status, "motorMessageText_",
|
sizeof(drvMessageText));
|
||||||
__PRETTY_FUNCTION__, __LINE__);
|
|
||||||
|
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 (timeoutStatus == asynError) {
|
||||||
// if (response == lastResponse) {
|
status = asynError;
|
||||||
// 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];
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TBD: The message should only ever terminate due to reason 2 -> Should we
|
// The message should only ever terminate due to reason 2
|
||||||
// indicate an error otherwise?
|
if (eomReason != 2) {
|
||||||
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
|
status = asynError;
|
||||||
"%s => line %d:\nMessage terminated due to reason %i\n",
|
|
||||||
__PRETTY_FUNCTION__, __LINE__, eomReason);
|
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
|
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) {
|
if (numExpectedResponses != numReceivedResponses) {
|
||||||
adjustResponseForPrint(modResponse, response, MAXBUF_);
|
adjustResponseForPrint(modResponse, response, MAXBUF_);
|
||||||
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
|
||||||
"%s => line %d:\nUnexpected response '%s' (carriage "
|
"Controller \"%s\", axis %d => %s, line %d\nUnexpected "
|
||||||
"returns are replaced with spaces) for command %s\n",
|
"response '%s' (carriage returns are replaced with spaces) "
|
||||||
__PRETTY_FUNCTION__, __LINE__, modResponse, command);
|
"for command %s\n",
|
||||||
|
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, modResponse,
|
||||||
|
command);
|
||||||
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);
|
||||||
pl_status = setStringParam(motorMessageText_, drvMessageText);
|
|
||||||
if (pl_status != asynSuccess) {
|
|
||||||
return paramLibAccessFailed(pl_status, "motorMessageText_",
|
|
||||||
__PRETTY_FUNCTION__, __LINE__);
|
|
||||||
}
|
|
||||||
status = asynError;
|
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) {
|
if (strlen(drvMessageText) == 0) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case asynSuccess:
|
case asynSuccess:
|
||||||
@ -402,43 +439,48 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
|||||||
if (status == asynSuccess) {
|
if (status == asynSuccess) {
|
||||||
|
|
||||||
asynPrint(lowLevelPortUser_, ASYN_TRACEIO_DRIVER,
|
asynPrint(lowLevelPortUser_, ASYN_TRACEIO_DRIVER,
|
||||||
"%s => line %d:\nDevice response: %s (_ are "
|
"Controller \"%s\", axis %d => %s, line %d\nDevice "
|
||||||
"carriage returns)\n",
|
"response: %s (carriage returns are replaced with spaces)\n",
|
||||||
__PRETTY_FUNCTION__, __LINE__, modResponse);
|
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, modResponse);
|
||||||
pl_status = axis->setIntegerParam(this->motorStatusCommsError_, 0);
|
paramLibStatus = axis->setIntegerParam(this->motorStatusCommsError_, 0);
|
||||||
} else {
|
} else {
|
||||||
asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR,
|
asynPrint(lowLevelPortUser_, ASYN_TRACE_ERROR,
|
||||||
"%s => line %d:\nCommunication failed for command %s (%s)\n",
|
"Controller \"%s\", axis %d => %s, line %d\nCommunication "
|
||||||
__PRETTY_FUNCTION__, __LINE__, fullCommand,
|
"failed for command %s (%s)\n",
|
||||||
|
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, fullCommand,
|
||||||
stringifyAsynStatus(status));
|
stringifyAsynStatus(status));
|
||||||
|
|
||||||
// Check if the axis already is in an error communication mode. If it is
|
// 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
|
// not, upstream the error. This is done to avoid "flooding" the user
|
||||||
// with different error messages if more than one error ocurred before
|
// with different error messages if more than one error ocurred before
|
||||||
// an error-free communication
|
// an error-free communication
|
||||||
pl_status =
|
paramLibStatus =
|
||||||
getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem);
|
getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem);
|
||||||
if (pl_status != asynSuccess) {
|
if (paramLibStatus != asynSuccess) {
|
||||||
return paramLibAccessFailed(pl_status, "motorStatusProblem_",
|
return paramLibAccessFailed(paramLibStatus, "motorStatusProblem_",
|
||||||
__PRETTY_FUNCTION__, __LINE__);
|
axisNo, __PRETTY_FUNCTION__, __LINE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (motorStatusProblem == 0) {
|
if (motorStatusProblem == 0) {
|
||||||
pl_status = axis->setStringParam(motorMessageText_, drvMessageText);
|
paramLibStatus =
|
||||||
if (pl_status != asynSuccess) {
|
axis->setStringParam(motorMessageText_, drvMessageText);
|
||||||
return paramLibAccessFailed(pl_status, "motorMessageText_",
|
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__);
|
__PRETTY_FUNCTION__, __LINE__);
|
||||||
}
|
}
|
||||||
|
|
||||||
pl_status = axis->setIntegerParam(motorStatusProblem_, 1);
|
paramLibStatus = axis->setIntegerParam(motorStatusProblem_, 1);
|
||||||
if (pl_status != asynSuccess) {
|
if (paramLibStatus != asynSuccess) {
|
||||||
return paramLibAccessFailed(pl_status, "motorStatusProblem",
|
return paramLibAccessFailed(paramLibStatus,
|
||||||
__PRETTY_FUNCTION__, __LINE__);
|
"motorStatusCommsError_", axisNo,
|
||||||
}
|
|
||||||
|
|
||||||
pl_status = axis->setIntegerParam(motorStatusProblem_, 1);
|
|
||||||
if (pl_status != asynSuccess) {
|
|
||||||
return paramLibAccessFailed(pl_status, "motorStatusCommsError_",
|
|
||||||
__PRETTY_FUNCTION__, __LINE__);
|
__PRETTY_FUNCTION__, __LINE__);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -462,7 +504,7 @@ asynStatus turboPmacController::writeInt32(asynUser *pasynUser,
|
|||||||
if (function == rereadEncoderPosition_) {
|
if (function == rereadEncoderPosition_) {
|
||||||
return axis->rereadEncoder();
|
return axis->rereadEncoder();
|
||||||
} else if (function == readConfig_) {
|
} else if (function == readConfig_) {
|
||||||
return axis->atFirstPoll();
|
return axis->init();
|
||||||
} else {
|
} else {
|
||||||
return sinqController::writeInt32(pasynUser, value);
|
return sinqController::writeInt32(pasynUser, value);
|
||||||
}
|
}
|
||||||
@ -519,80 +561,6 @@ asynStatus turboPmacCreateController(const char *portName,
|
|||||||
return asynSuccess;
|
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
|
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
|
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};
|
&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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
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
|
// 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
|
// in order to register both functions in the IOC shell
|
||||||
static void turboPmacRegister(void) {
|
static void turboPmacControllerRegister(void) {
|
||||||
iocshRegister(&configturboPmacCreateController,
|
iocshRegister(&configturboPmacCreateController,
|
||||||
configturboPmacCreateControllerCallFunc);
|
configTurboPmacCreateControllerCallFunc);
|
||||||
iocshRegister(&configturboPmacCreateAxis,
|
|
||||||
configturboPmacCreateAxisCallFunc);
|
|
||||||
}
|
}
|
||||||
epicsExportRegistrar(turboPmacRegister);
|
epicsExportRegistrar(turboPmacControllerRegister);
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
/********************************************
|
/********************************************
|
||||||
* turboPmacController.h
|
* turboPmacController.h
|
||||||
*
|
*
|
||||||
* PMAC V3 controller driver based on the asynMotorController class
|
* Turbo PMAC controller driver based on the asynMotorController class
|
||||||
*
|
*
|
||||||
* Stefan Mathis, September 2024
|
* Stefan Mathis, September 2024
|
||||||
********************************************/
|
********************************************/
|
||||||
|
|
||||||
#ifndef turboPmacController_H
|
#ifndef turboPmacController_H
|
||||||
#define turboPmacController_H
|
#define turboPmacController_H
|
||||||
#include "turboPmacAxis.h"
|
|
||||||
#include "sinqAxis.h"
|
#include "sinqAxis.h"
|
||||||
#include "sinqController.h"
|
#include "sinqController.h"
|
||||||
|
#include "turboPmacAxis.h"
|
||||||
|
|
||||||
class turboPmacController : public sinqController {
|
class turboPmacController : public sinqController {
|
||||||
|
|
||||||
@ -28,14 +28,15 @@ class turboPmacController : public sinqController {
|
|||||||
time (in seconds) has passed, then it declares a timeout.
|
time (in seconds) has passed, then it declares a timeout.
|
||||||
*/
|
*/
|
||||||
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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the axis object
|
* @brief Get the axis object
|
||||||
*
|
*
|
||||||
* @param pasynUser Specify the axis via the asynUser
|
* @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);
|
turboPmacAxis *getAxis(asynUser *pasynUser);
|
||||||
|
|
||||||
@ -43,7 +44,8 @@ class turboPmacController : public sinqController {
|
|||||||
* @brief Get the axis object
|
* @brief Get the axis object
|
||||||
*
|
*
|
||||||
* @param axisNo Specify the axis via its index
|
* @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);
|
turboPmacAxis *getAxis(int axisNo);
|
||||||
|
|
||||||
@ -81,8 +83,8 @@ class turboPmacController : public sinqController {
|
|||||||
int numExpectedResponses);
|
int numExpectedResponses);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Save cast of the given asynAxis pointer to a turboPmacAxis pointer.
|
* @brief Save cast of the given asynAxis pointer to a turboPmacAxis
|
||||||
* If the cast fails, this function returns a nullptr.
|
* pointer. If the cast fails, this function returns a nullptr.
|
||||||
*
|
*
|
||||||
* @param asynAxis
|
* @param asynAxis
|
||||||
* @return turboPmacAxis*
|
* @return turboPmacAxis*
|
||||||
@ -119,7 +121,7 @@ class turboPmacController : public sinqController {
|
|||||||
static const uint32_t MAXBUF_ = 200;
|
static const uint32_t MAXBUF_ = 200;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Stores the constructor input comTimeout
|
Timeout for the communication process in seconds
|
||||||
*/
|
*/
|
||||||
double comTimeout_;
|
double comTimeout_;
|
||||||
|
|
||||||
@ -133,6 +135,7 @@ class turboPmacController : public sinqController {
|
|||||||
|
|
||||||
friend class turboPmacAxis;
|
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 */
|
#endif /* turboPmacController_H */
|
||||||
|
Reference in New Issue
Block a user