Applied various bugfixes to make this driver fully operational

This commit is contained in:
2025-02-14 16:31:23 +01:00
parent fd4467ae54
commit b4e49a9d7a
6 changed files with 597 additions and 409 deletions

View File

@ -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

View File

@ -1,4 +1,5 @@
#---------------------------------------------
# SINQ specific DB definitions
#---------------------------------------------
registrar(turboPmacRegister)
registrar(turboPmacControllerRegister)
registrar(turboPmacAxisRegister)

File diff suppressed because it is too large Load Diff

View File

@ -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_;

View File

@ -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"

View File

@ -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 */