Compare commits

...

33 Commits

Author SHA1 Message Date
f423002d23 Updated sinqMotor 2025-06-18 08:26:49 +02:00
79ec79fac1 Updated sinqMotor 2025-06-18 08:18:53 +02:00
1703542770 Use new sinqMotor version 2025-06-17 13:16:20 +02:00
c7d1dc4930 Added getAxisParam variant for char arrays 2025-06-17 10:25:02 +02:00
6fd3848f13 Fixed template for char arrays 2025-06-17 08:45:07 +02:00
56f08f3c76 Fixed template error in sinqMotor 2025-06-17 08:41:07 +02:00
168bfae983 Updated sinqMotor 2025-06-17 08:34:07 +02:00
0e29750d13 Updated sinqMotor version 2025-06-16 16:18:06 +02:00
ba5b921aca Committed new sinqMotor version 2025-06-16 15:55:40 +02:00
1b810fb353 Adjusted dependency to use AxisParamChecked branch 2025-06-16 15:26:44 +02:00
4bc3388bc6 Added destructor for controller 2025-06-10 14:58:59 +02:00
c759156058 Added destructor for controller 2025-06-10 14:53:26 +02:00
eca513f3a0 Updated sinqMotor version to 1.1 2025-06-10 14:11:48 +02:00
26175290bf Added flag to disable reading the limits from the hardware 2025-06-10 13:53:39 +02:00
e316fcf67b Changed priority of IOC shell message 2025-06-06 11:20:50 +02:00
6cf00adb60 Ready for release 1.0 2025-05-23 12:27:31 +02:00
9710d442b8 Removed exit(-1) from init
When the initialization fails, the motor will now try again during each
poll.
2025-05-23 11:56:45 +02:00
8dd1dc4af2 Fixed poller bug in sinqMotor 2025-05-23 11:20:20 +02:00
a758db1211 Change to latest sinqMotor version 2025-05-23 10:11:27 +02:00
a11d10cb6c Updated sinqMotor to 0.15.2 2025-05-16 16:03:30 +02:00
ba353c4e5d Fixed remote git link in repo. 2025-05-16 15:58:17 +02:00
55b523ddaa Made some functions virtual so they can be overwritten in subclasses 2025-05-15 14:54:18 +02:00
75292a6a9c Fixed some merge bugs 2025-05-15 12:32:30 +02:00
53bbe2aae8 Merge branch 'static-dep' 2025-05-15 12:27:26 +02:00
1597dc34e0 Added default value for motorMessageText 2025-05-15 12:25:08 +02:00
dde7066f40 Use latest version of sinqMotor 2025-05-15 11:41:53 +02:00
b4d6447b32 Fixed serious bug in sinqMotor 2025-05-15 11:29:13 +02:00
2f83060ec1 Addes error message for failing to enable / disable within timeout 2025-05-14 16:26:55 +02:00
a3e3a79788 Adjusted usage of motorMessageText to act as an error message only. 2025-05-14 16:17:14 +02:00
2c5fdc7d0a Use new version of sinqMotor 2025-05-14 16:13:10 +02:00
7bf31ac256 Allow enabling / disabling the motor regardless of the status returned by the poll 2025-05-13 14:44:24 +02:00
63a5fcd518 Allow enabling / disabling the motor regardless of the status returned
by the poll.
2025-05-13 14:39:08 +02:00
47e72d65a9 Fixed small inaccuracy in the README 2025-05-12 17:04:09 +02:00
7 changed files with 435 additions and 595 deletions

View File

@ -4,7 +4,7 @@
## Overview ## Overview
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. This is a driver for the Turbo PMAC motion controller with the SINQ communication protocol. It is based on the sinqMotor shared library (https://gitea.psi.ch/lin-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 ## User guide
@ -14,8 +14,6 @@ The folder "utils" contains utility scripts for working with pmac motor controll
- writeRead.py: Allows sending commands to and receiving commands from a pmac controller over an ethernet connection. - writeRead.py: Allows sending commands to and receiving commands from a pmac controller over an ethernet connection.
- analyzeTcpDump.py: Parse the TCP communication between an IOC and a MCU and format it into a dictionary. "demo.py" shows how this data can be easily visualized for analysis. - analyzeTcpDump.py: Parse the TCP communication between an IOC and a MCU and format it into a dictionary. "demo.py" shows how this data can be easily visualized for analysis.
### IOC startup script ### IOC startup script
turboPmac exports the following IOC shell functions: turboPmac exports the following IOC shell functions:
@ -54,11 +52,11 @@ setMaxSubsequentTimeouts("$(DRIVER_PORT)", 20);
setThresholdComTimeout("$(DRIVER_PORT)", 300, 10); setThresholdComTimeout("$(DRIVER_PORT)", 300, 10);
# Parametrize the EPICS record database with the substitution file named after the MCU. # Parametrize the EPICS record database with the substitution file named after the MCU.
epicsEnvSet("SINQDBPATH","$(sinqMotor_DB)/sinqMotor.db") epicsEnvSet("SINQDBPATH","$(turboPmac_DB)/sinqMotor.db")
dbLoadTemplate("$(TOP)/$(DRIVER_PORT).substitutions", "INSTR=$(INSTR)$(DRIVER_PORT):,CONTROLLER=$(DRIVER_PORT)") dbLoadTemplate("$(TOP)/$(DRIVER_PORT).substitutions", "INSTR=$(INSTR)$(DRIVER_PORT):,CONTROLLER=$(DRIVER_PORT)")
epicsEnvSet("SINQDBPATH","$(turboPmac_DB)/turboPmac.db") epicsEnvSet("SINQDBPATH","$(turboPmac_DB)/turboPmac.db")
dbLoadTemplate("$(TOP)/$(DRIVER_PORT).substitutions", "INSTR=$(INSTR)$(DRIVER_PORT):,CONTROLLER=$(DRIVER_PORT)") dbLoadTemplate("$(TOP)/$(DRIVER_PORT).substitutions", "INSTR=$(INSTR)$(DRIVER_PORT):,CONTROLLER=$(DRIVER_PORT)")
dbLoadRecords("$(sinqMotor_DB)/asynRecord.db","P=$(INSTR)$(DRIVER_PORT),PORT=$(IP_PORT)") dbLoadRecords("$(turboPmac_DB)/asynRecord.db","P=$(INSTR)$(DRIVER_PORT),PORT=$(IP_PORT)")
``` ```
### Additional records ### Additional records

View File

@ -31,3 +31,14 @@ record(longout, "$(INSTR)FlushHardware") {
field(PINI, "NO") field(PINI, "NO")
field(VAL, "1") field(VAL, "1")
} }
# If this PV is set to 1 (default), the position limits are read out from the
# controller. Otherwise, the limits given in the substitution file (DHLM and
# DLLM) are used.
# This record is coupled to the parameter library via limFromHardware -> LIM_FROM_HARDWARE.
record(longout, "$(INSTR)$(M):LimFromHardware") {
field(DTYP, "asynInt32")
field(OUT, "@asyn($(CONTROLLER),$(AXIS),1) LIM_FROM_HARDWARE")
field(PINI, "YES")
field(VAL, "$(LIMFROMHARDWARE=1)")
}

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,10 @@
#ifndef turboPmacAXIS_H #ifndef turboPmacAXIS_H
#define turboPmacAXIS_H #define turboPmacAXIS_H
#include "sinqAxis.h" #include "sinqController.h"
#include "turboPmacController.h"
#include <memory>
// Forward declaration of the controller class to resolve the cyclic dependency struct turboPmacAxisImpl;
// between the controller and the axis .h-file. See
// https://en.cppreference.com/w/cpp/language/class.
class turboPmacController;
class turboPmacAxis : public sinqAxis { class turboPmacAxis : public sinqAxis {
public: public:
@ -21,6 +20,7 @@ class turboPmacAxis : public sinqAxis {
/** /**
* @brief Destroy the turboPmacAxis * @brief Destroy the turboPmacAxis
* *
* This destructor is necessary in order to use the PIMPL idiom.
*/ */
virtual ~turboPmacAxis(); virtual ~turboPmacAxis();
@ -31,7 +31,7 @@ class turboPmacAxis : public sinqAxis {
* value is currently not used. * value is currently not used.
* @return asynStatus * @return asynStatus
*/ */
asynStatus stop(double acceleration); virtual asynStatus stop(double acceleration);
/** /**
* @brief Implementation of the `doHome` function from sinqAxis. The * @brief Implementation of the `doHome` function from sinqAxis. The
@ -43,8 +43,8 @@ class turboPmacAxis : public sinqAxis {
* @param forwards * @param forwards
* @return asynStatus * @return asynStatus
*/ */
asynStatus doHome(double minVelocity, double maxVelocity, virtual asynStatus doHome(double minVelocity, double maxVelocity,
double acceleration, int forwards); double acceleration, int forwards);
/** /**
* @brief Implementation of the `doPoll` function from sinqAxis. The * @brief Implementation of the `doPoll` function from sinqAxis. The
@ -53,7 +53,7 @@ class turboPmacAxis : public sinqAxis {
* @param moving * @param moving
* @return asynStatus * @return asynStatus
*/ */
asynStatus doPoll(bool *moving); virtual asynStatus doPoll(bool *moving);
/** /**
* @brief Implementation of the `doMove` function from sinqAxis. The * @brief Implementation of the `doMove` function from sinqAxis. The
@ -66,8 +66,9 @@ class turboPmacAxis : public sinqAxis {
* @param acceleration * @param acceleration
* @return asynStatus * @return asynStatus
*/ */
asynStatus doMove(double position, int relative, double min_velocity, virtual asynStatus doMove(double position, int relative,
double max_velocity, double acceleration); double min_velocity, double max_velocity,
double acceleration);
/** /**
* @brief Readout of some values from the controller at IOC startup * @brief Readout of some values from the controller at IOC startup
@ -79,7 +80,7 @@ class turboPmacAxis : public sinqAxis {
* *
* @return asynStatus * @return asynStatus
*/ */
asynStatus init(); virtual asynStatus init();
/** /**
* @brief Implementation of the `doReset` function from sinqAxis. * @brief Implementation of the `doReset` function from sinqAxis.
@ -87,7 +88,7 @@ class turboPmacAxis : public sinqAxis {
* @param on * @param on
* @return asynStatus * @return asynStatus
*/ */
asynStatus doReset(); virtual asynStatus doReset();
/** /**
* @brief Enable / disable the axis. * @brief Enable / disable the axis.
@ -95,7 +96,7 @@ class turboPmacAxis : public sinqAxis {
* @param on * @param on
* @return asynStatus * @return asynStatus
*/ */
asynStatus enable(bool on); virtual asynStatus enable(bool on);
/** /**
* @brief Read the encoder type (incremental or absolute) for this axis from * @brief Read the encoder type (incremental or absolute) for this axis from
@ -103,14 +104,14 @@ class turboPmacAxis : public sinqAxis {
* *
* @return asynStatus * @return asynStatus
*/ */
asynStatus readEncoderType(); virtual asynStatus readEncoderType();
/** /**
* @brief Trigger a rereading of the encoder position. * @brief Trigger a rereading of the encoder position.
* *
* @return asynStatus * @return asynStatus
*/ */
asynStatus rereadEncoder(); virtual asynStatus rereadEncoder();
/** /**
* @brief Interpret the error code and populate the user message accordingly * @brief Interpret the error code and populate the user message accordingly
@ -122,14 +123,29 @@ class turboPmacAxis : public sinqAxis {
*/ */
asynStatus handleError(int error, char *userMessage, int sizeUserMessage); asynStatus handleError(int error, char *userMessage, int sizeUserMessage);
protected: /**
* @brief Check if the axis needs to run its initialization function
*
* @return true
* @return false
*/
bool needInit();
/**
* @brief Instruct the axis to run its init() function during the next poll
*
* @param needInit
*/
void setNeedInit(bool needInit);
/**
* @brief Return a pointer to the axis controller
*/
virtual turboPmacController *pController() override { return pC_; };
private:
turboPmacController *pC_; turboPmacController *pC_;
std::unique_ptr<turboPmacAxisImpl> pTurboPmacA_;
bool waitForHandshake_;
time_t timeAtHandshake_;
// The axis status is used when enabling / disabling the motor
int axisStatus_;
}; };
#endif #endif

View File

@ -31,6 +31,24 @@ void adjustResponseForPrint(char *dst, const char *src, size_t buf_length) {
} }
} }
struct turboPmacControllerImpl {
// Timeout for the communication process in seconds
double comTimeout;
char lastResponse[sinqController::MAXBUF_];
// User for writing int32 values to the port driver.
asynUser *pasynInt32SyncIOipPort;
// Indices of additional PVs
int rereadEncoderPosition;
int readConfig;
int flushHardware;
int limFromHardware;
};
#define NUM_turboPmac_DRIVER_PARAMS 3
turboPmacController::turboPmacController(const char *portName, turboPmacController::turboPmacController(const char *portName,
const char *ipPortConfigName, const char *ipPortConfigName,
int numAxes, double movingPollPeriod, int numAxes, double movingPollPeriod,
@ -47,21 +65,25 @@ turboPmacController::turboPmacController(const char *portName,
{ {
// The paramLib indices are populated with the calls to createParam
pTurboPmacC_ =
std::make_unique<turboPmacControllerImpl>((turboPmacControllerImpl){
.comTimeout = comTimeout,
.lastResponse = {0},
});
// Initialization of local variables // Initialization of local variables
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
// Initialization of all member variables
comTimeout_ = comTimeout;
// Maximum allowed number of subsequent timeouts before the user is // Maximum allowed number of subsequent timeouts before the user is
// informed. // informed.
maxSubsequentTimeouts_ = 10; setMaxSubsequentTimeouts(10);
// ========================================================================= // =========================================================================
// Create additional parameter library entries // Create additional parameter library entries
status = createParam("REREAD_ENCODER_POSITION", asynParamInt32, status = createParam("REREAD_ENCODER_POSITION", asynParamInt32,
&rereadEncoderPosition_); &pTurboPmacC_->rereadEncoderPosition);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR, asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
@ -71,7 +93,8 @@ turboPmacController::turboPmacController(const char *portName,
exit(-1); exit(-1);
} }
status = createParam("READ_CONFIG", asynParamInt32, &readConfig_); status =
createParam("READ_CONFIG", asynParamInt32, &pTurboPmacC_->readConfig);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR, asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
@ -81,7 +104,19 @@ turboPmacController::turboPmacController(const char *portName,
exit(-1); exit(-1);
} }
status = createParam("FLUSH_HARDWARE", asynParamInt32, &flushHardware_); status = createParam("FLUSH_HARDWARE", asynParamInt32,
&pTurboPmacC_->flushHardware);
if (status != asynSuccess) {
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
"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("LIM_FROM_HARDWARE", asynParamInt32,
&pTurboPmacC_->limFromHardware);
if (status != asynSuccess) { if (status != asynSuccess) {
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR, asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a " "Controller \"%s\" => %s, line %d\nFATAL ERROR (creating a "
@ -100,7 +135,7 @@ turboPmacController::turboPmacController(const char *portName,
const char *message_from_device = const char *message_from_device =
"\006"; // Hex-code for ACK (acknowledge) -> Each message from the MCU "\006"; // Hex-code for ACK (acknowledge) -> Each message from the MCU
// is terminated by this value // is terminated by this value
status = pasynOctetSyncIO->setInputEos(pasynOctetSyncIOipPort_, status = pasynOctetSyncIO->setInputEos(pasynOctetSyncIOipPort(),
message_from_device, message_from_device,
strlen(message_from_device)); strlen(message_from_device));
if (status != asynSuccess) { if (status != asynSuccess) {
@ -109,7 +144,7 @@ turboPmacController::turboPmacController(const char *portName,
"(setting input EOS failed with %s).\nTerminating IOC", "(setting input EOS failed with %s).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__, portName, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status)); stringifyAsynStatus(status));
pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort_); pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort());
exit(-1); exit(-1);
} }
@ -121,7 +156,7 @@ turboPmacController::turboPmacController(const char *portName,
"with %s).\nTerminating IOC", "with %s).\nTerminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__, portName, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status)); stringifyAsynStatus(status));
pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort_); pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort());
exit(-1); exit(-1);
} }
@ -131,17 +166,21 @@ turboPmacController::turboPmacController(const char *portName,
We try to connect to the port via the port name provided by the constructor. We try to connect to the port via the port name provided by the constructor.
If this fails, the function is terminated via exit. If this fails, the function is terminated via exit.
*/ */
pasynInt32SyncIO->connect(ipPortConfigName, 0, &pasynInt32SyncIOipPort_, pasynInt32SyncIO->connect(ipPortConfigName, 0,
NULL); &pTurboPmacC_->pasynInt32SyncIOipPort, NULL);
if (status != asynSuccess || pasynInt32SyncIOipPort_ == nullptr) { if (status != asynSuccess ||
pTurboPmacC_->pasynInt32SyncIOipPort == nullptr) {
errlogPrintf("Controller \"%s\" => %s, line %d:\nFATAL ERROR (cannot " errlogPrintf("Controller \"%s\" => %s, line %d:\nFATAL ERROR (cannot "
"connect to MCU controller).\n" "connect to MCU controller).\n"
"Terminating IOC", "Terminating IOC",
portName, __PRETTY_FUNCTION__, __LINE__); portName, __PRETTY_FUNCTION__, __LINE__);
pasynOctetSyncIO->disconnect(pasynOctetSyncIOipPort());
exit(-1); exit(-1);
} }
} }
turboPmacController::~turboPmacController() {}
/* /*
Access one of the axes of the controller via the axis adress stored in asynUser. Access one of the axes of the controller via the axis adress stored in asynUser.
If the axis does not exist or is not a Axis, a nullptr is returned and an If the axis does not exist or is not a Axis, a nullptr is returned and an
@ -167,7 +206,6 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
// Definition of local variables. // Definition of local variables.
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
asynStatus paramLibStatus = asynSuccess;
asynStatus timeoutStatus = asynSuccess; asynStatus timeoutStatus = asynSuccess;
// char fullCommand[MAXBUF_] = {0}; // char fullCommand[MAXBUF_] = {0};
char drvMessageText[MAXBUF_] = {0}; char drvMessageText[MAXBUF_] = {0};
@ -215,20 +253,20 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
*/ */
status = pasynOctetSyncIO->writeRead( status = pasynOctetSyncIO->writeRead(
pasynOctetSyncIOipPort(), command, commandLength, response, MAXBUF_, pasynOctetSyncIOipPort(), command, commandLength, response, MAXBUF_,
comTimeout_, &nbytesOut, &nbytesIn, &eomReason); pTurboPmacC_->comTimeout, &nbytesOut, &nbytesIn, &eomReason);
msgPrintControlKey comKey = msgPrintControlKey comKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__); msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (status == asynTimeout) { if (status == asynTimeout) {
if (msgPrintControl_.shouldBePrinted(comKey, true, pasynUser())) { if (getMsgPrintControl().shouldBePrinted(comKey, true, pasynUser())) {
asynPrint( asynPrint(
this->pasynUser(), ASYN_TRACE_ERROR, this->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nTimeout while " "Controller \"%s\", axis %d => %s, line %d\nTimeout while "
"writing to the controller. Retrying ...%s\n", "writing to the controller. Retrying ...%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
msgPrintControl_.getSuffix()); getMsgPrintControl().getSuffix());
} }
timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText, timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText,
@ -239,13 +277,14 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
checkMaxSubsequentTimeouts(timeoutCounter, axis); checkMaxSubsequentTimeouts(timeoutCounter, axis);
timeoutCounter += 1; timeoutCounter += 1;
if (maxSubsequentTimeoutsExceeded_) { if (maxSubsequentTimeoutsExceeded()) {
break; break;
} }
status = pasynOctetSyncIO->writeRead( status = pasynOctetSyncIO->writeRead(
pasynOctetSyncIOipPort(), command, commandLength, response, pasynOctetSyncIOipPort(), command, commandLength, response,
MAXBUF_, comTimeout_, &nbytesOut, &nbytesIn, &eomReason); MAXBUF_, pTurboPmacC_->comTimeout, &nbytesOut, &nbytesIn,
&eomReason);
if (status != asynTimeout) { if (status != asynTimeout) {
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR, asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line " "Controller \"%s\", axis %d => %s, line "
@ -255,16 +294,28 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
} }
} }
} else if (status != asynSuccess) { } else if (status != asynSuccess) {
if (msgPrintControl_.shouldBePrinted(comKey, true, pasynUser())) { if (getMsgPrintControl().shouldBePrinted(comKey, true, pasynUser())) {
asynPrint( asynPrint(
this->pasynUser(), ASYN_TRACE_ERROR, this->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nError %s while " "Controller \"%s\", axis %d => %s, line %d\nError %s while "
"writing to the controller.%s\n", "writing to the controller.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
stringifyAsynStatus(status), msgPrintControl_.getSuffix()); stringifyAsynStatus(status), getMsgPrintControl().getSuffix());
} }
} else { } else {
msgPrintControl_.resetCount(comKey, pasynUser()); getMsgPrintControl().resetCount(comKey, pasynUser());
}
if (status != asynSuccess) {
/*
Since the communication failed, there is the possibility that the
controller is not connected at all to the network. In that case, we
cannot be sure that the information read out in the init method of the
axis is still up-to-date the next time we get a connection. Therefore,
an info flag is set which the axis object can use at the start of its
poll method to try to initialize itself.
*/
axis->setNeedInit(true);
} }
if (timeoutStatus == asynError) { if (timeoutStatus == asynError) {
@ -301,16 +352,17 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
"string detected\"). Please call the support.", "string detected\"). Please call the support.",
reasonStringified); reasonStringified);
if (msgPrintControl_.shouldBePrinted(terminateKey, true, pasynUser())) { if (getMsgPrintControl().shouldBePrinted(terminateKey, true,
pasynUser())) {
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR, asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nMessage " "Controller \"%s\", axis %d => %s, line %d\nMessage "
"terminated due to reason %s.%s\n", "terminated due to reason %s.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
reasonStringified, msgPrintControl_.getSuffix()); reasonStringified, getMsgPrintControl().getSuffix());
} }
} else { } else {
msgPrintControl_.resetCount(terminateKey, pasynUser()); getMsgPrintControl().resetCount(terminateKey, pasynUser());
} }
/* /*
@ -327,15 +379,15 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
if (numExpectedResponses != numReceivedResponses) { if (numExpectedResponses != numReceivedResponses) {
adjustResponseForPrint(modResponse, response, MAXBUF_); adjustResponseForPrint(modResponse, response, MAXBUF_);
if (msgPrintControl_.shouldBePrinted(numResponsesKey, true, if (getMsgPrintControl().shouldBePrinted(numResponsesKey, true,
pasynUser())) { pasynUser())) {
asynPrint( asynPrint(
this->pasynUser(), ASYN_TRACE_ERROR, this->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nUnexpected " "Controller \"%s\", axis %d => %s, line %d\nUnexpected "
"response '%s' (carriage returns are replaced with spaces) " "response '%s' (carriage returns are replaced with spaces) "
"for command %s.%s\n", "for command %s.%s\n",
portName, axisNo, __PRETTY_FUNCTION__, __LINE__, modResponse, portName, axisNo, __PRETTY_FUNCTION__, __LINE__, modResponse,
command, msgPrintControl_.getSuffix()); command, getMsgPrintControl().getSuffix());
} }
snprintf(drvMessageText, sizeof(drvMessageText), snprintf(drvMessageText, sizeof(drvMessageText),
@ -345,7 +397,7 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
modResponse, command); modResponse, command);
status = asynError; status = asynError;
} else { } else {
msgPrintControl_.resetCount(numResponsesKey, pasynUser()); getMsgPrintControl().resetCount(numResponsesKey, pasynUser());
} }
// Create custom error messages for different failure modes, if no error // Create custom error messages for different failure modes, if no error
@ -375,41 +427,18 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
// Log the overall status (communication successfull or not) // Log the overall status (communication successfull or not)
if (status == asynSuccess) { if (status == asynSuccess) {
paramLibStatus = axis->setIntegerParam(this->motorStatusCommsError_, 0); setAxisParamChecked(axis, motorStatusCommsError, false);
} else { } else {
// Check if the axis already is in an error communication mode. If // Check if the axis already is in an error communication mode. If
// it is not, upstream the error. This is done to avoid "flooding" // it is not, upstream the error. This is done to avoid "flooding"
// the user with different error messages if more than one error // the user with different error messages if more than one error
// ocurred before an error-free communication // ocurred before an error-free communication
paramLibStatus = getAxisParamChecked(axis, motorStatusProblem, &motorStatusProblem);
getIntegerParam(axisNo, motorStatusProblem_, &motorStatusProblem);
if (paramLibStatus != asynSuccess) {
return paramLibAccessFailed(paramLibStatus, "motorStatusProblem_",
axisNo, __PRETTY_FUNCTION__, __LINE__);
}
if (motorStatusProblem == 0) { if (motorStatusProblem == 0) {
paramLibStatus = setAxisParamChecked(axis, motorMessageText, drvMessageText);
axis->setStringParam(motorMessageText_, drvMessageText); setAxisParamChecked(axis, motorStatusProblem, true);
if (paramLibStatus != asynSuccess) { setAxisParamChecked(axis, motorStatusCommsError, true);
return paramLibAccessFailed(paramLibStatus, "motorMessageText_",
axisNo, __PRETTY_FUNCTION__,
__LINE__);
}
paramLibStatus = axis->setIntegerParam(motorStatusProblem_, 1);
if (paramLibStatus != asynSuccess) {
return paramLibAccessFailed(paramLibStatus,
"motorStatusProblem", axisNo,
__PRETTY_FUNCTION__, __LINE__);
}
paramLibStatus = axis->setIntegerParam(motorStatusProblem_, 1);
if (paramLibStatus != asynSuccess) {
return paramLibAccessFailed(paramLibStatus,
"motorStatusCommsError_", axisNo,
__PRETTY_FUNCTION__, __LINE__);
}
} }
} }
return status; return status;
@ -421,13 +450,13 @@ asynStatus turboPmacController::doFlushHardware() {
constant defined in pmacAsynIPPort.c. This reason is then used within constant defined in pmacAsynIPPort.c. This reason is then used within
the write method of pasynInt32SyncIO to select the flush function. the write method of pasynInt32SyncIO to select the flush function.
*/ */
int temp = pasynInt32SyncIOipPort_->reason; int temp = pTurboPmacC_->pasynInt32SyncIOipPort->reason;
pasynInt32SyncIOipPort_->reason = FLUSH_HARDWARE; pTurboPmacC_->pasynInt32SyncIOipPort->reason = FLUSH_HARDWARE;
asynStatus status = (asynStatus)pasynInt32SyncIO->write( asynStatus status = (asynStatus)pasynInt32SyncIO->write(
pasynInt32SyncIOipPort_, 1, comTimeout_); pTurboPmacC_->pasynInt32SyncIOipPort, 1, pTurboPmacC_->comTimeout);
// Reset the status afterwards // Reset the status afterwards
pasynInt32SyncIOipPort_->reason = temp; pTurboPmacC_->pasynInt32SyncIOipPort->reason = temp;
return status; return status;
} }
@ -440,11 +469,11 @@ asynStatus turboPmacController::writeInt32(asynUser *pasynUser,
turboPmacAxis *axis = getTurboPmacAxis(pasynUser); turboPmacAxis *axis = getTurboPmacAxis(pasynUser);
// Handle custom PVs // Handle custom PVs
if (function == rereadEncoderPosition_) { if (function == rereadEncoderPosition()) {
return axis->rereadEncoder(); return axis->rereadEncoder();
} else if (function == readConfig_) { } else if (function == readConfig()) {
return axis->init(); return axis->init();
} else if (function == flushHardware_) { } else if (function == flushHardware()) {
return doFlushHardware(); return doFlushHardware();
} else { } else {
return sinqController::writeInt32(pasynUser, value); return sinqController::writeInt32(pasynUser, value);
@ -462,6 +491,19 @@ asynStatus turboPmacController::couldNotParseResponse(const char *command,
command, modifiedResponse, axisNo, functionName, lineNumber); command, modifiedResponse, axisNo, functionName, lineNumber);
} }
int turboPmacController::rereadEncoderPosition() {
return pTurboPmacC_->rereadEncoderPosition;
}
int turboPmacController::readConfig() { return pTurboPmacC_->readConfig; }
int turboPmacController::flushHardware() { return pTurboPmacC_->flushHardware; }
int turboPmacController::limFromHardware() {
return pTurboPmacC_->limFromHardware;
}
asynUser *turboPmacController::pasynInt32SyncIOipPort() {
return pTurboPmacC_->pasynInt32SyncIOipPort;
}
/*************************************************************************************/ /*************************************************************************************/
/** The following functions are C-wrappers, and can be called directly from /** The following functions are C-wrappers, and can be called directly from
* iocsh */ * iocsh */

View File

@ -10,7 +10,14 @@
#define turboPmacController_H #define turboPmacController_H
#include "sinqAxis.h" #include "sinqAxis.h"
#include "sinqController.h" #include "sinqController.h"
#include "turboPmacAxis.h" #include <memory>
// Forward declaration of the controller class to resolve the cyclic dependency
// between the controller and the axis .h-file. See
// https://en.cppreference.com/w/cpp/language/class.
class turboPmacAxis;
struct turboPmacControllerImpl;
class turboPmacController : public sinqController { class turboPmacController : public sinqController {
public: public:
@ -33,6 +40,13 @@ class turboPmacController : public sinqController {
double idlePollPeriod, double comTimeout, double idlePollPeriod, double comTimeout,
int numExtraParams = 0); int numExtraParams = 0);
/**
* @brief Destroy the controller. Its implementation is empty, however the
* destructor needs to be provided for handling turboPmacControllerImpl.
*
*/
virtual ~turboPmacController();
/** /**
* @brief Get the axis object * @brief Get the axis object
* *
@ -116,36 +130,17 @@ class turboPmacController : public sinqController {
asynStatus doFlushHardware(); asynStatus doFlushHardware();
// Accessors for additional PVs // Accessors for additional PVs
int rereadEncoderPosition() { return rereadEncoderPosition_; } int rereadEncoderPosition();
int readConfig() { return readConfig_; } int readConfig();
int flushHardware() { return flushHardware_; } int flushHardware();
int limFromHardware();
// Set the maximum buffer size. This is an empirical value which must be asynUser *pasynInt32SyncIOipPort();
// large enough to avoid overflows for all commands to the device /
// responses from it.
static const uint32_t MAXBUF_ = 200;
asynUser *pasynInt32SyncIOipPort() { return pasynInt32SyncIOipPort_; } private:
std::unique_ptr<turboPmacControllerImpl> pTurboPmacC_;
protected: protected:
/*
Timeout for the communication process in seconds
*/
double comTimeout_;
char lastResponse[MAXBUF_];
// User for writing int32 values to the port driver.
asynUser *pasynInt32SyncIOipPort_;
// Indices of additional PVs
#define FIRST_turboPmac_PARAM rereadEncoderPosition_
int rereadEncoderPosition_;
int readConfig_;
int flushHardware_;
#define LAST_turboPmac_PARAM flushHardware_
}; };
#define NUM_turboPmac_DRIVER_PARAMS \
(&LAST_turboPmac_PARAM - &FIRST_turboPmac_PARAM + 1)
#endif /* turboPmacController_H */ #endif /* turboPmacController_H */