Compare commits

..

71 Commits
1.1.1 ... 1.6.4

Author SHA1 Message Date
08ce0999a3 Updated underlying version of motorBase to receive the RVEL bugfix
Some checks failed
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 6s
2026-02-12 12:05:34 +01:00
1d662ecd43 Use safe limit setter function from sinqMotor
Some checks failed
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 6s
2026-02-11 11:08:46 +01:00
bea68c807b Updated to new sinqMotor 1.6.0 version 2026-02-11 11:07:28 +01:00
01a04d3b24 Fixed of-by-one error
Some checks failed
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 8s
2026-02-03 14:05:12 +01:00
76cc48a49c Reverted back to sinqMotor 1.5.7
Some checks failed
Test And Build / Lint (push) Failing after 4s
Test And Build / Build (push) Failing after 7s
2026-02-03 13:44:21 +01:00
a9f623ba1d Merge pull request 'Document how to do reset when having HW error' (#1) from fix_hw_error_doc into main
Some checks failed
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 7s
Reviewed-on: #1
2026-02-03 10:07:56 +01:00
791e74f82f Expanded a bit more on the FAQ
Some checks failed
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 7s
2026-02-03 10:02:54 +01:00
2dc10fe869 Document how to do reset when having HW error
Some checks failed
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 7s
2026-02-03 09:49:36 +01:00
e10aaf978e Removed unneccessary paramLib callback which caused flickering
Some checks failed
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 7s
2026-01-23 13:21:43 +01:00
4159ef50bc Updated to correct version of sinqMotor 2026-01-23 11:57:40 +01:00
d168453329 Removed reference in gitmodules
Some checks failed
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 3s
2026-01-23 11:55:34 +01:00
1ea1bd8db0 Remove sinqMotor submodule completely
Some checks failed
Test And Build / Lint (push) Failing after 3s
Test And Build / Build (push) Failing after 3s
2026-01-23 11:53:15 +01:00
a6979d303b moved disconnect handling to sinqMotor 2026-01-22 09:47:36 +01:00
78810c3364 Fixed error messages 2026-01-09 13:46:14 +01:00
21c9adec35 Update src/turboPmacAxis.cpp 2026-01-09 13:20:29 +01:00
01a0239dfb Added compiler flags for the C++ code and formatted README 2025-12-23 13:27:14 +01:00
0e2b5f5e93 Fixed number of commands bug in turboPmacAxis::init 2025-12-23 11:59:09 +01:00
50aa44014b Added user manual and .gitignore to repo 2025-11-03 08:37:49 +01:00
6d00209e8a Fixed some small bugs 2025-10-13 16:57:30 +02:00
a70bcc0d75 Fixed bug: Wrong scale used before
The correct scale from counts to engeering units is Qxx00.
2025-09-10 11:52:51 +02:00
bf3406be1c Updated to sinqMotor 1.5 to get deadband feature 2025-09-09 16:52:19 +02:00
91e2519832 Deadband check in driver code 2025-09-09 13:43:11 +02:00
829665c078 Removed C++ only flag from USR_CFLAGS
USR_CFLAGS only applies to .c files, which are compiled with a C
compiler, therefore the C++ flag is not applied, resulting in a warning
during compilation.
2025-08-22 15:27:28 +02:00
4fd95743c4 Fixed bug with errorprint to ioc console
The previous setup created one print key for all error types. The
problem with that is that if an error directly followed another error
(without an error = 0 in between), the MsgPrintControl prevented
printing of the new error. Now every error has its own key.
2025-08-22 15:25:31 +02:00
5845d2e908 Fixed imprecise comment 2025-08-12 15:42:43 +02:00
501b240250 Updated to sinqMotor 1.4.0 2025-08-12 09:14:38 +02:00
5f35c2277d Hide visibility of classes to avoid symbol clashes 2025-08-12 09:00:42 +02:00
4f972fd53d Hide symbols via compilerflag 2025-08-11 16:46:22 +02:00
2fc2ad1970 Added flag to see whether we are in the middle of enabling / disabling 2025-08-11 15:47:27 +02:00
3f7998eb9a Added sleep after write commands
See comment in writeRead for explanation.
2025-08-11 15:47:27 +02:00
ed36207a4c remove unneeded line 2025-07-04 14:16:48 +02:00
54a11344f7 remove old pipeline file 2025-07-04 14:12:54 +02:00
8bbae3691d no newline 2025-07-04 14:07:20 +02:00
06b116c5fd adds IGNORE_SUBMODULES as other makefile tries to pull everything anew 2025-07-04 13:54:24 +02:00
3c8f5045f2 time to build 2025-07-04 12:57:23 +02:00
1b82da8362 time to build 2025-07-04 12:55:24 +02:00
a908d56bad time to build 2025-07-04 12:53:13 +02:00
404a4cd713 typo 2025-07-04 12:51:04 +02:00
dc89906af0 it should already be there it is now a submodule... 2025-07-04 12:50:28 +02:00
b63f2c8cec :) 2025-07-04 12:45:16 +02:00
dd7c67e53e is the url wrong ? 2025-07-04 12:43:33 +02:00
50b61ef634 maybe a key is necessary 2025-07-04 12:38:57 +02:00
d70689626a Can I check out another repository? 2025-07-04 12:31:40 +02:00
f1f673df6e Can I check out another repository? 2025-07-04 12:30:53 +02:00
c1761a161c Disable the axis on reset, if it is not moving
Resetting the axis now also disables it, if it is not moving.
2025-07-01 13:11:06 +02:00
1495e9a0e3 Links in README.md updated to point to Gitea now. 2025-06-26 13:56:57 +02:00
62cec4d8e1 Updated sinqMotor 2025-06-17 08:34:07 +02:00
c1cb407ea3 Updated sinqMotor version 2025-06-16 16:18:06 +02:00
b27babfa10 Committed new sinqMotor version 2025-06-16 15:55:40 +02:00
7906b788f8 Added destructor for controller 2025-06-10 14:58:59 +02:00
08bd303ab3 Added destructor for controller 2025-06-10 14:53:26 +02:00
f93e16eb36 Added flag to disable reading the limits from the hardware 2025-06-10 13:53:39 +02:00
fb5ba8f14e Changed priority of IOC shell message 2025-06-06 11:20:50 +02:00
18bc3dfc5f 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
738780f897 Change to latest sinqMotor version 2025-05-23 10:11:27 +02:00
2223a9b23e Fixed remote git link in repo. 2025-05-16 15:58:17 +02:00
4cf500cc96 Made some functions virtual so they can be overwritten in subclasses 2025-05-15 14:54:18 +02:00
e7dc673395 Fixed some merge bugs 2025-05-15 12:32:30 +02:00
572ee5c671 Merge branch 'static-dep' 2025-05-15 12:27:26 +02:00
2e5059da33 Added default value for motorMessageText 2025-05-15 12:25:08 +02:00
c945896da1 Addes error message for failing to enable / disable within timeout 2025-05-14 16:26:55 +02:00
1054b87467 Adjusted usage of motorMessageText to act as an error message only. 2025-05-14 16:17:14 +02:00
a7c82d1238 Use new version of sinqMotor 2025-05-14 16:13:10 +02:00
f6d7f3846d Allow enabling / disabling the motor regardless of the status returned by the poll 2025-05-13 14:44:24 +02:00
b53cf770ae Fixed small inaccuracy in the README 2025-05-12 17:04:09 +02:00
50192858e9 Fixed typo 2025-05-12 16:59:59 +02:00
d4bb77bae1 Added some comments that this library now uses sinqMotor as static
dependency.
2025-05-12 16:43:57 +02:00
3bafc5110c 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
31875ddd99 Specified new version for sinqMotor 2025-05-12 12:00:49 +02:00
980ea59958 Pinned sinqMotor version 2025-05-12 11:58:56 +02:00
4ee211b481 Added sinqMotor as a submodule for static linking 2025-05-12 08:52:10 +02:00
14 changed files with 592 additions and 755 deletions

View File

@@ -0,0 +1,24 @@
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 Normal file
View File

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

View File

@@ -1,58 +0,0 @@
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

3
.gitmodules vendored
View File

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

View File

@@ -10,7 +10,7 @@ ARCH_FILTER=RHEL%
REQUIRED+=motorBase REQUIRED+=motorBase
# Specify the version of motorBase we want to build against # Specify the version of motorBase we want to build against
motorBase_VERSION=7.2.2 motorBase_VERSION=7.3.2
# These headers allow to depend on this library for derived drivers. # These headers allow to depend on this library for derived drivers.
HEADERS += src/pmacAsynIPPort.h HEADERS += src/pmacAsynIPPort.h
@@ -34,4 +34,5 @@ TEMPLATES += db/turboPmac.db
DBDS += sinqMotor/src/sinqMotor.dbd DBDS += sinqMotor/src/sinqMotor.dbd
DBDS += src/turboPmac.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 -Wunused-result -Werror -fvisibility=hidden # -Wpedantic // Does not work because EPICS macros trigger warnings
USR_CXXFLAGS += -Wall -Wextra -Wunused-result -Werror -fvisibility=hidden

View File

@@ -1,18 +1,29 @@
# turboPmac # turboPmac
## <span style="color:red">Please read the documentation of sinqMotor first: https://git.psi.ch/sinq-epics-modules/sinqmotor</span> ## <span style="color:red">Please read the documentation of sinqMotor first: https://gitea.psi.ch/lin-epics-modules/sinqMotor</span>
## 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://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 ## 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://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/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. The folder "utils" contains utility scripts for working with pmac motor
- writeRead.py: Allows sending commands to and receiving commands from a pmac controller over an ethernet connection. controllers. To read their manual, run the scripts without any arguments.
- 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. - 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 ### IOC startup script
@@ -22,13 +33,15 @@ turboPmac exports the following IOC shell functions:
The full turboPmacX.cmd file looks like this: The full turboPmacX.cmd file looks like this:
``` ```bash
# Define the name of the controller and the corresponding port # Define the name of the controller and the corresponding port
epicsEnvSet("DRIVER_PORT","turboPmacX") epicsEnvSet("DRIVER_PORT","turboPmacX")
epicsEnvSet("IP_PORT","p$(DRIVER_PORT)") 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. # Create the TCP/IP socket used to talk with the controller. The socket can be
# We do not use the standard asyn port driver here, but a PMAC-specific one which enables the usage of StreamDevices for # 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. # communicating with the controller directly.
pmacAsynIPPortConfigure("$(IP_PORT)","172.28.101.24:1025") pmacAsynIPPortConfigure("$(IP_PORT)","172.28.101.24:1025")
@@ -48,7 +61,8 @@ turboPmacAxis("$(DRIVER_PORT)",5);
# Set the number of subsequent timeouts # Set the number of subsequent timeouts
setMaxSubsequentTimeouts("$(DRIVER_PORT)", 20); 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); 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.
@@ -61,14 +75,58 @@ dbLoadRecords("$(turboPmac_DB)/asynRecord.db","P=$(INSTR)$(DRIVER_PORT),PORT=$(I
### Additional records ### 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 ## Developer guide
### Versioning ### Versioning
Please see the documentation for the module sinqMotor: https://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/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 ### 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://git.psi.ch/sinq-epics-modules/sinqmotor/-/blob/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.
## FAQ
### Axis hardware error at startup
If at IOC startup you are met with the following errors:
```
2026/02/02 16:56:49.470 Controller "turboPmac1", axis 3 => asynStatus turboPmacAxis::handleError(int, char*, int), line 735
Driver hardware error triggered.
2026/02/02 16:56:49.485 Controller "turboPmac1", axis 7 => asynStatus turboPmacAxis::handleError(int, char*, int), line 735
Driver hardware error triggered.
2026/02/02 16:56:49.492 Controller "turboPmac1", axis 8 => asynStatus turboPmacAxis::handleError(int, char*, int), line 735
Driver hardware error triggered.
```
You may need to reset to do a axis reset. This can be done with
`utils/writeRead.py`. The following example does so for axis 3.
Confirm the hardware error:
```
utils/writeRead.py 172.28.87.24:1025 P0301 # 03 specifies axis 3, 01 is the command to read out the error.
# If it returns 13 it's a hardware error.
# For the full list of errors see TurboPMAC_manual.pdf or src/turboPmacAxis.cpp, function "handleError".
```
Reset the axis:
```
utils/writeRead.py 172.28.87.24:1025 P0301=0
```
Check if the error has appeared again:
```
utils/writeRead.py 172.28.87.24:1025 P0301
```
If the console output is not 0, the error has been deleted, but appeared
immediately again. In this case, the error cannot be reset remotely.
If it is an error such as 10 (limit switches hit), the motor needs to be
moved away from the limits, this resets the error automatically.
Other errors like 13 represent an actual issue on the hardware which
needs to be resolved by the electronics motion engineers.

BIN
TurboPMAC_manual.pdf Normal file

Binary file not shown.

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,22 +1,22 @@
#ifndef turboPmacAXIS_H #ifndef turboPmacAXIS_H
#define turboPmacAXIS_H #define turboPmacAXIS_H
#include "sinqAxis.h" #include "sinqController.h"
#include "turboPmacController.h"
#include <memory> #include <memory>
struct turboPmacAxisImpl; struct HIDDEN turboPmacAxisImpl;
// Forward declaration of the controller class to resolve the cyclic dependency class HIDDEN turboPmacAxis : public sinqAxis {
// between the controller and the axis .h-file. See
// https://en.cppreference.com/w/cpp/language/class.
class turboPmacController;
class turboPmacAxis : public sinqAxis {
public: public:
/** /**
* @brief Construct a new turboPmacAxis * @brief Construct a new turboPmacAxis
* *
* @param pController Pointer to the associated controller * @param pController Pointer to the associated controller
* @param axisNo Index of the axis * @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, turboPmacAxis(turboPmacController *pController, int axisNo,
bool initialize = true); bool initialize = true);
@@ -28,6 +28,18 @@ class turboPmacAxis : public sinqAxis {
*/ */
virtual ~turboPmacAxis(); 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 * @brief Implementation of the `stop` function from asynMotorAxis
* *
@@ -74,18 +86,6 @@ class turboPmacAxis : public sinqAxis {
double min_velocity, double max_velocity, double min_velocity, double max_velocity,
double acceleration); 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. * @brief Implementation of the `doReset` function from sinqAxis.
* *
@@ -142,6 +142,11 @@ class turboPmacAxis : public sinqAxis {
*/ */
void setNeedInit(bool needInit); void setNeedInit(bool needInit);
/**
* @brief Return a pointer to the axis controller
*/
virtual turboPmacController *pController() override { return pC_; };
private: private:
turboPmacController *pC_; turboPmacController *pC_;
std::unique_ptr<turboPmacAxisImpl> pTurboPmacA_; std::unique_ptr<turboPmacAxisImpl> pTurboPmacA_;

View File

@@ -41,13 +41,13 @@ struct turboPmacControllerImpl {
// User for writing int32 values to the port driver. // User for writing int32 values to the port driver.
asynUser *pasynInt32SyncIOipPort; asynUser *pasynInt32SyncIOipPort;
// Indices of additional PVs // Indices of additional ParamLib entries
int rereadEncoderPosition; int rereadEncoderPosition;
int readConfig; int readConfig;
int flushHardware; int flushHardware;
int limFromHardware; int limFromHardware;
}; };
#define NUM_turboPmac_DRIVER_PARAMS 3 #define NUM_turboPmac_DRIVER_PARAMS 5
turboPmacController::turboPmacController(const char *portName, turboPmacController::turboPmacController(const char *portName,
const char *ipPortConfigName, const char *ipPortConfigName,
@@ -61,17 +61,17 @@ turboPmacController::turboPmacController(const char *portName,
- REREAD_ENCODER_POSITION - REREAD_ENCODER_POSITION
- READ_CONFIG - READ_CONFIG
*/ */
numExtraParams + NUM_turboPmac_DRIVER_PARAMS) numExtraParams + NUM_turboPmac_DRIVER_PARAMS),
pTurboPmacC_(
{
// The paramLib indices are populated with the calls to createParam
pTurboPmacC_ =
std::make_unique<turboPmacControllerImpl>((turboPmacControllerImpl){ std::make_unique<turboPmacControllerImpl>((turboPmacControllerImpl){
.comTimeout = comTimeout, .comTimeout = comTimeout,
.lastResponse = {0}, .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
})) {
// Initialization of local variables // Initialization of local variables
asynStatus status = asynSuccess; asynStatus status = asynSuccess;
@@ -206,9 +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;
// char fullCommand[MAXBUF_] = {0};
char drvMessageText[MAXBUF_] = {0}; char drvMessageText[MAXBUF_] = {0};
char modResponse[MAXBUF_] = {0}; char modResponse[MAXBUF_] = {0};
int motorStatusProblem = 0; int motorStatusProblem = 0;
@@ -256,6 +253,22 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
pasynOctetSyncIOipPort(), command, commandLength, response, MAXBUF_, pasynOctetSyncIOipPort(), command, commandLength, response, MAXBUF_,
pTurboPmacC_->comTimeout, &nbytesOut, &nbytesIn, &eomReason); 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);
}
msgPrintControlKey comKey = msgPrintControlKey comKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__); msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
@@ -270,8 +283,7 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
getMsgPrintControl().getSuffix()); getMsgPrintControl().getSuffix());
} }
timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText, checkComTimeoutWatchdog(axisNo, drvMessageText, sizeof(drvMessageText));
sizeof(drvMessageText));
int timeoutCounter = 0; int timeoutCounter = 0;
while (1) { while (1) {
@@ -319,14 +331,10 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
axis->setNeedInit(true); axis->setNeedInit(true);
} }
if (timeoutStatus == asynError) {
status = asynError;
}
// The message should only ever terminate due to reason 2 // The message should only ever terminate due to reason 2
msgPrintControlKey terminateKey = msgPrintControlKey terminateKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__); msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (eomReason != 2) { if (status == asynSuccess && eomReason != 2) {
status = asynError; status = asynError;
char reasonStringified[30] = {0}; char reasonStringified[30] = {0};
@@ -377,7 +385,7 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
} }
msgPrintControlKey numResponsesKey = msgPrintControlKey numResponsesKey =
msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__); msgPrintControlKey(portName, axisNo, __PRETTY_FUNCTION__, __LINE__);
if (numExpectedResponses != numReceivedResponses) { if (status == asynSuccess && numExpectedResponses != numReceivedResponses) {
adjustResponseForPrint(modResponse, response, MAXBUF_); adjustResponseForPrint(modResponse, response, MAXBUF_);
if (getMsgPrintControl().shouldBePrinted(numResponsesKey, true, if (getMsgPrintControl().shouldBePrinted(numResponsesKey, true,
@@ -428,41 +436,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;
@@ -582,7 +567,8 @@ static const iocshArg *const CreateControllerArgs[] = {
&CreateControllerArg0, &CreateControllerArg1, &CreateControllerArg2, &CreateControllerArg0, &CreateControllerArg1, &CreateControllerArg2,
&CreateControllerArg3, &CreateControllerArg4, &CreateControllerArg5}; &CreateControllerArg3, &CreateControllerArg4, &CreateControllerArg5};
static const iocshFuncDef configTurboPmacCreateController = { static const iocshFuncDef configTurboPmacCreateController = {
"turboPmacController", 6, CreateControllerArgs}; "turboPmacController", 6, CreateControllerArgs,
"Create a new instance of a TurboPMAC controller."};
static void configTurboPmacCreateControllerCallFunc(const iocshArgBuf *args) { static void configTurboPmacCreateControllerCallFunc(const iocshArgBuf *args) {
turboPmacCreateController(args[0].sval, args[1].sval, args[2].ival, turboPmacCreateController(args[0].sval, args[1].sval, args[2].ival,
args[3].dval, args[4].dval, args[5].dval); args[3].dval, args[4].dval, args[5].dval);

View File

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