Compare commits

...

63 Commits

Author SHA1 Message Date
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
15 changed files with 829 additions and 872 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 Normal file
View File

@@ -0,0 +1,3 @@
[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 include /ioc/tools/driver.makefile
MODULE=turboPmac MODULE=turboPmac
@@ -8,28 +8,31 @@ ARCH_FILTER=RHEL%
# Additional module dependencies # Additional module dependencies
REQUIRED+=motorBase REQUIRED+=motorBase
REQUIRED+=sinqMotor
# 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.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. # These headers allow to depend on this library for derived drivers.
HEADERS += src/pmacAsynIPPort.h HEADERS += src/pmacAsynIPPort.h
HEADERS += src/turboPmacAxis.h HEADERS += src/turboPmacAxis.h
HEADERS += src/turboPmacController.h HEADERS += src/turboPmacController.h
# Source files to build # 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/pmacAsynIPPort.c
SOURCES += src/turboPmacAxis.cpp SOURCES += src/turboPmacAxis.cpp
SOURCES += src/turboPmacController.cpp SOURCES += src/turboPmacController.cpp
# Store the record files # Store the record files
TEMPLATES += sinqMotor/db/asynRecord.db
TEMPLATES += sinqMotor/db/sinqMotor.db
TEMPLATES += db/turboPmac.db TEMPLATES += db/turboPmac.db
# This file registers the motor-specific functions in the IOC shell. # This file registers the motor-specific functions in the IOC shell.
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,20 +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://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
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
The folder "utils" contains utility scripts for working with pmac motor controllers. To read their manual, run the scripts without any arguments. `drvAsynIPPortConfigure`. For the general configuration, please see
- writeRead.py: Allows sending commands to and receiving commands from a pmac controller over an ethernet connection. https://gitea.psi.ch/lin-epics-modules/sinqMotor/src/branch/main/README.md.
- 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 ### IOC startup script
@@ -24,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")
@@ -50,27 +61,36 @@ 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.
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
`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
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://gitea.psi.ch/lin-epics-modules/sinqMotor/src/branch/main/README.md for
more details.

BIN
TurboPMAC_manual.pdf Normal file

Binary file not shown.

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)")
}

1
sinqMotor Submodule

Submodule sinqMotor added at 4e30331c92

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,19 +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>
// Forward declaration of the controller class to resolve the cyclic dependency struct HIDDEN 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 HIDDEN 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);
@@ -21,9 +24,22 @@ 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();
/**
* @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
* *
@@ -31,7 +47,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 +59,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 +69,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,20 +82,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
*
* 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
*/
asynStatus init();
/** /**
* @brief Implementation of the `doReset` function from sinqAxis. * @brief Implementation of the `doReset` function from sinqAxis.
@@ -87,7 +92,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 +100,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 +108,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 +127,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 ParamLib entries
int rereadEncoderPosition;
int readConfig;
int flushHardware;
int limFromHardware;
};
#define NUM_turboPmac_DRIVER_PARAMS 5
turboPmacController::turboPmacController(const char *portName, turboPmacController::turboPmacController(const char *portName,
const char *ipPortConfigName, const char *ipPortConfigName,
int numAxes, double movingPollPeriod, int numAxes, double movingPollPeriod,
@@ -43,25 +61,29 @@ 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_(
{ 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
})) {
// 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,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;
@@ -215,37 +251,53 @@ 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);
/*
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__);
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, checkComTimeoutWatchdog(axisNo, drvMessageText, sizeof(drvMessageText));
sizeof(drvMessageText));
int timeoutCounter = 0; int timeoutCounter = 0;
while (1) { while (1) {
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,26 +307,34 @@ 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 (timeoutStatus == asynError) { if (status != asynSuccess) {
status = asynError; /*
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);
} }
// 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};
@@ -301,16 +361,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());
} }
/* /*
@@ -324,18 +385,18 @@ 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 (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 +406,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 +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;
@@ -421,13 +459,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 +478,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 +500,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 */
@@ -516,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,9 +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>
class turboPmacController : public sinqController { // 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;
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
@@ -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
* *
@@ -79,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
@@ -116,36 +131,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 */