Compare commits

..

8 Commits
main ... 1.3.1

Author SHA1 Message Date
62a3d8c834 Added documenation for enableDisable flag 2025-07-29 11:50:34 +02:00
fd2afa9ec6 Added sleep to stop to make sure at least one poll happens and EPCIS knows the motor is moving 2025-07-29 09:44:26 +02:00
949e9faef3 Fixed various bugs found by the automated tests 2025-07-25 16:53:52 +02:00
5df84d6017 Using fixed 1.3.0 version 2025-07-24 11:32:44 +02:00
08c85129f3 Updated sinqMotor to 1.3.0 2025-07-24 11:16:37 +02:00
a980740fd5 Test timing problem 2025-07-24 09:40:52 +02:00
338b4d61b4 Added sleep after write commands
See comment in writeRead for explanation.
2025-07-24 08:51:38 +02:00
25b832a6e9 Updated sinqMotor to latest version 2025-07-23 09:55:24 +02:00
13 changed files with 167 additions and 222 deletions

View File

@@ -1,24 +0,0 @@
name: Test And Build
on: [push]
jobs:
Lint:
runs-on: linepics
steps:
- name: checkout repo
uses: actions/checkout@v4
- name: cppcheck
run: cppcheck --std=c++17 --addon=cert --addon=misc --error-exitcode=1 src/*.cpp
- name: formatting
run: clang-format --style=file --Werror --dry-run src/*.cpp
Build:
runs-on: linepics
steps:
- name: checkout repo
uses: actions/checkout@v4
with:
submodules: 'true'
- run: |
sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
echo -e "\nIGNORE_SUBMODULES += sinqmotor" >> Makefile
make install

8
.gitignore vendored
View File

@@ -1,8 +0,0 @@
O.*
.cvsignore
.vscode
src/.vscode
utils/analyzeTcpDump/__pycache__
utils/analyzeTcpDump/demo.pcap.json
utils/analyzeTcpDump/demovenv
utils/analyzeTcpDump/venv

58
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,58 @@
default:
image: docker.psi.ch:5000/sinqdev/sinqepics:latest
stages:
- lint
- build
- test
cppcheck:
stage: lint
script:
- cppcheck --std=c++17 --addon=cert --addon=misc --error-exitcode=1 src/*.cpp
artifacts:
expire_in: 1 week
tags:
- sinq
formatting:
stage: lint
script:
- clang-format --style=file --Werror --dry-run src/*.cpp
artifacts:
expire_in: 1 week
tags:
- sinq
# clangtidy:
# stage: lint
# script:
# - curl https://docker.psi.ch:5000/v2/_catalog
# # - dnf update -y
# # - dnf install -y clang-tools-extra
# # - clang-tidy sinqEPICSApp/src/*.cpp sinqEPICSApp/src/*.c sinqEPICSApp/src/*.h -checks=cppcoreguidelines-*,cert-*
# # tags:
# # - sinq
build_module:
stage: build
script:
- export SINQMOTOR_VERSION="$(grep 'sinqMotor_VERSION=' Makefile | cut -d= -f2)"
- git clone --depth 1 --branch "${SINQMOTOR_VERSION}" https://gitlab-ci-token:${CI_JOB_TOKEN}@git.psi.ch/sinq-epics-modules/sinqmotor.git
- pushd sinqmotor
- sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
- echo "LIBVERSION=${SINQMOTOR_VERSION}" >> Makefile
- make install
- popd
- sed -i 's/ARCH_FILTER=.*/ARCH_FILTER=linux%/' Makefile
- echo "LIBVERSION=${CI_COMMIT_TAG:-0.0.1}" >> Makefile
- make install
- cp -rT "/ioc/modules/turboPmac/$(ls -U /ioc/modules/turboPmac/ | head -1)" "./turboPmac-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
artifacts:
name: "turboPmac-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}"
paths:
- "turboPmac-${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}/*"
expire_in: 1 week
when: always
tags:
- sinq

View File

@@ -34,5 +34,4 @@ TEMPLATES += db/turboPmac.db
DBDS += sinqMotor/src/sinqMotor.dbd
DBDS += src/turboPmac.dbd
USR_CFLAGS += -Wall -Wextra -Wunused-result -Werror -fvisibility=hidden # -Wpedantic // Does not work because EPICS macros trigger warnings
USR_CXXFLAGS += -Wall -Wextra -Wunused-result -Werror -fvisibility=hidden
USR_CFLAGS += -Wall -Wextra -Weffc++ -Wunused-result -Werror # -Wpedantic // Does not work because EPICS macros trigger warnings

View File

@@ -4,26 +4,15 @@
## 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://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.
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
This driver is a standard sinqMotor-derived which however uses a special low
level IP Port driver (`pmacAsynIPPortConfigure`) instead of the standard
`drvAsynIPPortConfigure`. For the general configuration, please see
https://gitea.psi.ch/lin-epics-modules/sinqMotor/src/branch/main/README.md.
This driver is a standard sinqMotor-derived which however uses a special low level IP Port driver (`pmacAsynIPPortConfigure`) instead of the standard `drvAsynIPPortConfigure`. For the general configuration, please see https://gitea.psi.ch/lin-epics-modules/sinqMotor/src/branch/main/README.md.
The folder "utils" contains utility scripts for working with pmac motor
controllers. To read their manual, run the scripts without any arguments.
- 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.
The folder "utils" contains utility scripts for working with pmac motor controllers. To read their manual, run the scripts without any arguments.
- 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
@@ -38,10 +27,8 @@ The full turboPmacX.cmd file looks like this:
epicsEnvSet("DRIVER_PORT","turboPmacX")
epicsEnvSet("IP_PORT","p$(DRIVER_PORT)")
# Create the TCP/IP socket used to talk with the controller. The socket can be
# adressed from within the IOC shell via the port name.
# We do not use the standard asyn port driver here, but a PMAC-specific one
# which enables the usage of StreamDevices for
# Create the TCP/IP socket used to talk with the controller. The socket can be adressed from within the IOC shell via the port name.
# We do not use the standard asyn port driver here, but a PMAC-specific one which enables the usage of StreamDevices for
# communicating with the controller directly.
pmacAsynIPPortConfigure("$(IP_PORT)","172.28.101.24:1025")
@@ -61,8 +48,7 @@ turboPmacAxis("$(DRIVER_PORT)",5);
# Set the number of subsequent timeouts
setMaxSubsequentTimeouts("$(DRIVER_PORT)", 20);
# Configure the timeout frequency watchdog: A maximum of 10 timeouts are allowed
# in 300 seconds before an alarm message is sent.
# Configure the timeout frequency watchdog: A maximum of 10 timeouts are allowed in 300 seconds before an alarm message is sent.
setThresholdComTimeout("$(DRIVER_PORT)", 300, 10);
# Parametrize the EPICS record database with the substitution file named after the MCU.
@@ -75,22 +61,14 @@ dbLoadRecords("$(turboPmac_DB)/asynRecord.db","P=$(INSTR)$(DRIVER_PORT),PORT=$(I
### Additional records
`turboPmac` provides a variety of additional records. See `db/turboPmac.db` for
the complete list and the documentation.
`turboPmac` provides a variety of additional records. See `db/turboPmac.db` for the complete list and the documentation.
## Developer guide
### Versioning
Please see the documentation for the module sinqMotor:
https://gitea.psi.ch/lin-epics-modules/sinqMotor/src/branch/main/README.md.
Please see the documentation for the module sinqMotor: https://gitea.psi.ch/lin-epics-modules/sinqMotor/src/branch/main/README.md.
### How to build it
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://gitea.psi.ch/lin-epics-modules/sinqMotor/src/branch/main/README.md for
more details.
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://gitea.psi.ch/lin-epics-modules/sinqMotor/src/branch/main/README.md for more details.

Binary file not shown.

View File

@@ -230,7 +230,8 @@ static asynStatus sendPmacGetBuffer(pmacPvt *pPmacPvt, asynUser *pasynUser,
// =============================================================================
int pmacAsynIPPortConfigure(const char *portName, const char *hostInfo) {
epicsShareFunc int pmacAsynIPPortConfigure(const char *portName,
const char *hostInfo) {
asynStatus status = asynSuccess;
asynInterface *int32LowerLevelInterface = NULL;
asynInterface *octetLowerLevelInterface = NULL;

View File

@@ -2,7 +2,6 @@
#define asynInterposePmac_H
#include <epicsExport.h>
#include <macros.h>
#include <shareLib.h>
/*
@@ -26,7 +25,8 @@ extern "C" {
* 172.23.243.156:1025)
* @return status
*/
int HIDDEN pmacAsynIPPortConfigure(const char *portName, const char *hostInfo);
epicsShareFunc int pmacAsynIPPortConfigure(const char *portName,
const char *hostInfo);
#ifdef __cplusplus
}

View File

@@ -70,13 +70,12 @@ turboPmacAxis::turboPmacAxis(turboPmacController *pC, int axisNo,
axes.push_back(this);
}
pTurboPmacA_ = std::make_unique<turboPmacAxisImpl>((turboPmacAxisImpl){
.waitForHandshake = false,
.enableDisable = false,
.timeAtHandshake = 0,
.axisStatus = 0,
.needInit = false,
});
pTurboPmacA_ = std::make_unique<turboPmacAxisImpl>(
(turboPmacAxisImpl){.waitForHandshake = false,
.enableDisable = false,
.timeAtHandshake = 0,
.axisStatus = 0,
.needInit = false});
// Provide initial values for some parameter library entries
status = pC_->setIntegerParam(axisNo_, pC_->rereadEncoderPosition(), 0);
@@ -133,16 +132,9 @@ asynStatus turboPmacAxis::init() {
double motorVelocity = 0.0;
double motorVmax = 0.0;
double motorAccel = 0.0;
// Conversion factor between engineering units (EU) and encoder counts
double counts_to_eu = 0.0;
// Deadband in counts. Can be a fraction of a count, hence double.
double deadband_counts = 0.0;
// Offset time for the movement watchdog caused by the air cushions in
// milliseconds.
int acoDelay = 0.0;
int acoDelay = 0.0; // Offset time for the movement watchdog caused by
// the air cushions in milliseconds.
int axStatus = 0;
// The parameter library takes some time to be initialized. Therefore we
// wait until the status is not asynParamUndefined anymore.
@@ -171,14 +163,12 @@ asynStatus turboPmacAxis::init() {
/*
Read out the axis status, the current position, current and maximum speed,
acceleration, the air cushion delay, the scaling factor between counts and
engineering units ([Qxx00] = engineering units / counts) and the deadband
Ixx65.
acceleration and the air cushion delay.
*/
snprintf(command, sizeof(command),
"Q%2.2d10 Q%2.2d03 Q%2.2d04 Q%2.2d06 P%2.2d22 Q%2.2d00 I%2.2d65",
axisNo_, axisNo_, axisNo_, axisNo_, axisNo_, axisNo_, axisNo_);
status = pC_->writeRead(axisNo_, command, response, 7);
"P%2.2d00 Q%2.2d10 Q%2.2d03 Q%2.2d04 Q%2.2d06 P%2.2d22", axisNo_,
axisNo_, axisNo_, axisNo_, axisNo_, axisNo_);
status = pC_->writeRead(axisNo_, command, response, 6);
if (status != asynSuccess) {
asynPrint(
pC_->pasynUser(), ASYN_TRACE_ERROR,
@@ -190,9 +180,8 @@ asynStatus turboPmacAxis::init() {
pC_->portName, axisNo(), __PRETTY_FUNCTION__, __LINE__);
pTurboPmacA_->needInit = true;
}
nvals = sscanf(response, "%lf %lf %lf %lf %d %lf %lf", &motorPos,
&motorVmax, &motorVelocity, &motorAccel, &acoDelay,
&counts_to_eu, &deadband_counts);
nvals = sscanf(response, "%d %lf %lf %lf %lf %d", &axStatus, &motorPos,
&motorVmax, &motorVelocity, &motorAccel, &acoDelay);
// The acoDelay is given in milliseconds -> Convert to seconds, rounded up
setOffsetMovTimeout(std::ceil(acoDelay / 1000.0));
@@ -202,7 +191,7 @@ asynStatus turboPmacAxis::init() {
// here to mm/s^2.
motorAccel = motorAccel * 1000;
if (nvals != 8) {
if (nvals != 6) {
return pC_->couldNotParseResponse(command, response, axisNo_,
__PRETTY_FUNCTION__, __LINE__);
}
@@ -226,26 +215,6 @@ asynStatus turboPmacAxis::init() {
return status;
}
/*
Check if the new target position is within the range current position +/-
deadband. If that is the case, no movement command should be sent. This
functionality is implemented within the motor record itself, we just need to
populate the field SPDP (see record PushSPDB2Field in db/turboPmac.db)
First, the deadband is read out (Ixx65, see Turbo PMAC User Manual, p.
160). This value then needs to be converted into counts (by dividing it by
16). After words, the deadband in counts then need to be converted into
engineering units. For this, the scaling factor Qxx10 needs to be applied
(see SINQ_2G_MCU_SW_manual_rev7, p. 17):
[Qxx10] = engineering units (eu) / counts
deadband_eu = Qxx10 * Ixx65 / 16
The values of Qxx10 and Ixx65 are read out during initialization and are
assumed to not change during operation.
*/
setAxisParamChecked(this, motorPositionDeadband,
counts_to_eu * deadband_counts / 16.0);
// Update the parameter library immediately
status = callParamCallbacks();
if (status != asynSuccess) {
@@ -552,6 +521,11 @@ asynStatus turboPmacAxis::doPoll(bool *moving) {
errorStatus = handleError(error, userMessage, sizeof(userMessage));
// Update the parameter library
if (error != 0) {
setAxisParamChecked(this, motorStatusProblem, true);
}
if (*moving == false) {
setAxisParamChecked(this, motorMoveToHome, false);
}
@@ -579,6 +553,11 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
int sizeUserMessage) {
asynStatus status = asynError;
// Create the unique callsite identifier manually so it can be used later in
// the shouldBePrinted calls.
msgPrintControlKey keyError = msgPrintControlKey(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__);
switch (error) {
case 0:
status = asynSuccess;
@@ -587,9 +566,8 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
case 1:
// EPICS should already prevent this issue in the first place,
// since it contains the user limits
if (pC_->getMsgPrintControl().shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
pC_->pasynUser())) {
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
pC_->pasynUser())) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nTarget "
"position would exceed user limits.%s\n",
@@ -601,9 +579,8 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
break;
case 5:
// Command not possible
if (pC_->getMsgPrintControl().shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
pC_->pasynUser())) {
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
pC_->pasynUser())) {
asynPrint(
pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAxis is "
@@ -617,9 +594,8 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
"moving. Please call the support.");
break;
case 8:
if (pC_->getMsgPrintControl().shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
pC_->pasynUser())) {
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
pC_->pasynUser())) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAir cushion "
"feedback stopped during movement (P%2.2d01 = %d).%s\n",
@@ -633,9 +609,8 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
setAxisParamChecked(this, motorMessageText, userMessage);
break;
case 9:
if (pC_->getMsgPrintControl().shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
pC_->pasynUser())) {
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
pC_->pasynUser())) {
asynPrint(
pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nNo air cushion "
@@ -658,9 +633,8 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
which is not properly homed or if a bug occured.
*/
if (pC_->getMsgPrintControl().shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
pC_->pasynUser())) {
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
pC_->pasynUser())) {
asynPrint(
pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nAxis hit the "
@@ -679,9 +653,8 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
case 11:
// Following error
if (pC_->getMsgPrintControl().shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
pC_->pasynUser())) {
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
pC_->pasynUser())) {
asynPrint(
pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nMaximum allowed "
@@ -698,9 +671,8 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
break;
case 12:
if (pC_->getMsgPrintControl().shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
pC_->pasynUser())) {
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
pC_->pasynUser())) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nSecurity "
"input is triggered (P%2.2d01 = %d).%s\n",
@@ -718,9 +690,8 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
case 13:
// Driver hardware error triggered
if (pC_->getMsgPrintControl().shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
pC_->pasynUser())) {
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
pC_->pasynUser())) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nDriver "
"hardware error triggered.%s\n",
@@ -736,9 +707,8 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
case 14:
// EPICS should already prevent this issue in the first place,
// since it contains the user limits
if (pC_->getMsgPrintControl().shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
pC_->pasynUser())) {
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
pC_->pasynUser())) {
asynPrint(pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nMove "
"command exceeds hardware limits (P%2.2d01 = %d).%s\n",
@@ -753,9 +723,8 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
break;
default:
if (pC_->getMsgPrintControl().shouldBePrinted(
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, true,
pC_->pasynUser())) {
if (pC_->getMsgPrintControl().shouldBePrinted(keyError, true,
pC_->pasynUser())) {
asynPrint(
pC_->pasynUser(), ASYN_TRACE_ERROR,
"Controller \"%s\", axis %d => %s, line %d\nUnknown error "
@@ -766,6 +735,10 @@ asynStatus turboPmacAxis::handleError(int error, char *userMessage,
setAxisParamChecked(this, motorMessageText, userMessage);
break;
}
if (status == asynSuccess) {
pC_->getMsgPrintControl().resetCount(keyError, pC_->pasynUser());
}
return status;
}
@@ -778,7 +751,7 @@ asynStatus turboPmacAxis::doMove(double position, int relative,
char command[pC_->MAXBUF_] = {0};
char response[pC_->MAXBUF_] = {0};
double motorTargetPosition = 0.0;
double motorCoordinatesPosition = 0.0;
double motorRecResolution = 0.0;
double motorVelocity = 0.0;
int enabled = 0;
@@ -787,10 +760,6 @@ asynStatus turboPmacAxis::doMove(double position, int relative,
// =========================================================================
// Suppress unused variables warning
(void)minVelocity;
(void)acceleration;
getAxisParamChecked(this, motorEnableRBV, &enabled);
getAxisParamChecked(this, motorRecResolution, &motorRecResolution);
@@ -803,14 +772,13 @@ asynStatus turboPmacAxis::doMove(double position, int relative,
}
// Convert from EPICS to user / motor units
motorTargetPosition = position * motorRecResolution;
motorCoordinatesPosition = position * motorRecResolution;
motorVelocity = maxVelocity * motorRecResolution;
asynPrint(pC_->pasynUser(), ASYN_TRACE_FLOW,
"Controller \"%s\", axis %d => %s, line %d\nStart of axis to "
"position %lf.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
motorTargetPosition);
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__, position);
// Check if the speed is allowed to be changed
getAxisParamChecked(this, motorCanSetSpeed, &motorCanSetSpeed);
@@ -835,11 +803,11 @@ asynStatus turboPmacAxis::doMove(double position, int relative,
if (relative) {
snprintf(&command[writeOffset], sizeof(command) - writeOffset,
"P%2.2d23=0 Q%2.2d02=%lf M%2.2d=2", axisNo_, axisNo_,
motorTargetPosition, axisNo_);
motorCoordinatesPosition, axisNo_);
} else {
snprintf(&command[writeOffset], sizeof(command) - writeOffset,
"P%2.2d23=0 Q%2.2d01=%lf M%2.2d=1", axisNo_, axisNo_,
motorTargetPosition, axisNo_);
motorCoordinatesPosition, axisNo_);
}
// We don't expect an answer
@@ -851,7 +819,7 @@ asynStatus turboPmacAxis::doMove(double position, int relative,
"Controller \"%s\", axis %d => %s, line %d\nStarting movement to "
"target position %lf failed.\n",
pC_->portName, axisNo_, __PRETTY_FUNCTION__, __LINE__,
motorTargetPosition);
motorCoordinatesPosition);
setAxisParamChecked(this, motorStatusProblem, true);
return status;
}
@@ -879,9 +847,6 @@ asynStatus turboPmacAxis::stop(double acceleration) {
// =========================================================================
// Suppress unused variables warning
(void)acceleration;
snprintf(command, sizeof(command), "M%2.2d=8", axisNo_);
status = pC_->writeRead(axisNo_, command, response, 0);
@@ -946,7 +911,7 @@ asynStatus turboPmacAxis::doReset() {
/*
Home the axis. On absolute encoder systems, this is a no-op
*/
asynStatus turboPmacAxis::doHome(double minVelocity, double maxVelocity,
asynStatus turboPmacAxis::doHome(double min_velocity, double max_velocity,
double acceleration, int forwards) {
// Status of read-write-operations of ASCII commands to the controller
@@ -957,12 +922,6 @@ asynStatus turboPmacAxis::doHome(double minVelocity, double maxVelocity,
// =========================================================================
// Suppress unused variables warning
(void)minVelocity;
(void)maxVelocity;
(void)acceleration;
(void)forwards;
getAxisParamChecked(this, encoderType, &response);
// Only send the home command if the axis has an incremental encoder

View File

@@ -4,19 +4,15 @@
#include "turboPmacController.h"
#include <memory>
struct HIDDEN turboPmacAxisImpl;
struct turboPmacAxisImpl;
class HIDDEN turboPmacAxis : public sinqAxis {
class turboPmacAxis : public sinqAxis {
public:
/**
* @brief Construct a new turboPmacAxis
*
* @param pController Pointer to the associated controller
* @param axisNo Index of the axis
* @param initialize By setting this parameter to false, the
* initialization functions of the axes are not executed. This is e.g.
* necessary when this constructor is called from a children class
* constructor which performs its own initialization.
*/
turboPmacAxis(turboPmacController *pController, int axisNo,
bool initialize = true);
@@ -28,18 +24,6 @@ class HIDDEN turboPmacAxis : public sinqAxis {
*/
virtual ~turboPmacAxis();
/**
* @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
* from the MCU and store this information in the parameter library.
* - Set the enable PV according to the initial status of the axis.
*
* @return asynStatus
*/
virtual asynStatus init();
/**
* @brief Implementation of the `stop` function from asynMotorAxis
*
@@ -86,6 +70,18 @@ class HIDDEN turboPmacAxis : public sinqAxis {
double min_velocity, double max_velocity,
double acceleration);
/**
* @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
* from the MCU and store this information in the parameter library.
* - Set the enable PV according to the initial status of the axis.
*
* @return asynStatus
*/
virtual asynStatus init();
/**
* @brief Implementation of the `doReset` function from sinqAxis.
*

View File

@@ -41,13 +41,13 @@ struct turboPmacControllerImpl {
// User for writing int32 values to the port driver.
asynUser *pasynInt32SyncIOipPort;
// Indices of additional ParamLib entries
// Indices of additional PVs
int rereadEncoderPosition;
int readConfig;
int flushHardware;
int limFromHardware;
};
#define NUM_turboPmac_DRIVER_PARAMS 5
#define NUM_turboPmac_DRIVER_PARAMS 3
turboPmacController::turboPmacController(const char *portName,
const char *ipPortConfigName,
@@ -61,17 +61,17 @@ turboPmacController::turboPmacController(const char *portName,
- REREAD_ENCODER_POSITION
- READ_CONFIG
*/
numExtraParams + NUM_turboPmac_DRIVER_PARAMS),
pTurboPmacC_(
std::make_unique<turboPmacControllerImpl>((turboPmacControllerImpl){
.comTimeout = comTimeout,
.lastResponse = {0},
.pasynInt32SyncIOipPort = nullptr, // Populated in constructor
.rereadEncoderPosition = 0, // Populated in constructor
.readConfig = 0, // Populated in constructor
.flushHardware = 0, // Populated in constructor
.limFromHardware = 0, // Populated in constructor
})) {
numExtraParams + NUM_turboPmac_DRIVER_PARAMS)
{
// The paramLib indices are populated with the calls to createParam
pTurboPmacC_ =
std::make_unique<turboPmacControllerImpl>((turboPmacControllerImpl){
.comTimeout = comTimeout,
.lastResponse = {0},
});
// Initialization of local variables
asynStatus status = asynSuccess;
@@ -254,21 +254,9 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
pasynOctetSyncIOipPort(), command, commandLength, response, MAXBUF_,
pTurboPmacC_->comTimeout, &nbytesOut, &nbytesIn, &eomReason);
/*
If sth. is written to the controller, it needs some time to process the
command. However, the controller returns the acknowledgment via
pasynOctetSyncIO->writeRead immediately, signalling to the driver that it is
ready to receive the next command. In practice, this can result in commands
getting discarded on the driver side or in bringing the driver in undefined
states (e.g. stuck in status 1).
To prevent this, we wait for 20 ms after a write command to give the
controller enough time to process everything. A write command can be
identified by looking for the equal sign.
*/
if (strchr(command, '=')) {
usleep(20000);
}
// if (strchr(command, '=')) {
// usleep(20000);
// }
msgPrintControlKey comKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
@@ -573,8 +561,7 @@ static const iocshArg *const CreateControllerArgs[] = {
&CreateControllerArg0, &CreateControllerArg1, &CreateControllerArg2,
&CreateControllerArg3, &CreateControllerArg4, &CreateControllerArg5};
static const iocshFuncDef configTurboPmacCreateController = {
"turboPmacController", 6, CreateControllerArgs,
"Create a new instance of a TurboPMAC controller."};
"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);

View File

@@ -15,11 +15,11 @@
// 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 HIDDEN turboPmacAxis;
class turboPmacAxis;
struct HIDDEN turboPmacControllerImpl;
struct turboPmacControllerImpl;
class HIDDEN turboPmacController : public sinqController {
class turboPmacController : public sinqController {
public:
/**
* @brief Construct a new turboPmacController object. This function is meant
@@ -93,8 +93,7 @@ class HIDDEN turboPmacController : public sinqController {
* @return asynStatus
*/
asynStatus writeRead(int axisNo, const char *command, char *response,
int numExpectedResponses)
__attribute__((visibility("hidden")));
int numExpectedResponses);
/**
* @brief Specialized version of sinqController::couldNotParseResponse