Compare commits

...

19 Commits

Author SHA1 Message Date
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
47e72d65a9 Fixed small inaccuracy in the README 2025-05-12 17:04:09 +02:00
5298b5ef69 Fixed typo 2025-05-12 16:59:59 +02:00
29f23216ad Added some comments that this library now uses sinqMotor as static
dependency.
2025-05-12 16:43:57 +02:00
26bc3df876 Fixed bug where the motor could be in state -6 and the driver would
interpret this as "not moving"
2025-05-12 16:19:34 +02:00
87d3cbb3eb Specified new version for sinqMotor 2025-05-12 12:00:49 +02:00
66552d5ffc Pinned sinqMotor version 2025-05-12 11:58:56 +02:00
253f65b25b Added sinqMotor as a submodule for static linking 2025-05-12 08:52:10 +02:00
7 changed files with 55 additions and 81 deletions

6
.gitmodules vendored Normal file
View File

@ -0,0 +1,6 @@
[submodule "sinqmotor"]
path = sinqmotor
url = https://gitea.psi.ch/lin-epics-modules/sinqmotor
[submodule "sinqMotor"]
path = sinqMotor
url = https://gitea.psi.ch/lin-epics-modules/sinqMotor

View File

@ -1,4 +1,4 @@
# Use the PSI build system
# Include the external Makefile
include /ioc/tools/driver.makefile
MODULE=turboPmac
@ -8,28 +8,30 @@ ARCH_FILTER=RHEL%
# Additional module dependencies
REQUIRED+=motorBase
REQUIRED+=sinqMotor
# Specify the version of motorBase we want to build against
motorBase_VERSION=7.2.2
# Specify the version of sinqMotor we want to build against
sinqMotor_VERSION=mathis_s
# These headers allow to depend on this library for derived drivers.
HEADERS += src/pmacAsynIPPort.h
HEADERS += src/turboPmacAxis.h
HEADERS += src/turboPmacController.h
# Source files to build
SOURCES += sinqMotor/src/msgPrintControl.cpp
SOURCES += sinqMotor/src/sinqAxis.cpp
SOURCES += sinqMotor/src/sinqController.cpp
SOURCES += src/pmacAsynIPPort.c
SOURCES += src/turboPmacAxis.cpp
SOURCES += src/turboPmacController.cpp
# Store the record files
TEMPLATES += sinqMotor/db/asynRecord.db
TEMPLATES += sinqMotor/db/sinqMotor.db
TEMPLATES += db/turboPmac.db
# This file registers the motor-specific functions in the IOC shell.
DBDS += sinqMotor/src/sinqMotor.dbd
DBDS += src/turboPmac.dbd
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Werror # -Wpedantic // Does not work because EPICS macros trigger warnings
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Werror # -Wpedantic // Does not work because EPICS macros trigger warnings

View File

@ -4,7 +4,7 @@
## 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
@ -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.
- 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
turboPmac exports the following IOC shell functions:
@ -54,11 +52,11 @@ setMaxSubsequentTimeouts("$(DRIVER_PORT)", 20);
setThresholdComTimeout("$(DRIVER_PORT)", 300, 10);
# 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)")
epicsEnvSet("SINQDBPATH","$(turboPmac_DB)/turboPmac.db")
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
@ -73,4 +71,4 @@ Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-e
### How to build it
Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md.
This driver can be compiled and installed by running `make install` from the same directory where the Makefile is located. However, since it uses the git submodule sinqMotor, make sure that the correct version of the submodule repository is checked out AND the change is commited (`git status` shows no non-committed changes). Please see the section "Usage as static dependency" in https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/main/README.md for more details.

1
sinqMotor Submodule

Submodule sinqMotor added at 275672aaef

View File

@ -71,6 +71,20 @@ turboPmacAxis::turboPmacAxis(turboPmacController *pC, int axisNo,
exit(-1);
}
// Even though this happens already in sinqAxis, a default value for
// motorMessageText is set here again, because apparently the sinqAxis
// constructor is not run before the string is accessed?
status = setStringParam(pC_->motorMessageText(), "");
if (status != asynSuccess) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d:\nFATAL ERROR "
"(setting a parameter value failed "
"with %s)\n. Terminating IOC",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
pC_->stringifyAsynStatus(status));
exit(-1);
}
// turboPmac motors can always be disabled
status = pC_->setIntegerParam(axisNo_, pC_->motorCanDisable(), 1);
if (status != asynSuccess) {
@ -397,13 +411,6 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
case -5:
// Axis is deactivated
*moving = false;
pl_status = setStringParam(pC_->motorMessageText(), "Deactivated");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
break;
case -4:
// Emergency stop
@ -426,19 +433,10 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
break;
case -3:
// Disabled
*moving = false;
pl_status = setStringParam(pC_->motorMessageText(), "Disabled");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
break;
case 0:
// Idle
@ -599,8 +597,7 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
resetError = false;
status = setStringParam(pC_->motorMessageText(),
"Target position would exceed software "
"limits. Please call the support.");
"Target position would exceed software limits");
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
@ -783,10 +780,10 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
}
resetError = false;
snprintf(userMessage, sizeUserMessage,
"Driver hardware error (P%2.2d01 = 13). "
"Please call the support.",
axisNo_);
snprintf(
userMessage, sizeUserMessage,
"Driver hardware error (P%2.2d01 = 13). Please call the support.",
axisNo_);
status = setStringParam(pC_->motorMessageText(), userMessage);
if (status != asynSuccess) {
return pC_->paramLibAccessFailed(status, "motorMessageText_",
@ -1084,22 +1081,7 @@ asynStatus turboPmacAxis::doHome(double min_velocity, double max_velocity,
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
pl_status = setStringParam(pC_->motorMessageText(), "Homing");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
return callParamCallbacks();
} else {
pl_status = setStringParam(pC_->motorMessageText(),
"Can't home a motor with absolute encoder");
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
}
return asynSuccess;
@ -1271,6 +1253,8 @@ asynStatus turboPmacAxis::enable(bool on) {
// Status of parameter library operations
asynStatus pl_status = asynSuccess;
// =========================================================================
/*
Continue regardless of the status returned by the poll; we just want to
find out whether the motor is currently moving or not. If the poll
@ -1280,8 +1264,6 @@ asynStatus turboPmacAxis::enable(bool on) {
bool moving = false;
doPoll(&moving);
// =========================================================================
// If the axis is currently moving, it cannot be disabled. Ignore the
// command and inform the user. We check the last known status of the axis
// instead of "moving", since status -6 is also moving, but the motor can
@ -1330,16 +1312,7 @@ asynStatus turboPmacAxis::enable(bool on) {
"Controller \"%s\", axis %d => %s, line %d\n%s axis\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
on ? "Enable" : "Disable");
if (on == 0) {
pl_status = setStringParam(pC_->motorMessageText(), "Disabling ...");
} else {
pl_status = setStringParam(pC_->motorMessageText(), "Enabling ...");
}
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,
__LINE__);
}
rw_status = pC_->writeRead(axisNo_, command, response, 0);
if (rw_status != asynSuccess) {
return rw_status;
@ -1382,7 +1355,7 @@ asynStatus turboPmacAxis::enable(bool on) {
// Output message to user
snprintf(command, sizeof(command), "Failed to %s within %d seconds",
on ? "enable" : "disable", timeout_enable_disable);
pl_status = setStringParam(pC_->motorMessageText(), "Enabling ...");
pl_status = setStringParam(pC_->motorMessageText(), command);
if (pl_status != asynSuccess) {
return pC_->paramLibAccessFailed(pl_status, "motorMessageText_",
axisNo_, __PRETTY_FUNCTION__,

View File

@ -31,7 +31,7 @@ class turboPmacAxis : public sinqAxis {
* value is currently not used.
* @return asynStatus
*/
asynStatus stop(double acceleration);
virtual asynStatus stop(double acceleration);
/**
* @brief Implementation of the `doHome` function from sinqAxis. The
@ -43,8 +43,8 @@ class turboPmacAxis : public sinqAxis {
* @param forwards
* @return asynStatus
*/
asynStatus doHome(double minVelocity, double maxVelocity,
double acceleration, int forwards);
virtual asynStatus doHome(double minVelocity, double maxVelocity,
double acceleration, int forwards);
/**
* @brief Implementation of the `doPoll` function from sinqAxis. The
@ -53,7 +53,7 @@ class turboPmacAxis : public sinqAxis {
* @param moving
* @return asynStatus
*/
asynStatus doPoll(bool *moving);
virtual asynStatus doPoll(bool *moving);
/**
* @brief Implementation of the `doMove` function from sinqAxis. The
@ -66,8 +66,9 @@ class turboPmacAxis : public sinqAxis {
* @param acceleration
* @return asynStatus
*/
asynStatus doMove(double position, int relative, double min_velocity,
double max_velocity, double acceleration);
virtual asynStatus doMove(double position, int relative,
double min_velocity, double max_velocity,
double acceleration);
/**
* @brief Readout of some values from the controller at IOC startup
@ -79,7 +80,7 @@ class turboPmacAxis : public sinqAxis {
*
* @return asynStatus
*/
asynStatus init();
virtual asynStatus init();
/**
* @brief Implementation of the `doReset` function from sinqAxis.
@ -87,7 +88,7 @@ class turboPmacAxis : public sinqAxis {
* @param on
* @return asynStatus
*/
asynStatus doReset();
virtual asynStatus doReset();
/**
* @brief Enable / disable the axis.
@ -95,7 +96,7 @@ class turboPmacAxis : public sinqAxis {
* @param on
* @return asynStatus
*/
asynStatus enable(bool on);
virtual asynStatus enable(bool on);
/**
* @brief Read the encoder type (incremental or absolute) for this axis from
@ -103,14 +104,14 @@ class turboPmacAxis : public sinqAxis {
*
* @return asynStatus
*/
asynStatus readEncoderType();
virtual asynStatus readEncoderType();
/**
* @brief Trigger a rereading of the encoder position.
*
* @return asynStatus
*/
asynStatus rereadEncoder();
virtual asynStatus rereadEncoder();
/**
* @brief Interpret the error code and populate the user message accordingly

View File

@ -120,17 +120,10 @@ class turboPmacController : public sinqController {
int readConfig() { return readConfig_; }
int flushHardware() { return flushHardware_; }
// Set the maximum buffer size. This is an empirical value which must be
// large enough to avoid overflows for all commands to the device /
// responses from it.
static const uint32_t MAXBUF_ = 200;
asynUser *pasynInt32SyncIOipPort() { return pasynInt32SyncIOipPort_; }
protected:
/*
Timeout for the communication process in seconds
*/
// Timeout for the communication process in seconds
double comTimeout_;
char lastResponse[MAXBUF_];