Renamed from pmacV3 to turboPmac

This commit is contained in:
2025-01-21 13:07:09 +01:00
parent df7bc07259
commit fd4467ae54
9 changed files with 254 additions and 194 deletions

View File

@ -47,11 +47,11 @@ build_module:
- sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
- echo "LIBVERSION=${CI_COMMIT_TAG:-0.0.1}" >> Makefile
- make install
- cp -rT "/ioc/modules/pmacv3/$(ls -U /ioc/modules/pmacv3/ | head -1)" "./pmacv3-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
- cp -rT "/ioc/modules/turboPmac/$(ls -U /ioc/modules/turboPmac/ | head -1)" "./turboPmac-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
artifacts:
name: "pmacv3-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
name: "turboPmac-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
paths:
- "pmacv3-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}/*"
- "turboPmac-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}/*"
expire_in: 1 week
when: always
tags:

View File

@ -1,7 +1,7 @@
# Use the PSI build system
include /ioc/tools/driver.makefile
MODULE=pmacv3
MODULE=turboPmac
BUILDCLASSES=Linux
EPICS_VERSIONS=7.0.7
ARCH_FILTER=RHEL%
@ -14,17 +14,17 @@ REQUIRED+=sinqMotor
sinqMotor_VERSION=0.6.3
# These headers allow to depend on this library for derived drivers.
HEADERS += src/pmacv3Axis.h
HEADERS += src/pmacv3Controller.h
HEADERS += src/turboPmacAxis.h
HEADERS += src/turboPmacController.h
# Source files to build
SOURCES += src/pmacv3Axis.cpp
SOURCES += src/pmacv3Controller.cpp
SOURCES += src/turboPmacAxis.cpp
SOURCES += src/turboPmacController.cpp
# Store the record files
TEMPLATES += db/pmacv3.db
TEMPLATES += db/turboPmac.db
# This file registers the motor-specific functions in the IOC shell.
DBDS += src/pmacv3.dbd
DBDS += src/turboPmac.dbd
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result # -Werror

View File

@ -1,8 +1,8 @@
# pmacv3
# turboPmac
## Overview
This is a driver for the pmacV3 motion controller with the SINQ communication protocol. It is based on the sinqMotor shared library (https://git.psi.ch/sinq-epics-modules/sinqmotor). The header files contain detailed documentation for all public functions. The headers themselves are exported when building the library to allow other drivers to depend on this one.
This is a driver for the Turbo PMAC motion controller with the SINQ communication protocol. It is based on the sinqMotor shared library (https://git.psi.ch/sinq-epics-modules/sinqmotor). The header files contain detailed documentation for all public functions. The headers themselves are exported when building the library to allow other drivers to depend on this one.
## User guide
@ -17,12 +17,12 @@ The folder "utils" contains utility scripts for working with pmac motor controll
### Usage in IOC shell
pmacv3 exposes the following IOC shell functions (all in pmacv3Controller.cpp):
- `pmacv3Controller`: Create a new controller object.
- `pmacv3Axis`: Create a new axis object.
turboPmac exposes the following IOC shell functions (all in turboPmacController.cpp):
- `turboPmacController`: Create a new controller object.
- `turboPmacAxis`: Create a new axis object.
These functions are parametrized as follows:
```
pmacv3Controller(
turboPmacController(
"$(NAME)", # Name of the MCU, e.g. mcu1. This parameter should be provided by an environment variable.
"$(ASYN_PORT)", # IP-Port of the MCU. This parameter should be provided by an environment variable.
8, # Maximum number of axes
@ -32,7 +32,7 @@ pmacv3Controller(
);
```
```
pmacv3Axis(
turboPmacAxis(
"$(NAME)", # Name of the associated MCU, e.g. mcu1. This parameter should be provided by an environment variable.
1 # Index of the axis.
);

View File

@ -7,7 +7,7 @@ record(longout, "$(INSTR)$(M):RereadEncoder") {
field(PINI, "NO")
}
# The pmacV3 driver reads certain configuration parameters (such as the velocity
# The turboPmac driver reads certain configuration parameters (such as the velocity
# and the acceleration) directly from the MCU. This reading procedure is performed
# once at IOC startup during atFirstPoll. However, it can be triggered manually
# by setting this record value to 1.

View File

@ -1,4 +1,4 @@
#---------------------------------------------
# SINQ specific DB definitions
#---------------------------------------------
registrar(pmacv3Register)
registrar(turboPmacRegister)

View File

@ -1,6 +1,6 @@
#include "pmacv3Axis.h"
#include "turboPmacAxis.h"
#include "asynOctetSyncIO.h"
#include "pmacv3Controller.h"
#include "turboPmacController.h"
#include <cmath>
#include <errlog.h>
#include <limits>
@ -8,7 +8,7 @@
#include <string.h>
#include <unistd.h>
pmacv3Axis::pmacv3Axis(pmacv3Controller *pC, int axisNo)
turboPmacAxis::turboPmacAxis(turboPmacController *pC, int axisNo)
: sinqAxis(pC, axisNo), pC_(pC) {
asynStatus status = asynSuccess;
@ -50,7 +50,7 @@ pmacv3Axis::pmacv3Axis(pmacv3Controller *pC, int axisNo)
exit(-1);
}
// pmacv3 motors can always be disabled
// turboPmac motors can always be disabled
status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable_, 1);
if (status != asynSuccess) {
asynPrint(
@ -66,7 +66,7 @@ pmacv3Axis::pmacv3Axis(pmacv3Controller *pC, int axisNo)
scaleMovTimeout_ = 2.0;
}
pmacv3Axis::~pmacv3Axis(void) {
turboPmacAxis::~turboPmacAxis(void) {
// Since the controller memory is managed somewhere else, we don't need to
// clean up the pointer pC here.
}
@ -74,7 +74,7 @@ pmacv3Axis::~pmacv3Axis(void) {
/**
Read the configuration at the first poll
*/
asynStatus pmacv3Axis::atFirstPoll() {
asynStatus turboPmacAxis::atFirstPoll() {
// Local variable declaration
asynStatus status = asynSuccess;
@ -162,7 +162,7 @@ asynStatus pmacv3Axis::atFirstPoll() {
}
// Perform the actual poll
asynStatus pmacv3Axis::doPoll(bool *moving) {
asynStatus turboPmacAxis::doPoll(bool *moving) {
// Return value for the poll
asynStatus poll_status = asynSuccess;
@ -240,7 +240,7 @@ asynStatus pmacv3Axis::doPoll(bool *moving) {
}
// Transform from EPICS to motor coordinates (see comment in
// pmacv3Axis::atFirstPoll)
// turboPmacAxis::atFirstPoll)
previousPosition = previousPosition * motorRecResolution;
// Query the axis status
@ -648,7 +648,7 @@ asynStatus pmacv3Axis::doPoll(bool *moving) {
}
// Transform from motor to EPICS coordinates (see comment in
// pmacv3Axis::atFirstPoll())
// turboPmacAxis::atFirstPoll())
currentPosition = currentPosition / motorRecResolution;
pl_status = setDoubleParam(pC_->motorPosition_, currentPosition);
@ -660,7 +660,7 @@ asynStatus pmacv3Axis::doPoll(bool *moving) {
return poll_status;
}
asynStatus pmacv3Axis::doMove(double position, int relative, double minVelocity,
asynStatus turboPmacAxis::doMove(double position, int relative, double minVelocity,
double maxVelocity, double acceleration) {
// Status of read-write-operations of ASCII commands to the controller
@ -771,7 +771,7 @@ asynStatus pmacv3Axis::doMove(double position, int relative, double minVelocity,
return rw_status;
}
asynStatus pmacv3Axis::stop(double acceleration) {
asynStatus turboPmacAxis::stop(double acceleration) {
// Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess;
@ -804,7 +804,7 @@ asynStatus pmacv3Axis::stop(double acceleration) {
/*
Home the axis. On absolute encoder systems, this is a no-op
*/
asynStatus pmacv3Axis::doHome(double min_velocity, double max_velocity,
asynStatus turboPmacAxis::doHome(double min_velocity, double max_velocity,
double acceleration, int forwards) {
// Status of read-write-operations of ASCII commands to the controller
@ -859,7 +859,7 @@ asynStatus pmacv3Axis::doHome(double min_velocity, double max_velocity,
/*
Read the encoder type and update the parameter library accordingly
*/
asynStatus pmacv3Axis::readEncoderType() {
asynStatus turboPmacAxis::readEncoderType() {
// Status of read-write-operations of ASCII commands to the controller
asynStatus rw_status = asynSuccess;
@ -924,7 +924,7 @@ the encoder as not all motors have breaks and they may start to move when
disabled. For that reason, we don't automatically disable the motors to run the
command and instead require that the scientists first disable the motor.
*/
asynStatus pmacv3Axis::rereadEncoder() {
asynStatus turboPmacAxis::rereadEncoder() {
char command[pC_->MAXBUF_] = {0};
char response[pC_->MAXBUF_] = {0};
char encoderType[pC_->MAXBUF_] = {0};
@ -1006,7 +1006,7 @@ asynStatus pmacv3Axis::rereadEncoder() {
return asynSuccess;
}
asynStatus pmacv3Axis::enable(bool on) {
asynStatus turboPmacAxis::enable(bool on) {
int timeout_enable_disable = 2;
char command[pC_->MAXBUF_], response[pC_->MAXBUF_];

View File

@ -1,27 +1,27 @@
#ifndef pmacv3AXIS_H
#define pmacv3AXIS_H
#ifndef turboPmacAXIS_H
#define turboPmacAXIS_H
#include "sinqAxis.h"
// Forward declaration of the controller class to resolve the cyclic dependency
// between C804Controller.h and C804Axis.h. See
// https://en.cppreference.com/w/cpp/language/class.
class pmacv3Controller;
class turboPmacController;
class pmacv3Axis : public sinqAxis {
class turboPmacAxis : public sinqAxis {
public:
/**
* @brief Construct a new pmacv3Axis
* @brief Construct a new turboPmacAxis
*
* @param pController Pointer to the associated controller
* @param axisNo Index of the axis
*/
pmacv3Axis(pmacv3Controller *pController, int axisNo);
turboPmacAxis(turboPmacController *pController, int axisNo);
/**
* @brief Destroy the pmacv3Axis
* @brief Destroy the turboPmacAxis
*
*/
virtual ~pmacv3Axis();
virtual ~turboPmacAxis();
/**
* @brief Implementation of the `stop` function from asynMotorAxis
@ -104,7 +104,7 @@ class pmacv3Axis : public sinqAxis {
asynStatus rereadEncoder();
protected:
pmacv3Controller *pC_;
turboPmacController *pC_;
bool initial_poll_;
bool waitForHandshake_;
@ -114,7 +114,7 @@ class pmacv3Axis : public sinqAxis {
int axisStatus_;
private:
friend class pmacv3Controller;
friend class turboPmacController;
};
#endif

View File

@ -1,7 +1,7 @@
#include "pmacv3Controller.h"
#include "turboPmacController.h"
#include "asynMotorController.h"
#include "asynOctetSyncIO.h"
#include "pmacv3Axis.h"
#include "turboPmacAxis.h"
#include <epicsExport.h>
#include <errlog.h>
#include <iocsh.h>
@ -29,7 +29,7 @@ void adjustResponseForPrint(char *dst, const char *src, size_t buf_length) {
}
/**
* @brief Construct a new pmacv3Controller::pmacv3Controller object
* @brief Construct a new turboPmacController::turboPmacController object
*
* @param portName See documentation of sinqController
* @param ipPortConfigName See documentation of sinqController
@ -40,10 +40,11 @@ void adjustResponseForPrint(char *dst, const char *src, size_t buf_length) {
* is declared in writeRead (in seconds)
* @param extraParams See documentation of sinqController
*/
pmacv3Controller::pmacv3Controller(const char *portName,
const char *ipPortConfigName, int numAxes,
double movingPollPeriod,
double idlePollPeriod, double comTimeout)
turboPmacController::turboPmacController(const char *portName,
const char *ipPortConfigName,
int numAxes, double movingPollPeriod,
double idlePollPeriod,
double comTimeout)
: sinqController(
portName, ipPortConfigName, numAxes, movingPollPeriod, idlePollPeriod,
/*
@ -51,7 +52,7 @@ pmacv3Controller::pmacv3Controller(const char *portName,
- REREAD_ENCODER_POSITION
- READ_CONFIG
*/
NUM_PMACV3_DRIVER_PARAMS)
NUM_turboPmac_DRIVER_PARAMS)
{
@ -136,21 +137,21 @@ Access one of the axes of the controller via the axis adress stored in asynUser.
If the axis does not exist or is not a Axis, a nullptr is returned and an
error is emitted.
*/
pmacv3Axis *pmacv3Controller::getAxis(asynUser *pasynUser) {
turboPmacAxis *turboPmacController::getAxis(asynUser *pasynUser) {
asynMotorAxis *asynAxis = asynMotorController::getAxis(pasynUser);
return pmacv3Controller::castToAxis(asynAxis);
return turboPmacController::castToAxis(asynAxis);
}
/*
Access one of the axes of the controller via the axis index.
If the axis does not exist or is not a Axis, the function must return Null
*/
pmacv3Axis *pmacv3Controller::getAxis(int axisNo) {
turboPmacAxis *turboPmacController::getAxis(int axisNo) {
asynMotorAxis *asynAxis = asynMotorController::getAxis(axisNo);
return pmacv3Controller::castToAxis(asynAxis);
return turboPmacController::castToAxis(asynAxis);
}
pmacv3Axis *pmacv3Controller::castToAxis(asynMotorAxis *asynAxis) {
turboPmacAxis *turboPmacController::castToAxis(asynMotorAxis *asynAxis) {
// =========================================================================
@ -161,18 +162,18 @@ pmacv3Axis *pmacv3Controller::castToAxis(asynMotorAxis *asynAxis) {
// Here, an error is emitted since asyn_axis is not a nullptr but also not
// an instance of Axis
pmacv3Axis *axis = dynamic_cast<pmacv3Axis *>(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 pmacv3Axis",
"%s => line %d:\nAxis %d is not an instance of turboPmacAxis",
__PRETTY_FUNCTION__, __LINE__, axis->axisNo_);
}
return axis;
}
asynStatus pmacv3Controller::writeRead(int axisNo, const char *command,
char *response,
int numExpectedResponses) {
asynStatus turboPmacController::writeRead(int axisNo, const char *command,
char *response,
int numExpectedResponses) {
// Definition of local variables.
asynStatus status = asynSuccess;
@ -183,9 +184,17 @@ asynStatus pmacv3Controller::writeRead(int axisNo, const char *command,
int motorStatusProblem = 0;
int numReceivedResponses = 0;
// Send the message and block the thread until either a response has
// been received or the timeout is triggered
int eomReason = 0; // Flag indicating why the message has ended
/*
asyn defines the following reasons for an end-of-message coming from the MCU
(https://epics.anl.gov/modules/soft/asyn/R4-14/asynDriver.pdf, p. 28):
0: Timeout
1: Request count reached
2: End of string detected -> In this driver, this is the "normal" case
4: End indicator detected
Combinations of reasons are also possible, e.g. eomReason = 5 would mean
that both the request count was reached and an end indicator was detected.
*/
int eomReason = 0;
// Number of bytes of the outgoing message (which is command + the
// end-of-string terminator defined in the constructor)
@ -197,14 +206,14 @@ asynStatus pmacv3Controller::writeRead(int axisNo, const char *command,
// =========================================================================
pmacv3Axis *axis = getAxis(axisNo);
turboPmacAxis *axis = getAxis(axisNo);
if (axis == nullptr) {
// We already did the error logging directly in getAxis
return asynError;
}
/*
The message protocol of the pmacv3 used at PSI looks as follows (all
The message protocol of the turboPmac used at PSI looks as follows (all
characters immediately following each other without a newline):
0x40 (ASCII value of @) -> Request for download
0xBF (ASCII value of ¿) -> Select mode "get_response"
@ -223,26 +232,119 @@ asynStatus pmacv3Controller::writeRead(int axisNo, const char *command,
methods of C don't work.
*/
// The entire message is equal to the command length
const size_t commandLength =
strlen(command) + 1; // +1 because of the appended /r
const int offset = 8;
const size_t commandLength = strlen(command);
const int offset = 9;
// Positions 2 to 6 must have the value 0. Since fullCommand is initialized
// as an array of zeros, we don't need to set these bits manually.
fullCommand[0] = '\x40';
fullCommand[1] = '\xBF';
fullCommand[7] = commandLength;
snprintf((char *)fullCommand + offset, MAXBUF_ - offset, "%s\r", 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));
// 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++) {
fullCommand[i + offset] = command[i];
}
asynPrint(this->pasynUserSelf, ASYN_TRACEIO_DRIVER,
"%s => line %d:\nSending command %s", __PRETTY_FUNCTION__,
__LINE__, fullCommand);
// Perform the actual writeRead
status = pasynOctetSyncIO->writeRead(
lowLevelPortUser_, fullCommand, commandLength + offset, response,
MAXBUF_, comTimeout_, &nbytesOut, &nbytesIn, &eomReason);
// 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
method, because the latter is actually a flushWriteRead (see
https://epics.anl.gov/modules/soft/asyn/R4-14/asynDriver.pdf, p. 31) ->
Calls the flush command, then the write command, then the read 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):
*/
// Send out the command
status = pasynOctetSyncIO->write(lowLevelPortUser_, fullCommand,
commandLength + offset, comTimeout_,
&nbytesOut);
if (status != asynSuccess) {
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__);
}
}
// 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__);
}
}
// // 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];
// }
// }
// 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);
/*
Calculate the number of received responses by counting the number of
@ -253,69 +355,23 @@ asynStatus pmacv3Controller::writeRead(int axisNo, const char *command,
numReceivedResponses++;
}
}
/*
Check if we got the expected amount of responses. If we didn't, flush the
PMAC and try again. If that fails as well, return an error.
*/
if (numExpectedResponses != numReceivedResponses) {
// Flush message as defined in Turbo PMAC User Manual, p. 430:
// \x40\xB3000
// VR_DOWNLOAD = \x40
// VR_PMAC_FLUSH = \xB3
char flush_msg[5] = {0};
flush_msg[0] = '\x40';
flush_msg[1] = '\xB3';
size_t nbytesOut = 0;
status = pasynOctetSyncIO->write(lowLevelPortUser_, flush_msg, 5,
comTimeout_, &nbytesOut);
// Wait after the flush so the MCU has time to prepare for the
// next command
usleep(100000);
if (status == asynSuccess) {
// If flushing the MCU succeded, try to send the command again
status = pasynOctetSyncIO->writeRead(
lowLevelPortUser_, fullCommand, commandLength + offset,
response, MAXBUF_, comTimeout_, &nbytesOut, &nbytesIn,
&eomReason);
// If the command returned a bad answer for the second time, give up
// and propagate the problem
numReceivedResponses = 0;
for (size_t i = 0; i < strlen(response); i++) {
if (response[i] == '\r') {
numReceivedResponses++;
}
}
// Second check: If this fails, give up and propagate the error.
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);
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;
}
} else {
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
"%s => line %d:\nFlushing the MCU failed with %s\n",
__PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status));
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);
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
@ -390,12 +446,13 @@ asynStatus pmacv3Controller::writeRead(int axisNo, const char *command,
return status;
}
asynStatus pmacv3Controller::writeInt32(asynUser *pasynUser, epicsInt32 value) {
asynStatus turboPmacController::writeInt32(asynUser *pasynUser,
epicsInt32 value) {
int function = pasynUser->reason;
// =====================================================================
pmacv3Axis *axis = getAxis(pasynUser);
turboPmacAxis *axis = getAxis(pasynUser);
if (axis == nullptr) {
// We already did the error logging directly in getAxis
return asynError;
@ -421,7 +478,7 @@ asynStatus sinqController::readInt32(asynUser *pasynUser, epicsInt32 *value) {
}
}
asynStatus pmacv3Controller::errMsgCouldNotParseResponse(
asynStatus turboPmacController::errMsgCouldNotParseResponse(
const char *command, const char *response, int axisNo,
const char *functionName, int lineNumber) {
char modifiedResponse[MAXBUF_] = {0};
@ -437,13 +494,13 @@ asynStatus pmacv3Controller::errMsgCouldNotParseResponse(
extern "C" {
/*
C wrapper for the controller constructor. Please refer to the pmacv3Controller
constructor documentation.
C wrapper for the controller constructor. Please refer to the
turboPmacController constructor documentation.
*/
asynStatus pmacv3CreateController(const char *portName,
const char *ipPortConfigName, int numAxes,
double movingPollPeriod,
double idlePollPeriod, double comTimeout) {
asynStatus turboPmacCreateController(const char *portName,
const char *ipPortConfigName, int numAxes,
double movingPollPeriod,
double idlePollPeriod, double comTimeout) {
/*
We create a new instance of the controller, using the "new" keyword to
allocate it on the heap while avoiding RAII.
@ -455,19 +512,19 @@ asynStatus pmacv3CreateController(const char *portName,
*/
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-variable"
pmacv3Controller *pController =
new pmacv3Controller(portName, ipPortConfigName, numAxes,
movingPollPeriod, idlePollPeriod, comTimeout);
turboPmacController *pController =
new turboPmacController(portName, ipPortConfigName, numAxes,
movingPollPeriod, idlePollPeriod, comTimeout);
return asynSuccess;
}
/*
C wrapper for the axis constructor. Please refer to the pmacv3Axis constructor
documentation. The controller is read from the portName.
C wrapper for the axis constructor. Please refer to the turboPmacAxis
constructor documentation. The controller is read from the portName.
*/
asynStatus pmacv3CreateAxis(const char *portName, int axis) {
pmacv3Axis *pAxis;
asynStatus turboPmacCreateAxis(const char *portName, int axis) {
turboPmacAxis *pAxis;
/*
findAsynPortDriver is a asyn library FFI function which uses the C ABI.
@ -498,11 +555,11 @@ asynStatus pmacv3CreateAxis(const char *portName, int axis) {
asynPortDriver *apd = (asynPortDriver *)(ptr);
// Safe downcast
pmacv3Controller *pC = dynamic_cast<pmacv3Controller *>(apd);
turboPmacController *pC = dynamic_cast<turboPmacController *>(apd);
if (pC == nullptr) {
errlogPrintf(
"%s => line %d:\ncontroller on port %s is not a pmacv3Controller.",
__PRETTY_FUNCTION__, __LINE__, portName);
errlogPrintf("%s => line %d:\ncontroller on port %s is not a "
"turboPmacController.",
__PRETTY_FUNCTION__, __LINE__, portName);
return asynError;
}
@ -521,7 +578,7 @@ asynStatus pmacv3CreateAxis(const char *portName, int axis) {
*/
#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
#pragma GCC diagnostic ignored "-Wunused-variable"
pAxis = new pmacv3Axis(pC, axis);
pAxis = new turboPmacAxis(pC, axis);
// Allow manipulation of the controller again
pC->unlock();
@ -557,11 +614,11 @@ static const iocshArg CreateControllerArg5 = {"Communication timeout (s)",
static const iocshArg *const CreateControllerArgs[] = {
&CreateControllerArg0, &CreateControllerArg1, &CreateControllerArg2,
&CreateControllerArg3, &CreateControllerArg4, &CreateControllerArg5};
static const iocshFuncDef configPmacV3CreateController = {"pmacv3Controller", 6,
CreateControllerArgs};
static void configPmacV3CreateControllerCallFunc(const iocshArgBuf *args) {
pmacv3CreateController(args[0].sval, args[1].sval, args[2].ival,
args[3].dval, args[4].dval, args[5].dval);
static const iocshFuncDef configturboPmacCreateController = {
"turboPmacController", 6, CreateControllerArgs};
static void configturboPmacCreateControllerCallFunc(const iocshArgBuf *args) {
turboPmacCreateController(args[0].sval, args[1].sval, args[2].ival,
args[3].dval, args[4].dval, args[5].dval);
}
/*
@ -573,20 +630,21 @@ static const iocshArg CreateAxisArg0 = {"Controller name (e.g. mcu1)",
static const iocshArg CreateAxisArg1 = {"Axis number", iocshArgInt};
static const iocshArg *const CreateAxisArgs[] = {&CreateAxisArg0,
&CreateAxisArg1};
static const iocshFuncDef configPmacV3CreateAxis = {"pmacv3Axis", 2,
CreateAxisArgs};
static void configPmacV3CreateAxisCallFunc(const iocshArgBuf *args) {
pmacv3CreateAxis(args[0].sval, args[1].ival);
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 pmacv3.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
static void pmacv3Register(void) {
iocshRegister(&configPmacV3CreateController,
configPmacV3CreateControllerCallFunc);
iocshRegister(&configPmacV3CreateAxis, configPmacV3CreateAxisCallFunc);
static void turboPmacRegister(void) {
iocshRegister(&configturboPmacCreateController,
configturboPmacCreateControllerCallFunc);
iocshRegister(&configturboPmacCreateAxis,
configturboPmacCreateAxisCallFunc);
}
epicsExportRegistrar(pmacv3Register);
epicsExportRegistrar(turboPmacRegister);
#endif

View File

@ -1,22 +1,22 @@
/********************************************
* pmacv3Controller.h
* turboPmacController.h
*
* PMAC V3 controller driver based on the asynMotorController class
*
* Stefan Mathis, September 2024
********************************************/
#ifndef pmacv3Controller_H
#define pmacv3Controller_H
#include "pmacv3Axis.h"
#ifndef turboPmacController_H
#define turboPmacController_H
#include "turboPmacAxis.h"
#include "sinqAxis.h"
#include "sinqController.h"
class pmacv3Controller : public sinqController {
class turboPmacController : public sinqController {
public:
/**
* @brief Construct a new pmacv3Controller object
* @brief Construct a new turboPmacController object
*
* @param portName See sinqController constructor
* @param ipPortConfigName See sinqController constructor
@ -27,7 +27,7 @@ class pmacv3Controller : public sinqController {
the underlying asynOctetSyncIO interface waits for a response until this
time (in seconds) has passed, then it declares a timeout.
*/
pmacv3Controller(const char *portName, const char *ipPortConfigName,
turboPmacController(const char *portName, const char *ipPortConfigName,
int numAxes, double movingPollPeriod,
double idlePollPeriod, double comTimeout);
@ -35,17 +35,17 @@ class pmacv3Controller : public sinqController {
* @brief Get the axis object
*
* @param pasynUser Specify the axis via the asynUser
* @return pmacv3Axis* If no axis could be found, this is a nullptr
* @return turboPmacAxis* If no axis could be found, this is a nullptr
*/
pmacv3Axis *getAxis(asynUser *pasynUser);
turboPmacAxis *getAxis(asynUser *pasynUser);
/**
* @brief Get the axis object
*
* @param axisNo Specify the axis via its index
* @return pmacv3Axis* If no axis could be found, this is a nullptr
* @return turboPmacAxis* If no axis could be found, this is a nullptr
*/
pmacv3Axis *getAxis(int axisNo);
turboPmacAxis *getAxis(int axisNo);
/**
* @brief Overloaded function of sinqController
@ -81,17 +81,17 @@ class pmacv3Controller : public sinqController {
int numExpectedResponses);
/**
* @brief Save cast of the given asynAxis pointer to a pmacv3Axis pointer.
* @brief Save cast of the given asynAxis pointer to a turboPmacAxis pointer.
* If the cast fails, this function returns a nullptr.
*
* @param asynAxis
* @return pmacv3Axis*
* @return turboPmacAxis*
*/
pmacv3Axis *castToAxis(asynMotorAxis *asynAxis);
turboPmacAxis *castToAxis(asynMotorAxis *asynAxis);
/**
* @brief Specialized version of sinqController::errMsgCouldNotParseResponse
* for pmacv3
* for turboPmac
*
* This is an overloaded version of
* sinqController::errMsgCouldNotParseResponse which calls
@ -123,14 +123,16 @@ class pmacv3Controller : public sinqController {
*/
double comTimeout_;
char lastResponse[MAXBUF_];
// Indices of additional PVs
#define FIRST_PMACV3_PARAM rereadEncoderPosition_
#define FIRST_turboPmac_PARAM rereadEncoderPosition_
int rereadEncoderPosition_;
int readConfig_;
#define LAST_PMACV3_PARAM readConfig_
#define LAST_turboPmac_PARAM readConfig_
friend class pmacv3Axis;
friend class turboPmacAxis;
};
#define NUM_PMACV3_DRIVER_PARAMS (&LAST_PMACV3_PARAM - &FIRST_PMACV3_PARAM + 1)
#define NUM_turboPmac_DRIVER_PARAMS (&LAST_turboPmac_PARAM - &FIRST_turboPmac_PARAM + 1)
#endif /* pmacv3Controller_H */
#endif /* turboPmacController_H */