Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 62ccf046fd | |||
| 9d61852713 | |||
| e64cedb243 | |||
| 0ff5632112 | |||
| f67941d67b | |||
| 7a96ed2b71 | |||
| cf43a1c57a | |||
| 85317e24cd | |||
| 1ee483f8e9 | |||
| 002b5d2616 | |||
| ce80426790 | |||
| 72f3965881 | |||
| d41e7bf054 | |||
| 30bfa1cac5 | |||
| 235816fd20 | |||
| bd0966d2c9 | |||
| 9d5d90574a | |||
| 6effc5e906 | |||
| bc5de11b16 | |||
| aa488627f9 | |||
| ccaee6c5d1 | |||
| 323b030581 | |||
| b206012df2 | |||
| 21ec7e8467 | |||
| 398bc8241a | |||
| 8ca684604d | |||
| 72fe2b3681 | |||
| 5911e62029 | |||
| 682024091f | |||
| 335de72bc5 | |||
| 8e5055d6b8 | |||
| 1cab6e14ff | |||
| 1e8a6495b8 | |||
| 6b91ab6d51 | |||
| f423002d23 | |||
| 79ec79fac1 | |||
| 1703542770 | |||
| c7d1dc4930 | |||
| 6fd3848f13 | |||
| 56f08f3c76 | |||
| 168bfae983 | |||
| 0e29750d13 | |||
| ba5b921aca | |||
| 1b810fb353 | |||
| 4bc3388bc6 | |||
| c759156058 | |||
| eca513f3a0 | |||
| 26175290bf | |||
| e316fcf67b | |||
| 6cf00adb60 | |||
| 9710d442b8 | |||
| 8dd1dc4af2 | |||
| a758db1211 | |||
| a11d10cb6c | |||
| ba353c4e5d | |||
| 55b523ddaa | |||
| 75292a6a9c | |||
| 53bbe2aae8 | |||
| 1597dc34e0 | |||
| dde7066f40 | |||
| b4d6447b32 | |||
| 2f83060ec1 | |||
| a3e3a79788 | |||
| 2c5fdc7d0a | |||
| 7bf31ac256 | |||
| 47e72d65a9 | |||
| 5298b5ef69 | |||
| 29f23216ad | |||
| 26bc3df876 | |||
| 87d3cbb3eb | |||
| 66552d5ffc | |||
| 253f65b25b |
24
.gitea/workflows/action.yaml
Normal file
24
.gitea/workflows/action.yaml
Normal 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
8
.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
O.*
|
||||||
|
.cvsignore
|
||||||
|
.vscode
|
||||||
|
src/.vscode
|
||||||
|
utils/analyzeTcpDump/__pycache__
|
||||||
|
utils/analyzeTcpDump/demo.pcap.json
|
||||||
|
utils/analyzeTcpDump/demovenv
|
||||||
|
utils/analyzeTcpDump/venv
|
||||||
@@ -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
|
|
||||||
6
.gitmodules
vendored
Normal file
6
.gitmodules
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
[submodule "sinqmotor"]
|
||||||
|
path = sinqmotor
|
||||||
|
url = https://gitea.psi.ch/lin-epics-modules/sinqmotor
|
||||||
|
[submodule "sinqMotor"]
|
||||||
|
path = sinqMotor
|
||||||
|
url = https://gitea.psi.ch/lin-epics-modules/sinqMotor
|
||||||
15
Makefile
15
Makefile
@@ -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
|
||||||
52
README.md
52
README.md
@@ -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
|
||||||
|
|
||||||
@@ -29,8 +38,10 @@ The full turboPmacX.cmd file looks like this:
|
|||||||
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
BIN
TurboPMAC_manual.pdf
Normal file
Binary file not shown.
@@ -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
1
sinqMotor
Submodule
Submodule sinqMotor added at e234d05815
@@ -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;
|
||||||
|
|||||||
@@ -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
@@ -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,7 +59,7 @@ 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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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,7 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
|||||||
|
|
||||||
// Definition of local variables.
|
// Definition of local variables.
|
||||||
asynStatus status = asynSuccess;
|
asynStatus status = asynSuccess;
|
||||||
asynStatus paramLibStatus = asynSuccess;
|
|
||||||
asynStatus timeoutStatus = asynSuccess;
|
asynStatus timeoutStatus = asynSuccess;
|
||||||
// char fullCommand[MAXBUF_] = {0};
|
|
||||||
char drvMessageText[MAXBUF_] = {0};
|
char drvMessageText[MAXBUF_] = {0};
|
||||||
char modResponse[MAXBUF_] = {0};
|
char modResponse[MAXBUF_] = {0};
|
||||||
int motorStatusProblem = 0;
|
int motorStatusProblem = 0;
|
||||||
@@ -215,20 +252,36 @@ 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,
|
timeoutStatus = checkComTimeoutWatchdog(axisNo, drvMessageText,
|
||||||
@@ -239,13 +292,14 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
|||||||
checkMaxSubsequentTimeouts(timeoutCounter, axis);
|
checkMaxSubsequentTimeouts(timeoutCounter, axis);
|
||||||
timeoutCounter += 1;
|
timeoutCounter += 1;
|
||||||
|
|
||||||
if (maxSubsequentTimeoutsExceeded_) {
|
if (maxSubsequentTimeoutsExceeded()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = pasynOctetSyncIO->writeRead(
|
status = pasynOctetSyncIO->writeRead(
|
||||||
pasynOctetSyncIOipPort(), command, commandLength, response,
|
pasynOctetSyncIOipPort(), command, commandLength, response,
|
||||||
MAXBUF_, comTimeout_, &nbytesOut, &nbytesIn, &eomReason);
|
MAXBUF_, pTurboPmacC_->comTimeout, &nbytesOut, &nbytesIn,
|
||||||
|
&eomReason);
|
||||||
if (status != asynTimeout) {
|
if (status != asynTimeout) {
|
||||||
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
|
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"Controller \"%s\", axis %d => %s, line "
|
"Controller \"%s\", axis %d => %s, line "
|
||||||
@@ -255,16 +309,28 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (status != asynSuccess) {
|
} else if (status != asynSuccess) {
|
||||||
if (msgPrintControl_.shouldBePrinted(comKey, true, pasynUser())) {
|
if (getMsgPrintControl().shouldBePrinted(comKey, true, pasynUser())) {
|
||||||
asynPrint(
|
asynPrint(
|
||||||
this->pasynUser(), ASYN_TRACE_ERROR,
|
this->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nError %s while "
|
"Controller \"%s\", axis %d => %s, line %d\nError %s while "
|
||||||
"writing to the controller.%s\n",
|
"writing to the controller.%s\n",
|
||||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||||
stringifyAsynStatus(status), msgPrintControl_.getSuffix());
|
stringifyAsynStatus(status), getMsgPrintControl().getSuffix());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
msgPrintControl_.resetCount(comKey, pasynUser());
|
getMsgPrintControl().resetCount(comKey, pasynUser());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != asynSuccess) {
|
||||||
|
/*
|
||||||
|
Since the communication failed, there is the possibility that the
|
||||||
|
controller is not connected at all to the network. In that case, we
|
||||||
|
cannot be sure that the information read out in the init method of the
|
||||||
|
axis is still up-to-date the next time we get a connection. Therefore,
|
||||||
|
an info flag is set which the axis object can use at the start of its
|
||||||
|
poll method to try to initialize itself.
|
||||||
|
*/
|
||||||
|
axis->setNeedInit(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timeoutStatus == asynError) {
|
if (timeoutStatus == asynError) {
|
||||||
@@ -301,16 +367,17 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
|||||||
"string detected\"). Please call the support.",
|
"string detected\"). Please call the support.",
|
||||||
reasonStringified);
|
reasonStringified);
|
||||||
|
|
||||||
if (msgPrintControl_.shouldBePrinted(terminateKey, true, pasynUser())) {
|
if (getMsgPrintControl().shouldBePrinted(terminateKey, true,
|
||||||
|
pasynUser())) {
|
||||||
|
|
||||||
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
|
asynPrint(this->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
"Controller \"%s\", axis %d => %s, line %d\nMessage "
|
"Controller \"%s\", axis %d => %s, line %d\nMessage "
|
||||||
"terminated due to reason %s.%s\n",
|
"terminated due to reason %s.%s\n",
|
||||||
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
portName, axisNo, __PRETTY_FUNCTION__, __LINE__,
|
||||||
reasonStringified, msgPrintControl_.getSuffix());
|
reasonStringified, getMsgPrintControl().getSuffix());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
msgPrintControl_.resetCount(terminateKey, pasynUser());
|
getMsgPrintControl().resetCount(terminateKey, pasynUser());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -327,7 +394,7 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
|||||||
if (numExpectedResponses != numReceivedResponses) {
|
if (numExpectedResponses != numReceivedResponses) {
|
||||||
adjustResponseForPrint(modResponse, response, MAXBUF_);
|
adjustResponseForPrint(modResponse, response, MAXBUF_);
|
||||||
|
|
||||||
if (msgPrintControl_.shouldBePrinted(numResponsesKey, true,
|
if (getMsgPrintControl().shouldBePrinted(numResponsesKey, true,
|
||||||
pasynUser())) {
|
pasynUser())) {
|
||||||
asynPrint(
|
asynPrint(
|
||||||
this->pasynUser(), ASYN_TRACE_ERROR,
|
this->pasynUser(), ASYN_TRACE_ERROR,
|
||||||
@@ -335,7 +402,7 @@ asynStatus turboPmacController::writeRead(int axisNo, const char *command,
|
|||||||
"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 +412,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 +442,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 +465,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 +484,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 +506,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 +573,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);
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
Reference in New Issue
Block a user